[JAVA] 상속

msung99·2022년 4월 4일
0
post-thumbnail

상속

  • 코드를 물려 받는 것
  • 부모 클래스(기본 클래스), 자식 클래스(파생 클래스)로 구성
  • 자식 클래스 쪽에서 어떤 클래스를 물려받을 것인지 명시해야함. => extends 키워드를 사용

상속 개념

  • 부모의 코드를물려받는 다는 것이 꼭 장점만 있는 것은 아니다.

  • 코드 의존성 : 부모 클래스와 자식 클래스는 코드상으로는 분리되어있다. 그런데 객체화가 되면 의미상 자식 객체안에 부모 클래스가 들어가게 된다.

  • 상속 계층부에서 꽤 부모 클래스에 속하는 애를 고쳐야하면, 그 부모 클래스를 상속받은 모든 자식 클래스들이 부모에서 수정된 코드의 영향을 받을것인지 안받을 것인지 판단하기 위해 자식 클래스 코드를 싹다 까봐서 확인해야 한다.

  • 다형성 구현 가능

  • 부모를 물려받는 다고 해서, 모든 것을 자식에서 사용가능한 것은 아님. 부모의 private 은 자식에서 사용 불가능. 자식에서 사용하고 싶다면 public 으로 열어줘야함.

  • 다른 패키지의 클래스를 상속받아 사용하고 싶다면 default 로 선언


형태

public class B extends A
  • 자바는 단일 상속만 지원(하나만 상속 받을수있음)한다.
  • 즉, 다중 상속(여러개 상속)이 불가능하다

자식에서 부모의 private를 사용 못하므로,
부모의 멤버를 초기화하려면 부모의 public 메소드로 get, set 를 만들고 호출해서 초기화 해야한다.


부모 생성자 호출

형태 : super(매개값, ...)

  • 자식 객체를 만들면 부모 객체가 알아서 먼저 만들어지는 개념
    • 생성 순서는 부모 객체 => 자식 객체이지만, 자식 객체를 만들면 부모 객체가 만들어지고 자식 객체가 만들어지는 것
  • 생성자 호출 및 객체 생성순서 : 자식 생성자가 호출되기 전에 부모 생성자가 호출되서 부모 객체를 생성후, 자식 생성자가 호출되서 자식 객체가 생성되는 것이다.

super() 로 명시적으로 부모 클래스 생성자 호출하기

  • 자식 클래스 생성자 코드의 가장 첫 줄에 super() 키워드가 있어야한다!!!!

  • 디폴트 생성자는 우리 눈에 안보일뿐, 자동적으로 생성이 된다.

  • 디폴트 생성자는 A() 처럼 괄호안에 매개변수가 아무것도 없는 것을 의미

A(){
  Sysout.println("~~");
}

자식의 생성자에서 부모 생성자를 호출해야하는데, 자식의 생성자를 만들지 않아서 이를(부모 생성자 호출을) 명시적으로 하지 않았다면 컴파일러가 부모의 디폴트 생성자를 암시적으로 호출시킨다.

  • 즉, 자식 클래스 생성자 안에서 디폴트 생성자처럼 부모 생성자를 명시적으로 호출하는 문장이 없는 경우, 부모의 디폴트 생성자를 자동으로 생성한다.

자식 생성자를 명시적으로 생성했는데 그 자식 생성자에서 부모 생성자를 명시적으로 호출하는 문장이 없는 경우, 컴파일러가 자동으로 부모 클래스의 디폴트 생성자를 호출한다. 그러나, 부모 클래스에서 부모 생성자가 이미 정의되어 있는데 자식 생성자에서 부모 생성자를 호출하지 않은경우 에러가 발생한다.

  • 자식 생성자를 명시적으로 생성했는데 그 자식 생성자에서 부모 생성자를 명시적으로 호출하는 문장이 없다면 에러가 발생한다. 명시적으로 자식 생성자를 생성할 때는 반드시 그 안에 부모 생성자를 호출하는 문장이 있어야 한다.

  • super() : 자식 클래스의 명시적으로 부모 생성자 호출하는 키워드

    • 자식 생성자에서 명시적으로 생성자를 생성할시에(디폴트 생성자가 아닌 우리가 명시적으로 자식 생성자를 정의해준다면) 반드시 super() 키워드로 부모 생성자를 호출해야 한다.

예시
CASE1) 부모 생성자가 이미 정의되어 있는 경우

// 부모 클래스
package practice;

public class CellPhone {
	String model;
	String color;
	CellPhone(String model, String color){    // 부모 생성자가 정의되어 있다.
		this.model = model;
		this.color = color;
	}
	
