상속을 하려면 extends를 사용한다.
public class Car {
public void move(){
System.out.println("차를 이동합니다.");
}
}
public class ElectricCar extends Car{
public void charge(){
System.out.println("충전합니다.");
}
}
public class GasCar extends Car{
public void fillUp(){
System.out.println("기름을 주유합니다.");
}
}
public class CarMain {
public static void main(String[] args) {
ElectricCar electricCar = new ElectricCar();
electricCar.move();
electricCar.charge();
GasCar gasCar = new GasCar();
gasCar.move();
gasCar.fillUp();
}
}
자식클래스는 부모의 필드와 메서드를 모두 물려받으므로 부모의 필드와 메서드를 사용할 수 있다.
자바는 다중상속을 지원하지 않는다. 하나의 클래스는 하나의 부모만 가질수있다. 물론, 그 부모가 또 부모를 가지는건 가능하다.
오버라이딩: 부모로 부터 상속받은 기능을 자식이 재정의 하는것.
public class ElectricCar extends Car {
public void charge(){
System.out.println("충전합니다.");
}
@Override
public void move(){
System.out.println("전기차가 더 빠르게 움직입니다.");
}
}
@Override
오버라이딩한 메서드 위에 이 애노테이션을 붙여야한다.
사실, 붙이지 않아도 오버라이딩이 되긴하는데, 오버라이딩 조건을 만족시키지 않으면 컴파일 에러가 발생하므로, 실수로 오버라이딩을 못하는 경우를 방지해준다.
굳이 @Override를 붙이지 않아도 본인타입에서 move()메서드를 찾기 때문에 오버라이딩한 move가 호출된다.
그러나, 만약 movee라고 내가 잘못 메서드를 정의해놓으면 부모타입으로 올라가서 move를 찾을 것이다. -> 오버라이딩 안됨
고로 @Override로 붙여서 명확성을 가지는게 좋다.
참고
오버라이딩 조건
현재 Parent와 Child의 패키지가 다르다.
public class Parent {
public int publicValue;
protected int protectedValue;
int defaultValue;
private int privateValue;
public void publicMethod() {
System.out.println("Parent.publicMethod");
}
protected void protectedMethod() {
System.out.println("Parent.protectedMethod");
}
void defaultMethod() {
System.out.println("Parent.defaultMethod");
}
private void privateMethod() {
System.out.println("Parent.privateMethod");
}
public void printParent() {
System.out.println("==Parent 메서드 안==");
System.out.println("publicValue = " + publicValue);
System.out.println("protectedValue = " + protectedValue);
System.out.println("defaultValue = " + defaultValue); //부모 메서드 안에서 접근 가능
System.out.println("privateValue = " + privateValue); //부모 메서드 안에서 접근 가능
//부모 메서드 안에서 모두 접근 가능
defaultMethod();
privateMethod();
}
}
public class Child extends Parent {
public void call(){
publicValue = 1;
protectedValue = 1;
//defaultValue = 1 //다른패키지 접근 불가
//privateValue = 1; // private은 클래스 내부에서만 사용가능
publicMethod();
protectedMethod();
//defaultMethod(); //protected는 다른패키지 접근불가
//privateMethod(); //private는 클래스 내부에서만 사용가능
printParent(); //public 메서드 접근가능, 해당 메서드 내부에서 private protected메서드 부를 수 있음, 자기 클래스 내부라서
}
}
printParent()메서드는 public메서드라 접근가능, 해당 메서드 내부에서 private,protected메서드를 호출하는데, 이건 클래스 내부에서 호출하는거라 접근가능
super를 사용하는 이유가 뭘까?
super를 사용하면 부모의 필드와 메서드에 접근이가능하다.
public class Parent {
public String value = "parent";
public void hello(){
System.out.println("Parent.hello");
}
}
public class Child extends Parent{
public String value = "child";
@Override
public void hello(){
System.out.println("Child.hello");
}
public void call(){
System.out.println("this value = "+ this.value);
System.out.println("super value = "+ super.value);
this.hello();
super.hello();
}
}
항상 호출한 클래스의 타입부터 시작한다고 했으므로, 부모의 hello를 호출하고 싶어도.
child.hello()를 하면 Child.hello가 나올것이다.
이때 super.hello()를 통해 부모클래스에 있는 기능을 사용할 수 있다.
public class SuperMain {
public static void main(String[] args) {
Child child = new Child();
child.call();
}
}
생각해보면, 자식인스턴스를 생성할때 부모의 인스턴스도 생성된다고 하였는데,
우리는 부모의 생성자를 호출 해준적이 없다.
자바가 부모 클래스의 생성자가 기본생성자인 경우에는 super()를 자동으로 호출해준다.
public class ClassA {
public ClassA(){
System.out.println("ClassA 생성자");
}
}
ClassA -> 파라미터가 없는 기본생성자임
public class ClassB extends ClassA{
public ClassB(int a){
//super();
System.out.println("ClassB 생성자 a="+a);
}
}
ClassB생성자는 반드시 부모의 생성자를 호출해야한다. 그러나 supr()를 호출하지 않아도, ClassA의 생성자는 기본생성자이기 때문에 자동으로 자바가 호출해준다.
public class ClassC extends ClassB{
public ClassC(){
//super(); 기본생성자 호출 불가 -> B에서 이미 생성자를 지정해놨기때문에 기본생성자를 Java가 만들지 않음
super(10);
System.out.println("classC 생성자");
}
}
ClassC에서는 기본생성자를 반드시 호출해야한다.
왜냐하면, B에서 이미 생성자를 선언했으므로 자바가 기본생성자를 만들지 않는다. (생성자를 하나도 만들지 않은경우에 자바가 기본생성자 자동으로 만듬),
고로 ClassC생성자는 반드시 부모클래스의 생성자를 호출해줘야한다.
부모에서 기본생성자가 아닌 생성자를 정의했다 -> 자식 생성자에서 반드시 super호출