상속 02

오늘·2021년 3월 10일
0

Java

목록 보기
17/42

메소드 오버라이딩

: 상속된 메소드의 내용이 자식 클래스에 맞지 않을 경우, 자식 클래스에서 동일한 메소드를 재정의 하는 것을 말한다.
(상속의 개념에서 부모 클래스의 메소들르 자식의 클래스로 재정의하는 것)

: 메소드가 오버라이딩되었다면 부모 객체의 메소드는 숨겨지기 때문에, 자식 객체에서 메소드를 호출하면 오버라이딩 된 자식 메소드가 호출된다.

: 어노테이션은 (@Override 표시는) 생략 가능하지만, 실수를 줄여줄 수 있으니 사용하는 것이 좋다.


부모와 똑같은 이름의 출력 메소드 작성해보기

(BB 클래스와 출력 클래스에서 확인 가능)
결과
: 에러 안남. 부모와 자식의 메소드 이름은 같지만
내용이 다르면 설정 가능 (메소드 오버로딩 같은 느낌)
: BB 클래스를 객체화해 BB.Aprint()를 출력했을 때
부모의 Aprint() 메소드가 아니라 본인의 Aprint() 메소드가 출력되는 것을 확인

1) A 클래스
// 점 x, y 에 대한 클래스
public class A {
	private int x;
	private int y;
	
	// 출력 메소드
	void Aprint() {
		System.out.println("(" + x + "," + y+")");
	}

	
	// private로 막아줬으니 값 받고 내보낼 Setter Getter 설정
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}

	
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
	
}






2) AA 클래스
// A를 상속받으며
// 점 x, y, z 에 대한 클래스
public class AA extends A {
	private int z;

	// 풀력 메소드
	// (x,y,z) 찍기
	void aaPrint() {
		// AA가 A의 자식 클래스라도 private로 막아놨기 때문에 바로 가져올 수 없다
		// System.out.println("(" + x + "," + y+ "," + z + ")");

		// getter setter 메소드를 가져와 사용해야함
		System.out.println(getX());
		System.out.println(getY());
		System.out.println(z);
	}

	// z도 private로 막아줬기 때문에 Getter Setter 설정
	public int getZ() {
		return z;
	}
	public void setZ(int z) {
		this.z = z;
	}
}






3) BB 클래스
//
public class BB extends A {
	private String color;
	
	// 출력하기
	void bbPrint() {
		System.out.println(getX());
		System.out.println(getY());
		System.out.println(color);
	}
	
	// 부모와 똑같은 이름의 출력 메소드 작성해보기
	// 결과
	// : 에러 안남. 부모와 자식의 메소드 이름은 같지만
	//   내용이 다르면 설정 가능 (메소드 오버로딩처럼 상속 관계에서도 성립)
	// : BB 클래스를 객체화해 BB.Aprint()를 출력했을 때
	//   부모의 Aprint() 메소드가 아니라 본인의 Aprint() 메소드가 출력되는 것을 확인
    
//	@Override
//	void Aprint() {
//		System.out.println(getX());
//		System.out.println(getY());
//		System.out.println(color);
//	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}
}






4) 객체 생성 후 출력해보는 main
public class business {
	public static void main(String[] args) {
		
		// A 객체 생성해서 10, 20 출력하기
		A a = new A();
		// 값 넣기
		a.setX(10);
		a.setY(20);
		//출력하기
		a.Aprint();
		System.out.println("-------------------------------");
		
		// AA 객체 생성해 30, 40, 50 출력하기
		AA aa = new AA();
		// 값 넣기
		aa.setX(30);
		aa.setY(40);
		aa.setZ(50);
		//출력하기
		aa.aaPrint();
		System.out.println("\n AA에서 A클래스의 Aprint() 메소드를 출력했습니다");
		aa.Aprint();
		System.out.println("-------------------------------");
		
		// BB 객체 생성해 60, 70, red 출력하기
		BB bb = new BB();
		// 값 넣기
		bb.setX(60);
		bb.setY(70);
		bb.setColor("red");
		// 출력하기
		bb.bbPrint();
		System.out.println("\n BB에서 A클래스의 Aprint() 메소드를 출력했습니다");
		bb.Aprint();
		System.out.println("-------------------------------");
		
	}
}

실행 결과


@Override 의 기본적인사용모습

기본적으로 있는 Object 클래스를 상속받아 오버라이딩을 사용해보자

1) Object에 이미 정의 된 클래스를 가져와 실행내용 부분만 재정의
//
public class KK extends Object{
	
