생성자와초기화 블럭은 상속되지 않습니다.멤버만 상속됩니다.- 접근 제어자가
private또는default인 멤버들은 상속되지 않는다기보다 상속은 받지만 자손 클래스로부터접근이 제한되는 것입니다.- 모든 멤버를 복사해서 가져오지만 접근 제어자에 따라 제한 여부가 결정됩니다.
- 자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버와 자손 클래스의 멤버가 합쳐진
하나의 인스턴스로 생성됩니다. 참조값도 따로 생기지 않습니다.
클래스간의
관계를 맺어주는 방법으로 상속보다 더유연한포함을 자주 사용합니다.
포함 관계를 설명하는 두가지 개념이 있습니다.
1.Composite(합성): ex) 자동차-엔진, 집-방
2.Aggregation(집합): ex) 학과-학생, 도서관-책
| 특징 | Composite (합성) | Aggregation (집합) |
|---|---|---|
| 연관 관계 | 강함 (Strong) | 약함 (Weak) |
| 존재 의존성 | 부분 객체는 전체 객체에 의존적 (전체 소멸 시 부분 소멸) | 부분 객체는 전체 객체와 독립적 (전체 소멸해도 부분 존재) |
| 소유권 | 배타적 소유 (Exclusive) | 공유 가능 (Shared) |
| 생명주기 관리 | 전체 객체가 부분 객체의 생성 및 소멸 관리 | 부분 객체의 생성 및 소멸은 독립적일 수 있음 |
Java는 다중 상속을 의도적으로 막았습니다.
단일 상속만 허용하는 이유는 다음과 같습니다.
- 클래스
관계를단순하고명확하게- 멤버의
이름이 겹치는 경우를 방지
- 이름이 같은 변수라도
this,super를 통해 구별할 수 있습니다.- super는 한 번만 사용 가능합니다.
- 부모보다 상위 클래스의 인스턴스 변수는
up-casting을 통해 접근 가능합니다.
- 이 방법이 필요한 경우, 클래스
재설계를 고려해야 합니다.
public class InheritanceTestMain {
public static void main(String[] args) {
GrandChild grandChild = new GrandChild();
grandChild.myMethod(1);
}
}
class Parent {
int v=4;
}
class Child extends Parent{
int v=3;
}
class GrandChild extends Child{
int v=2;
void myMethod(int v) {
System.out.println("v = " + v); // local var v
System.out.println("this.v = " + this.v); // GrandChild instance var v
System.out.println("super.v = " + super.v); // Child instance var v
System.out.println("((Parent) this).v = " + ((Parent) this).v); // Parent instance var v
}
}
- 자식 클래스의 모든 생성자에서
첫 줄은 반드시 자신 혹은부모 클래스의 생성자를 호출 해야 합니다.
- 자식 클래스는 부모 클래스의 모든 멤버를 상속받고
생성자가 호출되는 시점부터 그 멤버들을 사용할 수 있기 때문에 반드시 초기화가 필요합니다.- 부모 클래스에 기본 생성자(매개변수가 없는 생성자)가 있는 경우
자식 클래스의 생성자들은 부모 클래스의 생성자(super())를 호출 하지 않으면
컴파일러가자동으로 부모 클래스의기본 생성자를 추가해줍니다.
- 모든 클래스의 공통 조상인 Object 의 생성자는 언제나 호출됩니다.
자식 클래스가 부모 클래스의 메서드를 오버라이딩 했다면
자식 클래스의 인스턴스에서는
- 업 캐스팅을 하더라도 오버라이딩 하기 전의
부모 메서드를 호출 할 수 없습니다.- 자식 클래스가 오버라이딩 하기 전 메서드를 호출하기 위해서는
super 를 사용하는 방법 뿐입니다.
public class SuperTest {
public static void main(String[] args) {
ParentClass child = new ChildClass(); //up-casting
child.myMethod(); // -> "ChildClass.myMethod"
// there is no way to call ParentClass.myMethod()
if (child instanceof ChildClass c) {
c.callParentMethod(); // -> "ParentClass.myMethod"
}
}
}
class ParentClass {
void myMethod() {
System.out.println("ParentClass.myMethod");
}
}
class ChildClass extends ParentClass{
@Override
void myMethod() {
System.out.println("ChildClass.myMethod");
}
void callParentMethod() {
super.myMethod(); // only way
}
}
instanceof 연산은 다음과 같은 경우에 컴파일러 에러가 발생합니다.
1.좌항 피연산자가참조값 또는 null이 아닌 경우
2.우항 피연산자가제네릭타입인 경우(와일드카드 타입은 허용)
3. 좌항의 피연산자를 우항의 피연산자로형변환 불가능한(상속 관계가 없는) 경우
public class InstanceofTest {
public static void main(String[] args) {
FireEngine fe = new FireEngine();
boolean b1 = fe instanceof FireEngine;
boolean b2 = fe instanceof Movable;
boolean b3 = fe instanceof Object;
boolean b4 = fe instanceof Car;
// boolean b5 = fe instanceof Ambulance; // <- compiler error
}
}
class Car {}
interface Movable {}
class FireEngine extends Car implements Movable {}
class Ambulance extends Car {}
null 의 타입이 무엇인지 java 에서 명시하고 있지는 않지만
모든 참조 변수에 대입 가능하다는 점에서최저 자손이라고 생각해 볼 수 있습니다.
- 모든 클래스의
최고 조상인Object타입의 참조 변수는대입 연산자의우항에 어떤 참조 타입의 값이 오더라도자동 형변환되어 할당될 수 있습니다.- 같은 관점에서
null은 대입 연산자의 좌항에 어떤 참조 변수 타입이 오더라도자동 형변환되니 모든 클래스의최저 자손이라고 할 수 있습니다.
Object o1 = new MyClass();
Object o2 = new String();
Object o3 = null;
MyClass myClass = null;
String string = null;
Object object = null;
- Java 의
import문은런타임 성능에 직접적인영향을 주지 않습니다.클래스 로딩과는별개의 작업입니다.- import 문의 주요 목적은 코드 내에서 클래스나 인터페이스의 정규화된 이름을 매번 쓰는
번거로움을 덜어주고 코드의가독성을 높이는 것입니다.