부모의 코드를물려받는 다는 것이 꼭 장점만 있는 것은 아니다.
코드 의존성 : 부모 클래스와 자식 클래스는 코드상으로는 분리되어있다. 그런데 객체화가 되면 의미상 자식 객체안에 부모 클래스가 들어가게 된다.
상속 계층부에서 꽤 부모 클래스에 속하는 애를 고쳐야하면, 그 부모 클래스를 상속받은 모든 자식 클래스들이 부모에서 수정된 코드의 영향을 받을것인지 안받을 것인지 판단하기 위해 자식 클래스 코드를 싹다 까봐서 확인해야 한다.
다형성 구현 가능
부모를 물려받는 다고 해서, 모든 것을 자식에서 사용가능한 것은 아님. 부모의 private 은 자식에서 사용 불가능. 자식에서 사용하고 싶다면 public 으로 열어줘야함.
다른 패키지의 클래스를 상속받아 사용하고 싶다면 default 로 선언
public class B extends A
자식에서 부모의 private를 사용 못하므로,
부모의 멤버를 초기화하려면 부모의 public 메소드로 get, set 를 만들고 호출해서 초기화 해야한다.
형태 : super(매개값, ...)
자식 클래스 생성자 코드의 가장 첫 줄에 super() 키워드가 있어야한다!!!!
디폴트 생성자는 우리 눈에 안보일뿐, 자동적으로 생성이 된다.
디폴트 생성자는 A() 처럼 괄호안에 매개변수가 아무것도 없는 것을 의미
A(){
Sysout.println("~~");
}
자식의 생성자에서 부모 생성자를 호출해야하는데, 자식의 생성자를 만들지 않아서 이를(부모 생성자 호출을) 명시적으로 하지 않았다면 컴파일러가 부모의 디폴트 생성자를 암시적으로 호출시킨다.
자식 생성자를 명시적으로 생성했는데 그 자식 생성자에서 부모 생성자를 명시적으로 호출하는 문장이 없는 경우, 컴파일러가 자동으로 부모 클래스의 디폴트 생성자를 호출한다. 그러나, 부모 클래스에서 부모 생성자가 이미 정의되어 있는데 자식 생성자에서 부모 생성자를 호출하지 않은경우 에러가 발생한다.
자식 생성자를 명시적으로 생성했는데 그 자식 생성자에서 부모 생성자를 명시적으로 호출하는 문장이 없다면 에러가 발생한다. 명시적으로 자식 생성자를 생성할 때는 반드시 그 안에 부모 생성자를 호출하는 문장이 있어야 한다.
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 오버라이딩(재정의)
오버로딩 : 같은 이름을 가지고 있는 메소드를 중복해서 여러개 정의하는것. 각각의 메소드들은 서로 다르게 동작한다.
오버라이딩 : 기존에 있던것을 덮어버리고 새롭게 재정의.
상속 관계에서 오버라이딩이 정상적으로 잘 되었는지 체크해줌. 잘 안되었으면 에러를 발생시켜준다.
// 부모 클래스에서의 메소드 func
int func(int)
// 자식 클래스에서의 메소드 func
int func(double) => @Override 가 없다면 아무 문제없이 실행됨. 다만 오버라이딩 기능은 수행되지 않음
@Override => 오버라이드 오노테이션이 있다면 다음줄에 오는 메소드는 오버라이딩된 것이여야함. 아니면 오류 발생
int func(double) => 오류 발생
@Override
int func(int) => 정상 실행
class Parent{
void method1(){...}
void method2(){...}
}
class Child extends Parent{
void method2(){...}
void method3(){
method2(); => 자식 클래스에서 오버라이딩된 메소드 호출
super.method2(); => 부모 클래스에서 오버라이딩 되기전의 메소드 호출
}
}
final 키워드가 붙으면 변경될 수 없음을 의미.
final을 변수에 사용하면 그 변수의 값을 불가능해진다
모든 객체가 딱 하나만 가치고 있어도 괜찮을 변수라면 static final 로 만드는 것을 권장
final 을 클래스에 붙이면 상속이 불가능해진다.
오버라이딩이 불가능한 final 메소드