	// 부모인 Object 클래스의 것을 가져와 내용만 변경한 오버라이딩 부분
	// 주석 처리하면 Object 클래스의 실행부분이 나온다
	@Override
    public String toString() {
		// 원래 모습
		// return getClass().getName() + "@" +Integer.toHexString(hashCode());
		// 재정의
        return "지금 오바라이딩 배우는 중 \n 이곳은 자식 클래스 부분" ;
    }
}


class Child extends Object{
	// Child 클래스는 Object 클래스의 자식 클래스
	// 부모 클래스 Object 클래스의 메소드를 재정의해 사용할 수 있다.
	// final (static)으로 작성된 메소드가 아니라면 모두 가능
	
	@Override
	public boolean equals(Object obj) {
		// 원래 모습
		// return (this == obj);
		// 재정의
		return true;	
	}
}







2) 객체 생성 후 출력 모양 보는 main
//
public class RunClass {
	public static void main(String[] args) {
		// 객체 생성 후 메소드 호출
		KK kk = new KK();

		String r = kk.toString();
		System.out.println(r);
		
		// 객체 생성 후 메소드 호출
		Child ch = new Child();
		ch.equals(null);
	}
}

실행결과


부모 메소드(super)

자식 클래스에서 부모 클래스의 메소드를 오버라이딩하게 되면, 부모의 메소드는 숨겨지고, 오버라이딩 된 자식의 메소드만 사용된다. 그러나 자식 클래스 내부에서 오버라이딩된 부모 클래스의 메소드를 호출해야 하는 상황이 발생한다면 super 키워드를 사용해준다.

  1. 부모 클래스의 멤버를 가리킬때
    super.name
  2. 자식 생성자에서 부모 생성자를 호출해야할때
    super(매개값)

(1) 사용해보자

1) 자식 클래스
//
class Two extends One{
	static int sa;
	int ip;

	void twoMethod(String name, int sa) {
		// 부모 필드에 있는 name에 넣을것이다
		super.name = name;

		// two 는 one을 상속 받기 때문에
		// 자기 자신의 필드를 먼저 확인하고, 했는데 name이 없으면
		// 부모필드의 name을 슬그머니 가져와서 사용함
		// 고로 에러는 안나는데
		// this와 super는 다르니 그 경로를 잘 기억해둔다
		this.name = name;

		// static 붙어있는 변수는
		// 클래스명.변수명
		// 의 형태로 사용한다
		Two.sa = sa;
		// 오류는 안나지만 권장하지 않는 방밥
		// this.sa = sa;
		
		// 메개변수 sa의 값을 부모의 sp에 넣어라
		super.sp = sa;
		
		// 부모의 인스턴스 필드에 값 넣기
		super.ip = sa;
	}
	
	Two(){
//		this(10);
		
		// 부모의 명시적 생성자 부분을 호출
		super(100);
		
		System.out.println("자식의 기본 생성자");
	}
	
	Two(int ip) {
		// 자식 클래스의 기본 생성자 안에는 첫 줄에 super()가 생략된 것이다
		// 자식 클래스의 기본 생성자 안에 첫줄에 super(매개변수)를 호출하면
		// 부모의 명시적 생성자를 호출 할 것이므로
		// 부모 클래스의 기본 생성자는 호출되지 않음
		// (지우고 출력하는 것과 그냥 출력하는 것과 차이가 없음.
        	// 왜냐면 지워도 기본으로 super()가 있는데 안보이는 거니까)
		super();
		System.out.println("자식의 명시적 생성자");
		
		// 지금 있는 (Two) 필드에 있는 ip에 넣을 것이다
		this.ip = ip;
		// super  : 부모 필드에 있는 ip 에 넣은것이다.
		super.ip = ip;
	}
}




2) 부모 클래스
//
public class Inheritance5 {
	public static void main(String[] args) {
//		One one = new One();
		Two two = new Two(300);
//		two.twoMethod("김자바", 100);
	}
}

실행결과


(2) 여러 클래스가 이어져 있어도 사용가능한가

1) 메인 클래스
public class Bussiness {
	public static void main(String[] args) {
		
		// 하위 클래스 객체 생성
		// 하게되면 연결되어있는 최상위-상위-하위 모두 메모리에 올라간다
		AA aa = new AA();
	}
}



2. 최상위 P 클래스
public class P {
	int a = 1;
	public P() {
		System.out.println("상위 클래스");
	}
}



3. 상위 A 클래스
public class A extends P{
	int a = 10;
	public A() {
		System.out.println("하위 클래스");
	}
}



