상속(inheritance)은 부모 클래스의 멤버를 자식 클래스에게 물려주는 것을 말한다.
프로그램에서는 부모 클래스를 상위 클래스라고 부르고, 자식 클래스를 하위 클래스 또는 파생 클래스라고 한다.
상속은 이미 잘 개발된 클래스를 재사용해서 새로운 클래스를 만들기 때문에 중복되는 코드를 줄여준다.
<클래스 상속>
현실에서 상속은 부모가 자식을 선택해서 물려주지만, 프로그램에서는 자식이 부모를 선택한다.
자식 클래스를 선언할 때 어떤 부모 클래스를 상속받을 것인지 결정하고, 선택된 부모 클래스는 extends뒤에 기술한다.
class 자식클래스 extends 부모클래스 {
//필드
//생성자
//메소드
}
예를 들어, Car클래스를 상속해서 SportsCar클래스를 설계하려면 다음과 같이 작성한다.
class SportsCar extends Car{
}
자바에서 상속의 특징은 다음과 같다.
- 여러 개의 부모 클래스를 상속할 수 없다. (extends 뒤에는 단 하나의 부모 클래스만 와야 한다.)
- 부모 클래스에서 private 접근 제한을 갖는 필드와 메소드는 상속 대상에서 제외된다.
또한 다른 패키지에 존재한다면 default 접근 제한을 갖는 필드와 메소드도 상속 대상에서 제외된다.
*핸드폰(부모클래스)를 통해 dmb폰(자식클래스) 작성 예제
//핸드폰클래스(부모클래스)
public class CellPhone {
//필드
String model;
String color;
//생성자
//메소드
void powerOn() { System.out.println("전원을 켭니다."); }
void powerOff() { System.out.println("전원을 끕니다."); }
void bell() { System.out.println("벨이 울립니다."); }
void sendVoice(String message) { System.out.println("자기: " + message); }
void receiveVoice(String message) { System.out.println("상대방: " + message); }
void hangUp() { System.out.println("전화를 끊습니다."); }
}
//dmb폰클래스(자식클래스)
public class DmbCellPhone extends CellPhone {
//필드
int channel;
//생성자
DmbCellPhone(String model, String color, int channel) {
this.model = model;
this.color = color;
this.channel = channel;
}
//메소드
void turnOnDmb() {
System.out.println("채널 " + channel + "번 DMB 방송 수신을 시작합니다.");
}
void changeChannelDmb(int channel) {
this.channel = channel;
System.out.println("채널 " + channel + "번으로 바꿉니다.");
}
void turnOffDmb() {
System.out.println("DMB 방송 수신을 멈춥니다.");
}
}
public class DmbCellPhoneExample {
public static void main(String[] args) {
//DmbCellPhone 객체 생성
DmbCellPhone dmbCellPhone = new DmbCellPhone("자바폰", "검정", 10);
//CellPhone으로부터 상속 받은 필드
System.out.println("모델: " + dmbCellPhone.model);
System.out.println("색상: " + dmbCellPhone.color);
//DmbCellPhone의 필드
System.out.println("채널: " + dmbCellPhone.channel);
//CellPhone으로부터 상속 받은 메소드 호출
dmbCellPhone.powerOn();
dmbCellPhone.bell();
dmbCellPhone.sendVoice("여보세요");
dmbCellPhone.receiveVoice("안녕하세요! 저는 홍길동인데요");
dmbCellPhone.sendVoice("아~ 예 반갑습니다.");
dmbCellPhone.hangUp();
//DmbCellPhone의 메소드 호출
dmbCellPhone.turnOnDmb();
dmbCellPhone.changeChannelDmb(12);
dmbCellPhone.turnOffDmb();
}
}
결과 :
<부모 생성자 호출>
자식 객체를 생성하면, 부모 객ㅌ체가 먼저 생성되고 그 다음에 자식 객체가 생성된다.
DmbCellPhone dmbCellPhone = new DmbCellPhone();
이 코드는 DmbCellPhone 객체만 생성하는 것처럼 보이지만, 사실은 내부적으로 부모인 CellPhone 객체가 먼저 생성되고 자식 객체가 생성된 것이다.
이 과정을 메모리로 표현하면 다음과 같다.
모든 객체는 클래스의 생성자를 호출해야만 생성되며, 부모 생성자는 자식 생성자의 맨 첫 줄에서 호출된다.
직접 자식 생성자를 선언하고 명시적으로 부모 생성자를 호출하고 싶으면 다음과 같이 작성하면 된다.
자식클래스(매개변수선언, ...){
super(매개값,...);
...
}
여기서 super()는 부모의 기본 생성자를 호출하며, super(매개값, ...)는 매개값의 타입과 일치하는 부모 생성자를 호출한다. 만약 매개값의 타입과 일치하는 부모 생성자가 없을 경우 컴파일 에러가 발생한다.
<메소드 재정의>
부모 클래스의 모든 메소드가 자식 클래스에 맞게 설계되어 있다면 가장 이상적인 상속이지만, 어떤 메소드는 자식 클래스가 사용하기에 적합하지 않을 수도 있다. 이런 경우에 메소드를 다시 수정해서 사용하며, 메소드 재정의(오버라이딩:Overriding)이라고 한다.
▶ 메소드 재정의 방법
- 부모의 메소드와 동일한 시그니처(리턴 타입, 메소드 이름, 매개 변수 목록)를 가져야 한다.
- 접근 제한을 더 강하게 재정의할 수 없다.
- 새로운 예외(Exception)를 throws할 수 없다.(예외는 다음에 학습 예정!)
메소드가 재정의되었다면 부모 객체의 메소드는 숨겨지기 때문에, 자식 객체에서 메소드를 호출하면 재정의된 자식 메소드가 호출된다.
▶ 부모 메소드 호출
자식 클래스 내부에서 재정의된 부모 클래스의 메소드를 호출해야 하는 상황이 발생한다면 명시적으로 super 키워드를 붙여서 부모 메소드를 호출할 수 있다.
super.부모메소드();
<final 클래스와 final 메소드>
final 키워드는 클래스, 필드, 메소드를 선언할 대 사용할 수 있는데, 해당 선언이 최종 상태이고 결코 수정될 수 없음을 뜻한다.
▶ 상속할 수 없는 final 클래스
클래스를 선언할 때 final 키워드를 class 앞에 붙이면 이 클래스는 최종적인 클래스이므로 상속할 수 없는 클래스가 된다. 즉, final 클래스는 자식 클래스를 만들 수 없다.
final클래스 선언 방법은 다음과 같다.
public final class 클래스 { ... }
final 클래스의 대표적인 예는 자바 표준 API에서 제공하는 String 클래스이다. String클래스는 final클래스이기 때문에 자식 클래스를 만들 수 없다.
▶ 재정의할 수 없는 final 메소드
메소드를 선언할 때 final 키워드를 붙이면 최종적인 메소드이므로 재정의할 수 없는 메소드가 된다. 즉, 부모 클래스를 상속해서 자식 클래스를 선언할 때 부모 클래스에 선언된 final 메소드는 자식 클래스에서 재정의할 수 없다.
final 메소드 선언 방법은 다음과 같다.
public final 리턴타입 메소드( [매개변수, ...]){...}
'프로그래밍언어 > Java' 카테고리의 다른 글
07-3 추상 클래스 (0) | 2021.04.28 |
---|---|
07-2 타입 변환과 다형성 (0) | 2021.03.09 |
06-6 패키지와 접근 제한자 (0) | 2021.02.23 |
06-5 인스턴스 멤버와 정적 멤버 (0) | 2021.02.17 |
06-4 메소드 (0) | 2021.02.15 |