	void powerON() {System.out.println("전원을 킵니다.");}
	void powerOff() {System.out.println("전원을 끕니다");}
	void sendVoice(String message) {System.out.println("자기:" + message); }
	void receiveVoice(String message) {System.out.println("상대방:" + message);}
}
// 자식 클래스
package practice;

public class DemCellPhone extends CellPhone{
	int channel;
	DemCellPhone(String model, String color, int channel){
		super(model, color)      //    =>   super() 키워드로 반드시 부모 생성자를 호출해줘야한다.
		this.color = color;
	}
	
	void turnOnDmb(){System.out.println("채널 " + channel + "번 dmb 방송 수신을 시작합니다");}
}

CASE2) 부모 생성자가 정의되어 있지 않은 경우. (디폴트 부모 생성자인 경우)

// 부모 클래스
package practice;

public class CellPhone {
	String model;
	String color;
    
    // 부모 생성자가 없음. (디폴트 생성자)
    
	void powerON() {System.out.println("전원을 킵니다.");}
	void powerOff() {System.out.println("전원을 끕니다");}
	void sendVoice(String message) {System.out.println("자기:" + message); }
	void receiveVoice(String message) {System.out.println("상대방:" + message);}
}
// 자식 클래스
package practice;

public class DemCellPhone extends CellPhone{
	int channel;
	DemCellPhone(String model, String color, int channel){  
      this.model = model;     // 이 경우는 자식 생성자가 명시적으로 호출되었지만,
      this.color = color;     // 컴파일러가 자동으로 디폴트 부모 생성자를 호출해주므로
      this.channel = channel;   // super() 키워드로 따로 부모 생성자를 호출할 필요는 없다.(몰론 super키워드로 부모 디폴트 생성자를 호출해도 되긴함)
	}
	
	void turnOnDmb(){System.out.println("채널 " + channel + "번 dmb 방송 수신을 시작합니다");}
}

메소드 오버라이드(재정의)

  • 오버로딩(중복 정의) vs 오버라이딩(재정의)

  • 오버로딩 : 같은 이름을 가지고 있는 메소드를 중복해서 여러개 정의하는것. 각각의 메소드들은 서로 다르게 동작한다.

  • 오버라이딩 : 기존에 있던것을 덮어버리고 새롭게 재정의.

    • 메소드와 관련한 모든 것(메소드명, 리턴타입, 매개변수 목록의 개수,숫자,순서 등등) 이 모두 똑같은 메소드를 하나 더 만드는것.
    • 부모 안에있는 메소드를 자식 클래스에도 만들어준다. 결론적으로 부모와 자식에 있는 메소드 2개는 똑같으므로 자식 클래스에 정의된 메소드가 정의되고 호출된다.

    @Override 어노테이션

  • 상속 관계에서 오버라이딩이 정상적으로 잘 되었는지 체크해줌. 잘 안되었으면 에러를 발생시켜준다.

// 부모 클래스에서의 메소드 func
int func(int)

// 자식 클래스에서의 메소드 func
int func(double)    => @Override 가 없다면 아무 문제없이 실행됨. 다만 오버라이딩 기능은 수행되지 않음

@Override         => 오버라이드 오노테이션이 있다면 다음줄에 오는 메소드는 오버라이딩된 것이여야함. 아니면 오류 발생
int func(double)  => 오류 발생

@Override
int func(int)       => 정상 실행
  • 자식 클래스에서 오버라이딩 되기전의 부모 클래스 메소드를 사용하는 방법
    • super() 를 사용하면 된다.
class Parent{
  void method1(){...}
  void method2(){...}
}

class Child extends Parent{
  void method2(){...} 
  void method3(){
    method2();   => 자식 클래스에서 오버라이딩된 메소드 호출
    super.method2();  => 부모 클래스에서 오버라이딩 되기전의 메소드 호출
  }
}

final

  • final 키워드가 붙으면 변경될 수 없음을 의미.

  • final을 변수에 사용하면 그 변수의 값을 불가능해진다

  • 모든 객체가 딱 하나만 가치고 있어도 괜찮을 변수라면 static final 로 만드는 것을 권장

  • final 을 클래스에 붙이면 상속이 불가능해진다.

    • final 키워드 클래스는 부모 클래스가 될 수가 없게되서, 이 메소드로 부터 상속을 못 받는다.
  • 오버라이딩이 불가능한 final 메소드

    • 자식 클래스가 오버라이딩을 못하도록 부모 클래스의 메소드를 final 로 생성할 수 있다.
    • 메소드 앞에 final을 붙이면 나를 상속받는 모든 자식들이 그 메소드에 대해서 오버라이딩이 불가능해진다.

protected

  • 상속 관계에서만 권한을 풀어주는 것
  • 다른 패키지에있는 클래스가 나를 상속받으면 접근이 가능해진다.
    (public 권한을 열어주는 것과 마찬가지로 생각하면 된다)

0개의 댓글