4. 하위 AA 클래스
public class AA extends A{
	int a = 200;
	public AA() {
		// 하나 위에있는 A로 가서 값을 가져온다
		System.out.println("손녀 클래스 " + super.a);
		// AA위에 있는 A의 위에있는 P의 값을 가져오고 싶어도
		// 안됨
//		System.out.println("손녀 클래스 " + super.super.a);
	}
}

실행 결과


final 클래스와 final 메소드

각각 해석이 조금 다르지만 특정 클래스, 필드, 메소드를 상속해주고 싶지 않다면 사용한다.

상속할 수 없는 final 클래스

클래스 선언시 final 키워드를 class 앞에 붙이게 되면 이제 이 클래스는 최종적인 클래스가 되었기에 상속할 수 없게 된다.

// final 설정을 해주게 되면
final class One{  }

// 이렇게 사용할 때 에러가 난다
// One은 상속을 받을 수 없는 클래스니까
class two extends One { }

오버라이딩할 수 없는 final 메소드

부모 클래스를 상속해서 자식 클래스를 선언할 때 부모 클래스에 선언된 final 메소드는 자식 클래스에서 재정의할 수 없다

1) 상위 클래스
public class Car {
	//필드
	public int speed;
	
	// 메소드
	public void speedUp() {
		speed +=1;
	}
	
	// final 메소드
	// =사용은 가능하나, 재정의가 불가한 메소드로 선언
	public final void stop() {
		System.out.println("차를 멈춤");
		speed = 0;
	}
}




2) 하위 클래스
public class CarSports extends Car {
	
	@Override
	public void speedUp() {
		speed += 10;
	}
	
    	// 상위 메소드에서 final 메소드로 설정해줬기 때문에
	// 오버라이딩해 사용할 수 없다
//	@Override
//	public void stop() {
//		System.out.println("스포츠카 멈춤");
//		speed = 0;
//	}
	
}




3) 출력해보자
class CarPrint {
	public static void main(String[] args) {
		// 객체 생성
		CarSports cp = new CarSports();

		System.out.println(cp.speed);
		cp.speedUp();
		System.out.println(cp.speed);
		cp.stop();
	}
}

Protected 접근 제한자

접근 제한자는 public, protected, default, private와 같이 네 가지 종류가 있다.

  1. public
    : 클래스, 필드, 생성자, 메소드
    : 접근할 수 없는 클래스 없음. 같은 프로젝트 내에만 있다면 가능
  2. protected
    : 필드, 생성자, 메소
    : 하위 클래스 & 같은 패키지에 있다면 사용 가능
  3. default
    : 클래스, 필드, 생성자, 메소드
    : 같은 패키지라면 사용 가능하다
  4. private
    : 필드, 생성자, 메소드
    : 자기 자신만 사용 가능하다

protected는 같은 패키지 내에서는 접근 제한이 없다

1) A 클래스
package package1;

public class Protected_A {
	protected String field;
	
	protected Protected_A() {	}
	protected void method() {	}
}


2) B 클래스
package package1;

public class Protected_B {
	public void method() {
		// 상속 관계는 아니지만 같은 패키지에 있으므로 객체화해 사용 가능
		Protected_A a = new Protected_A();
		
		a.field = "value";
		a.method();
	}
}

protected는 다른 패키지에서는 사용 불가하다

1) A 클래스
package package1;

public class Protected_A {
	protected String field;
	
	protected Protected_A() {	}
	protected void method() {	}
}



2) C 클래스
package package2;

import package1.Protected_A;

// 다른 패키지이고, 상속관계도 아닌 protecte 클래스를 사용할 수 없다
public class Protected_C {
	public void method() {
		// 생성자가 protected 이므로 에러
//		Protected_A a = new Protected_A();
//		
//		a.field = "value";
//		a.method();
	}
}

상속관계에 있다면 다른 패키지에서도 사용 가능

1) A 클래스
package package1;

public class Protected_A {
	protected String field;
	
	protected Protected_A() {	}
	protected void method() {	}
}



2) D 클래스
package package2;

import package1.Protected_A;

// 같은 패키지는 아니지만 자식 클래스이기 때문에 A클래스를 사용 가능
public class Protected_D extends Protected_A{
	public Protected_D() {
		// 부모인 A생성자 호출
		super();
		
		// 현재 D 클래스에 field와 method()가 없으므로 this. 사용 가능
		// 현재 클래스에 동일명으로 작성된게 있다면 그거부터 찾아가기 때문에
		// 사용할 수 없다
		this.field = "value";
		this.method();
		
		// 부모에 있는 값과 메소드에 넣고 싶은 것이니 super. 도 사용가능
		super.field = "value2";
		super.method();
	}
}

0개의 댓글