상속은 기존 클래스의 속성과 기능을 그대로 물려 받는 것
객체 지향 프로그래밍의 핵심 요소 중 하나로, 기존 클래스의 멤버 변수(필드)와 메서드를 새로운 클래스에서 재사용하게 해준다.
상속을 하면 적은 양의 코드로 새로운 클래스를 작성할 수 있으며 공통적인 코드를 관리하여 재사용성이 증가하고 코드의 추가와 변경이 쉬워진다.
상속을 사용하려면 extends 키워드를 사용한다.
extends는 대상 하나만 선택이 가능하다 (다중 상속 불가)
부모 클래스 (슈퍼 클래스) : 상속을 통해 자신의 필드와 메서드를 다른 클래스에 제공하는 클래스
자식 클래스 (서브 클래스) : 부모 클래스로부터 필드와 메서드를 상속받는 클래스
자바는 다중 상속을 지원하지 않는다. 따라서 extends 대상은 하나만 선택할 수 있다.
즉, 부모를 하나만 선택할 수 있으며, 부모가 또 다른 부모 하나를 갖는 것은 문제가 없다.
다중 상속을 허용하게 되면 클래스 관계가 복잡해지는 문제가 발생하기 때문에 허용하지 않는다.
상속 관계를 객체로 생성할 때 메모리 구조를 확인하기
ElectricCar electricCar = new ElectricCar();
new 연산자를 통해 ElectricCar 인스턴스를 생성한다고 하면, 해당 ElectricCar 뿐만 아니라 상속 관계에 있는 Car 까지 포함해서 인스턴스를 생성한다.
참조값은 해당 위치의 메모리 주소지만, 실제 그 안에는 Car와 ElectricCar 두가지 클래스 정보가 존재한다.
즉, 상속 관계를 사용하면 부모 클래스도 함께 포함해서 생성된다.
그래서 외부에서 볼 때는 하나의 인스턴스를 생성하는 것이지만, 내부에서는 부모와 자식이 모두 생성되고 공간도 구분된다.
그래서 상속에서 메서드나 변수를 호출할 때, 호출하는 변수의 타입(클래스)를 기준으로 선택을 한다.
ElectricCar electricCar = new ElectricCar();
electricCar.charge() // 호출
호출하는 변수의 타입은 ElectricCar 이므로 본인 타입에서 찾는 방식이다.
무조건 현재 자신의 타입에 맞는 곳에 기능이 있는지 찾아보고 없다면 부모 클래스로 넘어가서 찾는 형식이다.
ElectricCar electricCar = new ElectricCar();
electricCar.move() // 호출
참고 : 상속 관계인 객체의 인스턴스를 생성하면, 부모 필드 + 자식 필드 만큼 메모리 공간이 할당이 된다.
상속과 메모리 구조는 반드시 이해할 것!
상속 관계의 객체를 생성하면 부모와 자식이 모두 생성
상속 관계의 객체를 호출할 때, 대상 타입을 정해야한다. 이때 호출자의 타입을 통해 대상 타입을 찾는다.
현재 타입에서 기능을 찾지 못하면 상위 부모 타입으로 기능을 찾아서 실행
기능을 찾지 못하면 컴파일 오류
상속 관계에 있는 클래스에서 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것을 의미
메서드 이름은 같아야 한다.
메서드 매개변수 타입, 순서, 갯수가 같아야 한다.
반환 타입이 같아야 한다. 단, 반환 타입이 자식 클래스 타입일 수 도 있다.
접근 제어자를 부모 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
오버라이딩 메서드는 상위 클래스의 메서드보다 더 많은 체크 예외를 throws 로 선언할 수 없다.
static, final, private 키워드가 붙은 메서드에 오버라이딩이 불가능하다.
static : 클래스 레벨에서 작동하므로 인스턴스 레벨에서 사용하는 오버라이딩은 의미 없음
final 는 메서드 재정의 금지
private 메서드는 해당 클래스에서만 접근 가능하기에 오버라이딩 불가
생성자 오버라이딩은 불가능하다.
부모 클래스의 멤버를 참조할 수 있는 키워드
부모와 자식의 필드명이 같거나 메서드가 오버라이딩 되어 있다면, 자식에서 부모의 필드나 메서드를 호출할 수 있다.
이때 사용하는 것이 super 키워드
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); // -> 부모의 value값을 부름
this.hello();
super.hello(); // -> 부모의 hello() 메서드를 호출
}
}
여기서 Parent 클래스에는 멤버변수 value와 hello() 메서드가 정의되어 있다고 한다.
super는 부모 클래스에 대한 참조를 뜻한다.
필드 이름과 메서드 이름이 같지만 super를 사용해서 부모 클래스에 있는 기능을 사용할 수 있다.
부모 클래스의 생성자롤 호출할 수 있는 키워드
상속 관계를 사용하면 자식 클래스의 생성자에서 부모 클래스의 생성자를 반드시 호출해야 한다. (규칙)
상속 관계의 인스턴스를 생성하면 결국 메모리 내부에는 자식과 부모 클래스가 각각 다 만들어지기 때문
상속 관계에서 부모의 생성자를 호출할 때 super(...)를 사용하면 된다.
public class ClassB extends ClassA {
public ClassB(int a) {
super(); // 기본 생성자는 생략이 가능하다.
System.out.println("ClassB 생성자 a=" + a);
}
}
상속을 받으면 생성자 첫줄에 super(...)를 사용해서 부모 클래스 생성자를 호출해야 한다.
예외로 생성자 첫줄에 this(...)를 사용할 수 있지만, super(...)은 자식의 생성자 안에서 언젠가 반드시 호출해야한다.
부모 클래스의 생성자가 기본 생성자라면 super()를 생략할 수 있다.
상속 관계의 생성자 호출은 결과적으로 부모에서 자식 순서대로 실행
부모의 데이터를 먼저 초기화하고 그 다음에 자식의 데이터를 초기화
상속 관계에서 자식 클래스의 생성자 첫줄에 반드시 super(...)를 호출해야한다.
Object는 말 그대로 "객체"이며, 모든 클래스의 최상위 부모이다.
<Object 클래스의 메서드 몇가지..>
메서드 | 설명 |
---|---|
Object clone() | 해당 객체의 복제본을 생성하여 반환 |
boolean equals(Object object) | 해당 객체와 전달받은 객체가 같은지 여부를 반환 |
Class getClass() | 해당 객체의 클래스 타입을 반환함 |
int hashCode() | 자바에서 객체를 식별하는 정수값인 해시 코드를 반환 |
String toString() | 해당 객체의 정보를 문자열로 반환 & Object 클래스에서 클래스이름 @해쉬코드값 리턴 |