상속(Inheritance)
부모가 자식에게 물려주는 행위를 말한다.
자식은 상속을 통해서 부모가 물려준 것을 자연스럽게 이용할 수 있다.
부모 클래스 = 상위 클래스 // 자식클래스 = 하위 클래스 or 파생 클래스
부모가 자식에게 물려주는 행위로 중복 코드를 줄여 개발 시간을 단축시키고, 클래스 수정을 최소화할 수 있다.
* 클래스 상속
자식이 extends 키워드를 이용하여 부모 클래스를 선택하여 상속 받는다. → 다중 상속은 불가능하다.
부모 클래스
public class Phone {
public String model;
public String color;
public Phone() {
System.out.println("부모 생성자 호출");
}
public void bell() {
System.out.println("[부모] 벨이 울린다.");
}
public void hangup() {
System.out.println("[부모] 전화를 끊습니다.");
}
public void sendVoice(String message) {
System.out.println("[부모] " + message);
}
}
부모클래스 Phone를 상속받은 SmartPhone
public class SmartPhone extends Phone{
public boolean wifi;
public SmartPhone(String model, String color) {
this.model = model;
this.color = color;
}
public void setWifi(boolean wifi) {
this.wifi = wifi;
System.out.println("[자식] 와이파이 상태를 변경했습니다.");
}
}
* 부모 생성자 호출
자식 객체를 생성하면 부모 객체가 먼저 생성된 다음에 자식 객체가 생성된다.
부모 객체에 기본 생성자가 있다면, 자식 생성자의 맨 첫 줄에 컴파일 과정에서 자옫 super() 키워드가 추가된다.
부모 클래스에 기본 생성자가 없고 매개변수를 갖는 생성자만 있다면 개발자가 직접 자식 생성자에 super(매개값,.....)을 추가한다.
* 상위 클래스의 생성자 호출 super
하위클래스 생성자에서 상위클래스 생성자를 호출하는 것이 가장 먼저 해야하는 일
상속한 클래스의 인스턴스 생성하는 방법 1. 메모리 공간에 인스턴스 할당 2. 생성자 호출 -> 상속 상위클래스 생성자 호출 및 실행
이론 : 하위 클래스가 생성될 때 super 키워드를 사용하여 상위 클래스의 생성자를 호출 가능(하위클래스가 상위클래스의 초기화를 수행하기 위해) → 재사용성, 유지보수성에 도움
메소드가 오버라이딩되었다 면 부모 객체의 메소드는 숨겨지기 때문에 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출
* 상속
- 기존에 정의된 클래스에 메소드와 변수를 추가하여 새로운 클래스를 정의 하는 것
- 상속 받은 하위클래스의 인스턴스에는 상속의 대상인 상위클래스에 정의된 메소드와 변수가 존재한다.
- 상위 클래스의 인스턴스 변수는 상위 클래스의 생성자 내에서 초기화가 되어야 하고, 단지 하위 클래스에서는 상위 클래스의 인스턴스 변수를 초기화하는데 필요한
데이터를 키워드 super를 통해서 전달만 하는 것
- 인스턴스 변수에 별도의 초기화를 진행하지 않으면, 디폴트 값으로 초기화됨
- 하위 클래스의 생성자가 호출되며 super문에 의해서 상위 클래스의 생성자가 먼저 실행 됨
→ 하위클래스가 먼저 호출 되지만 상위 클래스의 생성자가 먼저 이뤄짐(상위 클래스 멤버가 먼저 초기화가 됨)
※ super문이 있기 때문에 상위클래스의 생성자가 호출된 것처럼 보일 수 있지만 하위클래스의 생성자 내에서는 반드시 상위 클래스의 생성자가 호출되어야 함
- 상속 관계에 있는 모든 클래스의 생성자가 호출되어야, 모든 인스턴스 변수들이 적절히 초기화가 된다.
override
하위 클래스에서 메소드를 다시 정의
인스턴스의 오버라이딩 된 메소드를 호출하면, 상위 클래스가 아닌 하위클래스의 메소드가 호출된다.
하위클래스에서 오버라이딩 된 상위 클래스의 메소드를 호출하려면 super키워드 사용
상위 클래스의 메소드가 하위 클래스의 메소드에 의해서 오버라이딩 되면, 외부에서는(참조변수를 통해서는) 오버라이딩 된 상위 클래스의 메소드를 호출할 수 없다.
오버라이딩이 이루어지면 오버라이딩된 상위 클래스의 메소드는 어떠한 참조변수로도 호출이 불가능하도록 철저히 가려지는 것
-> 참조변수의 자료형에 상관없이 인스턴스 외부에서는 절대 호출이 불가하며, 대시네 오버라이딩을 한 하위 클래스의 메소드가 호출 된다.
※ 정리
-오버라이딩 된 상위 클래스의 메소드는 오버라이딩을 한 하위 클래스의 메소드에 의해서 가려진다
-> 즉 외부에서는 참조변수를 통해서 오버라이딩 된 상위 클래스의 메소드를 호출할 수 없다.
* 부모 메서드 호출
super 키워드와 도트(.)연산자를 사용해 부모 클래스를 호출할 수 있다.
public class SmartPhone extends Phone{
// 코드 생략
@Override
public void bell() {
super.bell();
System.out.println("[자식] 벨이 울린다.");
}
}
public class SmartPhoneEx {
public static void main(String[] args) {
SmartPhone smartPhone = new SmartPhone("Galaxy", "Gary"); // [부모] Phone(String model, String color) 생성자 호출
smartPhone.bell(); // [부모] 벨이 울린다. // [자식]벨이 울린다.
}
* final 클래스
최종적인 클래스로 더 이상 상속할 수 없다. → 부모 클래스가 될 수 없다.
final 클래스의 대표적인 예는 자바 표준 API에서 제공하는 String 클래스이다.
* final 메서드
최종적인 메서드로 오버라이딩할 수 없다.
부모 클래스를 상속해서 자식 클래스르 선언할 때 부모 클래스에 선언된 final 메소드는 자식 클래스에서 재저으이할 수 없다는 것
* 타입 변환과 다형성
> 다형성
같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질을 말한다.
코드 측면에서 보면 다형성은 하나의 타입에 여러 객체를 대입함으로써 다양한 기능을 이용할 수 있도록 해준다.
자바는 다형성을 위해 부모 클래스로 타입 변환을 허용한다. → 부모 타입에 모든 자식 객체가 대입될 수 있다.
> 자동 타입 변환
자식은 부모의 특징과 기능을 상속받기 때문에 동일하게 취급될 수 있다는 것
타입 변환이 일어나는 이유는 다형성을 구현하는 기술적 방법 때문(다형성 : 동일한 타입을 사용하지만 다양한 결과가 나오는 성질)
실행 도중에 어떤 객체를 필드로 저장하느냐에 따라 실행 결과가 달라지는 것 이것이 필드의 다형성
자동 타입 변환에서 매개변수의 타입이 클래스일 경우, 해당 클래스의 객체뿐만 아니라 자식 객체까지도 매개값으로 사용할 수 있다는 것
부모타입으로 자동 타입 변환된 후에는 부모 클래스에 선언된 필드와 메서드만 접근이 가능하다.
단, 자식 클래스에서 오버라이딩 된 메서드가 있다면 부모 메서드 대신 오버라이딩된 메서드가 호출된다.
> 강제 타입 변환
부모 타입을 자식 타입으로 변환하는 것
자식클래스 변수 = (자식 클래스) 부모클래스타입 =? 자식 타입이 부모 타입으로 변환된 상태
// 부모 클래스
class Parent{
String field1;
void method1(){...}
void method2(){...}
}
// 자식 클래스
class Child extends Parent{
String field2;
void method3(){...}
}
public class Ex01 {
public static void main(String[] args) {
Parent parent = new Child();
parent.field1 = "xxx";
parent.method1();
parent.method2();
parent.field2 = "yyy"; // (불가능)
parent.method3(); // (불가능)
// Child child = (Child) new Parent(); // 런타임 에러 : ClassCastException Exception
Parent parent = new Child();
Child child = (Child) parent;
parent.field2 = "yyy"; // (가능)
child.method03(); // Child - method03() => (가능)
}
}
* 객체 타입 확인
인스턴스의 자료형에 따라서호출할 메소드를 다르게할 경우
> instanceof 연산자
참조변수가 참조하고 있는 인스턴스의 실제 자료형을 묻는 연산자가 아닌 → 상속 관계를 바탕으로 형변환이 가능한지를 묻는 연산자이며, boolean형식으로 반환
Boolean result = 객체 instanceof 타입;
객체가 타입이면 true, 그렇지 않으면 false
자바 12부터는 instanceof 연산의 결과가 true일 경우, 우측 타입 변수를 바로 사용 가능하여 따로 강제 타입 변환이 필요 없다.
* 추상 클래스
객체를 생성할 수 있는 클래스들의 공통적인 필드나 메소드를 추출해서 선언한 클래스로 new 연산자를 사용해 객체를 직접 생성할 수 없다.
추상 클래스는 객체를 생성할 수 있는 클래스의 부모 역할만 한다.
객체를 직접 생성할 수 있는 클래스를 실체 클래스, 공통적인 특성을 추출해서 선언한 클래스는 추상 클래스
> 추상 클래스의 용도
실체 클래스들의 공통된 필드와 메소드의 이름을 통일할 목적
실체 클래스를 작성할 때 시간을 절약
* 추상 클래스 선언
- 클래스 선언 시 abstract 키워드를 붙이면 추상 클래스가 된다.
public abstract class Phone {
// 필드 선언
String owner;
// 생성자 선언
public Phone(String owner) {
this.owner = owner;
}
//메서드 선언
void turnOn() {
System.out.println("전원 ON");
}
void turnOff() {
System.out.println("전원 OPF");
}
public abstract void sound();
}
* 추상 메소드와 재정의
추상 메소드란 abstract 키워드가 붙고, 메소드 실행 내용인 중괄호( { } )가 없다.
자식 클래스의 공통 메서드라는 것만 정의할 뿐, 실행 내용을 가지지 않는다.
자식 클래스에서 반드시 재정의해서 실행 내용을 채워야 한다.
'BACKEND > JAVA' 카테고리의 다른 글
| [이것이 자바다] 9장 중첩 클래스와 중첩 인터페이스 (0) | 2024.01.22 |
|---|---|
| [이것이 자바다] 8장 인터페이스 (0) | 2024.01.22 |
| [이것이 자바다] 6장 클래스 (0) | 2024.01.19 |
| [이것이 자바다] 5장 참조 타입 (0) | 2024.01.19 |
| [이것이 자바다] 3장 연산자 (0) | 2024.01.18 |