자바에서는 자식 클래스는 부모 클래스의 멤버(필드,메서드,이너 클래스) 를 물려받아 자식 클래스 내부에 포함시킬 수 있다.
이때 상속을 해주는 클래스를 부모 클래스 혹은 상위 클래스라 하며
상속을 받는 클래스는 자식 클래스 혹은 하위클래스라고 한다.
- 상속 문법
class 자식 클래스 extends 부모 클래스{
클래스 내용
}
상속을 하게 되면 자식 클래스는 부모 클래스의 멤버를 그대로 물려받으므로 코드의 중복성이 제거되어 코드가 간결해진다.
또 1개의 객체를 여러 가지 모양으로 표현할 수 있는 특성을 다형성이라고 하는데
상속을 하게 되면 다형적 표현이 가능해진다.
우리가 대학생을 사람이나 성인이라고 표현하는 것과 같은 맥락이다.
자바에서 상속을 할때는 다음과 같은 규칙이 있다.
class A{
int num = 3;
}
class B{
int num = 5;
}
class C extends class A, class B
클래스 C는 A와B를 모두 상속받았는데 이때 C의 num 필드에는 어떤 값이 저장되어야 할지 모호성이 발생하게 된다. 이런 이유로 다중 상속을 허용하지 않는다.
자바의 접근 지정자
public : 동일 패키지의 모든 클래스 + 다른 패키지의 모든 클래스에서 사용 가능
protected : 동일 패키지의 모든 클래스 + 다른 패키지의 자식 클래스에서 사용가능
default : 동일 패키지의 모든 클래스에서 사용 가능
private : 동일 클래스에서만 사용 가능
class A{
A() {
}
}
class B extends A{
A() {}
클래스 내부에는 필드,메서드,이너 클래스,생성자만이 올수 있다.
다음과 같이 생성자 A()를 상속받는다면 어디에 해당할까?
일단 소괄호와 중괄호가 있으므로 필드와 이너 클래스는 아니다.
클래스의 이름(B)과 다르므로 생성자도 아니다.
리턴 타입도 없으므로 메서드도 될 수 없다.
이러한 이유로 자식 클래스는 부모 클래스의 생성자를 상속 받지 않는다.
class A{
int m;
void abc();
}
class B extends A{
int n;
void bcd();
}
B가 A 를 상속받았을 때 B b = new B(); 와 같이 객체를 생성했을때를 보면
클래스 영역에 A,B가 생성 될 것이고 스택영역에는 참조 변수 b가 로딩된다.
힙 영역에는 실제 객체가 생성될 것이고 참조 변수 b는 B타입으로 선언 되어 있기 때문에 B타입 객체만을 가리킨다.

JVM은 자식 클래스의 객체를 생성할 때 먼저 부모 클래스의 객체를 생성한다.
그 이후에 자식 클래스 객체가 생성되며 결과적으로 부모 클래스 객체가
자식 클래스 객체에 포함되는 구조가 된다.
위의 예시에서 B() 생성자로 객체를 생성 했으므로 당연히 부모 클래스의 A객체가 먼저 생성된다. 이때 선언 타입은 그 객체를 가르키도록 지시한다.
A b = new B( )와 같이 객체가 형성되면 참조 변수 b는 B객체 내의 A객체를 가르키게 되는것이다.
다시 한번 강조하자면,
이 개념을 이해하면 다형성과 객체 타입도 쉽게 이해가 된다.

위 예시에서는 클래스 B가 A를 상속받고 클래스 C,D 가 B를 상속받는 구조이다.
즉, A ← B ← C,D 와 같은 상속 관계를 가진다.
17,18,19 행에서 자식 클래스의 생성자로 객체를 생성했으니 부모 클래스의 객체가 먼저 생성 되고 외부에 자식 클래스가 추가되며
자식 클래스 객체 내부에 부모 클래스 객체가 포함된 구조이다.
따라서 참조 변수가 부모 클래스의 객체를 가르킬 수 있는 것이다.
하지만 21행을 보면 부모 클래스의 생성자로 객체를 생성했으므로
자식 클래스의 객체는 생성되지 않고 오로지 부모 클래스의 객체만 생성된다.
근데 B타입으로 선언되어 있으므로 B객체를 가르켜야 하는데 B객체는 존재하지 않는다. 이것이 오류가 뜨는 이유다.
사람 ← 대학생의 관계로 살펴보자면
대학생은 사람이다.(O) // 항상 맞는 말이다. (업캐스팅)
사람은 대학생이다.(X) // 때에 따라 다르다. (다운캐스팅)
와 같이 업캐스팅은 항상 가능하지만 다운캐스팅은 때에 따라 다르다.

위의 예시에서는 A ← B ← C 와 같은 상속 관계를 가진다.
14행에서 C()생성자로 객체를 만들었고 C객체를 가르키도록 하고 있다.
당연히 C의 부모 클래스인 A객체와 B객체도 먼저 생성 됐으므로
B타입으로의 업캐스팅은 문제 없다.
15행의 (B)c1;에서 (B)를 생략해도 컴파일러가 자동으로 생성해준다.
17행에서는 B()생성자로 객체를 만들고 A객체를 가르키도록 하고 있다.
B()생성자로 객체를 만들었으므로 B타입으로 다운캐스팅도 문제없다.
하지만 업캐스팅과는 달리 18행의 (B)a1; 에서 (B)는 생략 할 수 없고
명시적으로 작성해줘야 한다.
20행에서는 A()생성자로 객체를 만들었고 A객체만이 생성 될 것이다.
B타입으로 다운캐스팅을 하려고 해도 B객체가 만들어져 있지 않으므로
다운 캐스팅이 불가능하다.
문법적으로 오류가 발생하지 않지만 예외가 발생한다.
메서드 오버라이딩이란 부모 클래스에게 상속받은 메서드와 동일한 이름의
메서드를 재정의 하는 개념이다.
메서드 오버라이딩을 할때는 다음 2가지 규칙을 지켜야 한다.
즉, 메서드의 리턴 타입부터 이름,입력 매개변수의 타입과 개수가 완벽하게 동일해야 하고 접근 지정자의 범위가 같거나 커야 한다.
메서드 오버라이딩은 덮어쓰기 개념이지만 부모 클래스의 메서드가 사라지는 것은 아니다.

다음과 같이 B클래스는 A클래스를 상속받고 abc( )메서드를 오버라이딩하여
메서드를 재정의 하였다.
18행은 A( ) 생성자로 객체를 만들었으므로 A객체만 생성이 되어
오버라이딩 되지 않아 a1.abc( )를 실행하면 class A가 출력된다.
하지만 19행과 20행에서는 B( ) 생성자로 객체를 생성했으므로
B객체가 만들어진다. B객체가 만들어지는 순간 메서드가 오버라이딩 되어
class B가 출력이 된다.
이 포스트를 참고해보면
this키워드는 자신의 객체를 의미하고
this( ) 메서드는 자신의 생성자를 의미한다고 했다.
이와 비슷하게
super키워드는 부모의 객체를 의미하고
super( )메서드는 부모의 생성자를 의미한다.
this키워드는 생략하면 컴파일러가 자동으로 추가해주지만 super키워드는
개발자가 직접 명시적으로 작성해줘야 한다.
super( )메서드는 this( )메서드와 마찬가지로
생성자의 내부에서만 사용이 가능하며 반드시 첫 줄에 와야한다.
두개의 메서드 모두 생성자 첫줄에 위치해야 하므로 같이 쓰는 것은 불가하다.
또한 모든 생성자의 첫줄에는 반드시 this( ) 또는 super( )가 위치해야 한다.
만일 아무것도 작성하지 않는다면 컴파일러는 자동으로 super( )를 삽입한다.


위의 예시에서는 클래스 A의 생성자에 첫줄에는 아무것도 작성되어 있지 않아
컴파일러가 super( )를 삽입해줘야 하지만 클래스 A는 아무것도 상속받지 않았다.
하지만 오류가 뜨지 않는데 이는 바로 최상위 클래스인 Object클래스 때문이다.
Object 클래스는 자바의 최상위 클래스로 자바의 모든 클래스는 Object를 상속받는다. 클래스를 생성할 때 아무런 클래스도 상속받지 않았다면 컴파일러가
자동으로 extends Object를 삽입해주어 Object 클래스를 상속받는다.
모든 클래스는 Object 클래스를 상속받으므로 Object 클래스의 메서드를 사용하고 일부 메서드는 오버라이딩하여 사용할 수 있다.
toString( )
객체 정보를 문자열로 리턴하는 메서드객체 정보는 '패키지명.클래스명@해시코드'로 나타난다.
직관적인 정보를 얻기위해 Override하여 사용한다.
println( ) 메서드는 객체를 출력하면 자동으로 객체 내의 toString( ) 메서드를 호출한다.

클래스 A 에서는 toString( ) 메서드를 오버라이딩 하지 않았고
클래스 B 에서는 오버라이딩 하였다.
26행은 객체의 해쉬코드를 16진수로 출력하는 코드이며
27행은 A객체의 정보를 제공한다.
B객체의 toString( ) 메서드는 직관적인 정보(필드값)을 제공하도록 오버라이딩 하였으므로 출력값이 다음과 같다.
equals(Object obj)
입력매개변수로 전달받은 객체와 자신의 객체의 스택 메모리 변수값을
비교하여 boolean 값으로 리턴하는 메서드실제 데이터 값이 아닌 실제 데이터의 주솟값을 비교한다.
등가연산(==)과 동일한 기능을 수행하는 메서드
Override를 통해 실제 데이터 값을 비교할 수 있다.

위의 예시에서는 A 클래스에서 equals( ) 메서드를 오버라이딩 하지않았다.
14,15행에서 완벽하게 동일한 생성자로 두개의 A객체를 생성했다.
이들의 실제 객체의 주소가 다르므로 객체 내부의 값이 동일해도
17,18행의 결과는 false가 출력된다.

위의 예시에서는 A 클래스에서 equals( ) 메서드를 오버라이딩 하였다.
12행의 instanceof키워드는 캐스팅 가능 여부를 확인한다.
캐스팅이 가능하면 true, 불가능하면 false이다.
메서드를 Override하여 이제 실제 데이터값을 비교할 수 있게 되었다.
그 결과 26행의 등가연산에서는 false가 출력되며
27행의 equals( ) 메서드 연산 결과 true가 출력된다.
hashCode( )
인스턴스가 저장된 가상머신의 주솟값을 10진수로 반환실제 메모리의 주솟값은 아님
hashCode( ) 가 동일하고 equals( ) 결과가 true이면 같은 객체로 인식한다.
이 둘중 하나라도 다르다면 서로 다른 객체이다.서로 다른 메모리에 위치한 두개의 객체가 동일성을 갖도록 하기 위해
hashCode( )와 equals( )를 Override한다.hashCode( ) 를 Override하여 값이 같아지게 하더라도
실제 hashCode가 같아지는 것은 아니다.

위는 hashCode( ) 메서드는 Override 하지 않고 equals( )메서드만 Override 했다.

실행 결과는 다음과 같다.
hashCode( ) 는 서로 다른 값이 출력이 되는데 hashCode( ) 도 Override하여
동일성을 구현해보도록 한다.

hashCode( ) 메서드를 호출하면 학생번호가 반환되도록 Override 하였다.
실행결과는 다음과 같다.

메서드 디스패치란 어떤 메서드를 호출할지 결정하여 실제로 실행시키는 과정이다.
자바는 컴파일 시에 생성할 객체 타입에 대한 정보만 보유하며 런타임 시 객체를 호출한다.
런타임 시 컴파일러는 해당 참조변수가 어느 클래스의 타입을 참조하는지에 대해
2가지 과정이 있다.
컴파일 시점에 컴파일러가 어느 메서드를 호출하는지 명확히 알고있는 경우이다.

컴파일러가 컴파일 시점에 어떤 메소드를 호출하는지 모르는 경우이다.
이때 런타임 시점에서 컴파일러가 알게 된다.

final 제어자는 필드,지역 변수,메서드,클래스 앞에 위치할 수 있는 제어자로
어디에 위치하느냐에 따라 의미가 다르다.
final 변수는 한번 대입된 값은 절대 수정할 수 없다.
즉, 한번 대입된 값이 최종값(final)이다.
final 필드는 값을 대입하지 않았을 때 강제 초기화가 이루어지지 않는다.
따라서 반드시 직접 초기화를 시켜줘야한다.
초기화 이후에는 값 대입 자체가 불가능하다.

위의 예시에서 8행과 같이 final 필드를 선언하고 생성자에서 값을 대입 할 수도
있지만 final 필드는 강제로 초기화가 안되므로 선언 시에 초기화를 시켜주지 않는다면 생성자에서 반드시 초기화를 시켜줘야 한다.
final 변수는 한번 대입된 값이 최종값이라고 하였다.
final 메서드와 final 클래스도 이와 유사하다.
final 메서드는 기능을 변경할 수 없는 메서드다.
즉, final 메서드로 정의를 한번 하고나면 자식 클래스에서 해당 메서드를
오버라이딩할 수 없다.
final 클래스는 상속 자체가 불가능한 클래스이다.
'추상적인' 이라는 의미를 가지고 있는 abstract 가 메서드나 클래스에 붙게 된다면
추상 메서드와 추상 클래스가 된다.
abstract의 의미를 생각해보며 추상 메서드를 살펴보면
추상 메서드는 미완성 메서드이며 아무런 기능을 구현하지 않은 메서드이다.
추상 메서드는 중괄호가 없는 메서드이며 다음과 같이 정의한다.
abstract 리턴 타입 메서드명( );
클래스가 이런 추상 메서드를 하나라도 포함하고 있다면 해당 클래스는
반드시 추상 클래스로 정의해야 한다.
추상클래스는 다음과 같이 정의한다.
abstract class 클래스명 {
}
추상 클래스로는 직접적으로 객체를 생성할 수 없지만
다음 2가지 방법으로 추상 클래스의 객체를 만들 수 있다.
- 추상 클래스를 일반 클래스로 상속해 객체 생성하기
- 익명 이너 클래스 사용하기

다음과 같이 자식 클래스의 생성자로 객체를 생성한 후
참조 변수를 추상 클래스의 타입으로 선언한다.
주로 여러개의 객체를 생성할 때 사용한다.

이는 컴파일러가 클래스 A를 직접 상속받아 print( ) 메서드를 오버라이딩한
익명 클래스의 생성자를 호출하는 것을 의미한다.
주로 한개의 객체만 생성할 때 사용한다.
Do it! 자바 완전 정복