본문 바로가기
java-liveStudy

6주차 과제. 상속

by 에드박 2020. 12. 25.

목표

자바의 상속에 대해 학습하세요.


학습할 것 (필수)

  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

클래스간의 관계

상속 관계 : ~은 ~이다 (is a)

  • 자식 클래스에 예약어 extends 로 부모클래스를 상속받는것을 말합니다.

ex) 개는 동물이다, 연필은 도구이다, 햄버거는 음식이다.

 

 

포함 관계 : ~은 ~을 가지고 있다.

  • 한 클래스의 멤버변수로 다른 클래스타입의 참조 변수를 선언하는것을 말합니다.

ex) 샤프는 샤프심을 가지고있다. 에어팟은 배터리를 가지고있다.


자바 상속(Inheritance)

상속이란 자식이 부모에게 무언가 물려받는다는 의미입니다.

 

Oracle Java Document의 상속에 보면 이렇게 나와있습니다.

 

다른 클래스로 부터 상속받는 클래스를 서브클래스(subclass) 라고 부릅니다.

(또한 derived class, extended class, child class 라고도 부릅니다)

 

서브 클래스에게 상속하는 클래스를 슈퍼클래스(superclass) 라고 부릅니다.

(또한 base class, parent class 라고도 부릅니다)

 

제 글에서는 상속이라는 말에 이해하기 쉽게 서브 클래스를 자식 클래스 / 슈퍼 클래스를 부모 클래스라고 부르겠습니다.

 

자바에서 상속이란 자식 클래스가 부모 클래스의 변수, 메소드를 물려받는것입니다.

즉, 자식 클래스는 부모 클래스의 변수와 메소드를 사용할 수 있습니다.

 

상속을 구현할 때 extends 예약어를 사용합니다.

예를들어 Animal 이라는 부모 클래스와 Dog, Cat 자식 클래스는 다음과 같습니다.

 

실행 결과 : 

 


자바 상속의 특징

 

1. 자바에서는 다중 상속을 지원하지 않는다.(부모가 여럿이 될 수 없습니다.)


2. 자바에서는 상속의 횟수에 제한을 두지 않는다.(부모는 여러 자식을 가질 수 있습니다.)


3. 최상위 클래스 Object 가 존재한다.(Object 만이 유일하게 부모 클래스를 가지고있지 않다)

자바의 모든 클래스에는 하나의 직접적인 부모 클래스가 있습니다.

만약 extends 예약어를 따로 사용하지 않았다면 암시적으로 Object를 상속하는 자식클래스가 됩니다.

 

https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html

 

Object 클래스는 JDK 1.0 부터 있었으며 부모클래스가 없음을 확인할 수 있습니다.


자식 클래스로 할 수 있는 것

  • 상속 된 필드는 다른 필드와 마찬가지로 직접 사용할 수 있습니다.
  • 부모 클래스에 없는 자식 클래스의 새 필드를 선언할 수 있습니다.
  • 부모 클래스의 필드와 동일한 이름으로 자식 클래스의 필드를 선언하여 숨길 수 있습니다.(권장하지 않음)
  • 상속 된 메서드는 그대로 직접 사용할 수 있습니다.
  • 부모 클래스에 있는 것과 동일한 이름을 가진 메서드를 자식 클래스에 작성하여 재정의(오버라이딩) 할 수 있습니다
  • 부모 클래스에 있는 정적 메서드(static method)와 동일한 이름으로 자식 클래스에 작성하여 부모 클래스의 정적 메서드를 숨길 수 있습니다.
  • 부모 클래스에 없는 새 메소드를 자식 클래스에 선언할 수 있습니다 (확장)
  • super 키워드를 사용해서 부모 클래스의 생성자를 호출하는 자식 클래스의 생성자를 작성할 수 있습니다.

 

1. 상속 된 필드는 다른 필드와 마찬가지로 직접 사용할 수 있습니다.

 

2. 부모 클래스에 없는 자식 클래스의 새 필드를 선언할 수 있습니다.

 

3. 부모 클래스의 필드와 동일한 이름으로 자식 클래스의 필드를 선언하여 숨길 수 있습니다.(권장하지 않음)

 

4. 상속 된 메서드는 그대로 직접 사용할 수 있습니다.

 

5. 부모 클래스에 있는 것과 동일한 이름을 가진 메서드를 자식 클래스에 작성하여 재정의(오버라이딩) 할 수 있습니다.

실행 결과 : 

 

6. 부모 클래스에 있는 정적 메서드(static method)와 동일한 이름으로 자식 클래스에 작성하여 부모 클래스의 정적 메서드를 숨길 수 있습니다.

 

7. 부모 클래스에 없는 새 메소드를 자식 클래스에 선언할 수 있습니다 (확장)

 

8. super 키워드를 사용해서 부모 클래스의 생성자를 호출하는 자식 클래스의 생성자를 작성할 수 있습니다.

 


super 키워드

 

super 키워드는 부모 클래스의 필드나 메소드를 자식 클래스에서 참조하는데 사용하는 참조 변수 입니다.

또한 부모 클래스의 멤버와 자식클래스의 멤버 이름이 같을 경우 super 키워드를 사용하여 구별할 수 있습니다.

 

만약 부모에만 10으로 초기화한 int 타입 변수 a를 선언하고 자식 클래스의 메서드에서 a, this.a, super.a 3가지를 호출하면 어떻게 출력될까?

 

실행 결과 : 

결과는 모두 10이 됩니다. 자식 클래스에 변수 a를 선언하지 않았다면 a, this.a, super.a 3가지 모두 부모의 변수를 호출합니다.

 

그렇다면 자식 클래스에 int타입 변수 a를 선언하고 20으로 초기화 하면 결과는 어떻게 나올지 보겠습니다.

 

 

실행 결과 : 

a, this.a 두개는 자신의 클래스에 선언한 변수 a를 가져오고 있습니다. super.a는 부모의 변수 a를 값으로 가져왔습니다.

 

super() 는 부모클래스의 생성자를 호출할 때 사용됩니다.
super(매개변수) 를 사용해서 부모클래스의 매개변수가 있는 생성자 또한 호출이 가능합니다.

 

주의할 점 자식 클래스의 생성자를 정의할 때는 부모클래스의 기본 생성자인 super() 가 암묵적으로 자식 클래스의 생성자에 정의 되어 있습니다.

그렇다면 부모 클래스에 기본 생성자가 없다면 어떻게 될까요?

실행 결과 : 

컴파일 에러가 발생합니다.

Parent에 매개변수가 있는 생성자를 선언했기 때문에 부모 클래스에는 기본생성자가 없는 상태입니다. 따라서 자식 클래스에서 생성자를 만들 때 부모 클래스의 기본 생성자인 super() 가 없기 때문에 컴파일 에러가 발생하는 것입니다.

 

클래스에 다른 생성자를 선언할 때는 기본 생성자도 함께 생성해주는것이 좋습니다.


메소드 오버라이딩

메소드 오버라이딩은 부모 클래스의 메소드 이름과 같은 메소드를 자식 클래스의 시그니쳐를 갖는 메소드로 다시 정의하는것을 말합니다.

 

출력결과 : 

 

오버라이딩의 조건

  • 메소드의 동작만을 재정의하는 것이므로, 메소드의 선언부는 기존 메소드와 완전히 같아야합니다.
    (메소드의 반환 타입은 부모 클래스의 반환타입으로 타입 변환할 수 있는 타입이라면 변경할 수 있습니다.)
  • 부모 클래스의 메소드보다 접근 제어자를 더 좁은 범위로 변경할 수 없습니다.
  • 부모 클래스의 메소드보다 더 큰 범위의 예외를 선언할 수 없습니다.

다이나믹 메소드 디스패치(Dynamic Method Dispatch)

 

Method Dispatch

어떤 메소드를 호출할지 결정하여 실제로 실행시키는 과정을 의미합니다.

 

Static Method Dispatch

컴파일 시점에서, 컴파일러가 특정 메소드를 호출할 것이라는걸 명확하게 알고있는 경우

런타임 전에 미리 어떤 메소드를 호출할지 미리 결정하는 개념

오버로딩된 메소드를 사용하는 경우도 매개변수, 리턴타입 등에 따라 어떤 메소드가 호출될 지 명확하기 때문에 미리 알 수 있습니다.

Sub sub = new Sub(); 도 변수의 타입과 초기화하는 인스턴스의 타입이 일치하기 때문에 어떤 메소드를 사용할 지 미리 알 수 있습니다.

 

Dynamic Method Dispatch(Runtime Polymophism in Java)

메소드 오버라이딩은 Java 가 런타임 다형성을 지원하는 방법중 하나입니다.

  • Dynamic Method Dispatch는 컴파일 타임이 아닌 런타임에 오버라이딩된 메소드에 대한 호출이 결정되는 메커니즘입니다.
  • 오버라이드된 메소드가 부모클래스 참조를 통해 호출되면 java는 호출이 발생할 때 참조되는 객체의 유형에 따라 어떤 메소드가 실행될 것인지 결정합니다. (부모 클래스의 메소드 / 자식 클래스의 오버라이딩된 메소드)
  • 런타임시 실행될 오버라이딩 메소드 버전을 결정하는것은 참조되는 객체의 타입(참조 변수 타입이 아님)에 따라 다릅니다.
  • 부모 클래스 참조 변수는 자식 클래스 객체를 참조할 수 있습니다. 이를 Up-Casting 이라고도 합니다. Java는 이 사실을 사용하여 런타임에 오버라이딩 된 메소드에 대한 호출을 해결합니다

따라서 부모 클래스에 자식 클래스에 의해 오버라이딩된 메소드가 포함된 경우 다른 유형의 객체가 부모 클래스 참조 변수를 통해 참조 될 때 다른 버전의 메소드가 실행됩니다.

 

아래의 예제는 Dynamic Method Dispatch 를 설명하고 있습니다.

A 라는 부모 클래스와 A를 상속받는 B와 C 두개의 자식클래스가 있습니다.

출력 결과 : 

 

A a = new A();
B b = new B();
C c = new C();

ref = a;
ref.m1();

A타입의 ref 변수를 A타입의 객체로 초기화 시키고 m1() 메소드를 실행합니다.
해당 m1() 메소드는 A클래스에 있는 m1() 메소드를 사용합니다.

ref = b;
ref.m1();

A타입의 ref 변수를 B타입의 객체로 초기화 시키고 m1() 메소드를 실행합니다.
해당 m1() 메소드는 B 클래스에서 오버라이딩 된 m1() 메소드를 사용합니다.

 

ref = c;
ref.m1();

A타입의 ref 변수를 C타입의 객체로 초기화 시키고 m1() 메소드를 실행합니다.
해당 m1() 메소드는 C클래스에 오버라이딩 된 m1() 메소드를 사용합니다.



Java 에서는 변수(데이터 멤버) 가 아닌 메서드만 재정의 할 수 있으므로 변수는 런타임 다형성을 달성할 수 없습니다. 즉, 변수는 재정의 되지 않으므로 부모의 타입 변수에 자식타입의 객체를 참조했을 때는 항상 부모 클래스의 변수를 참조합니다.

실행 결과 : 


Dynamic Method Dispatch의 장점
- Dynamic Method Dispatch는 Java가 런타임 다형성의 핵심인 메소드 대체를 지원할 수 있도록 합니다.
- 이를 통해 클래스는 모든 파생물에 공통되는 메서드를 지정할 수 있으며, 하위 클래스는 이러한 메서드의 일부 또는 전체에 대한 특정 구현을 정의할 수 있습니다.
- 또한 자식 클래스에 특정 메소드를 추가하여 일부의 특정 구현을 정의할 수 있습니다.

Static Binding vs Dynamic Binding
- Static Binding은 컴파일 타임에 수행되고 Dynamic Binding은 런타임 동안 수행됩니다.
- private, final 및 static 메소드 및 변수는 Static Binding을 사용하고 컴파일러에 의해 결합되는 반면 오버라이딩 된 메서드는 런타임 개체 유형에 따라 런타임 중에 결합됩니다.

 

 


추상 클래스

추상클래스란 말그대로 추상적인 클래스로 미완성 된 클래스라고 볼 수 있습니다.

하나 이상의 추상 메소드를 포함하는 클래스를 말합니다.

(미완성된 클래스이므로 인스턴스를 생성할 수 없습니다.)

 

추상 메소드란 자식 클래스에서 반드시 오버라이딩 해야하는 메소드를 의미합니다.

 

추상 클래스를 사용하는 목적은 반드시 사용되어야 하는 메소드를 추상 클래스에 추상 메소드로 선언해 놓으면,

이 클래스를 상속받는 모든 클래스에서 이 추상 메소드를 재정의 해야하기 때문에 반드시 구현하도록 강요할 수 있습니다.

 

추상 클래스 자체로는 클래스 로서의 역할을 다 못하지만, 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 가집니다.

 

추상 클래스는 class 구현부 앞에 'abstract' 키워드를 붙이기만 하면 됩니다.

추상 메소드는 메소드 앞에 'abstract' 키워드를 붙여 주고, 구현부가 없으므로 괄호 '{ }' 대신 문장의 끝을 알리는 ';' 를 적어줍니다.

 

 

 


final 키워드

 

자바에서 final 키워드는 상수를 표현하기 위한 예약어 입니다.

 

final 키워드를 사용할 수 있는곳은 세가지가 있습니다.

  • 메소드
  • 클래스
  • 변수

메소드에 사용된 final 키워드는 오버라이딩이 불가능 하도록 만듭니다. 따라서 해당 메소드는 상속받은 그대로 사용해야 합니다.

final로 선언한 helloJava() 메소드를 상속받은 Two 클래스에서 오버라이딩 했을 때 컴파일 타임에 에러가 발생합니다.

 

클래스에 사용된 final 키워드는 상속이 불가능 하도록 만듭니다. 

final 클래스인 One을 Two 클래스에서 상속하면 컴파일 타임에 에러가 발생합니다.

 

변수에 사용된 final 키워드는 변수의 값을 변경할 수 없도록 합니다.

  • 일반적으로 final이 붙은 변수는 선언과 초기화를 동시에 하지만, 인스턴스 변수의 경우 생성자에서 초기화 되도록 할 수 있습니다.

final 변수의 값을 변경하려고 하면 컴파일 단계에서 에러가 발생합니다.


Object 클래스

 

Object 클래스는 모든 클래스의 조상으로 상속 계층도의 최상위에 있는 조상 클래스입니다.

상속 받지않는 클래스는 자동적으로 Object 클래스를 상속 받습니다.

 

https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html

Object 클래스에서 11개의 메서드가 정의 되어 있습니다. (바로 위의 사진에 나와있는 메소드들 입니다.)

Object 클래스의 메서드 설 명
protected Object clone() 객체 자신의 복사본을 반환합니다.
public boolean equals() 객체 자신과 객체 obj가 같은 객체인지 알려줍니다.(같으면 true)
protected void finalize() 객체가 소멸될 때 가비지 컬렉터에 의해 자동적으로 호출됩니다. 이 때 수행되어야 하는 코드가 있을 때 오버라이딩합니다.(사용하지 않는것이 좋음)
public Class getClass() 객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환합니다.
public int hashCode() 객체 자신의 해시코드를 반환합니다.
public String toString() 객체 자신의 정보를 문자열로 반환합니다.
public void notify() 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨웁니다.
public void notifyAll() 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨웁니다.
public void wait()
public void wait(long timeout)
public void wait(long timeout, int nanos)
다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게 합니다.(timeout은 1/1000초, nanos는 1/1000000000c초)

 

finalize() 메서드는 문제점이 많아서 전문가들도 사용을 만류하는 메소드 입니다.

Java9 부터는 Deprecated 된 메서드입니다.

 

대표적인 문제점으로는 다음 4가지가 있습니다.

  • 신속하게 실행된다는 보장이 없음
  • 반드시 실행된다는 보장이 없음
  • 예외가 발생하면 무시됨
  • 성능 저하가 발생

 

참고 문헌

 

'java-liveStudy' 카테고리의 다른 글

8주차. 인터페이스  (0) 2021.01.09
7주차. 패키지  (0) 2021.01.01
5주차. 과제  (0) 2020.12.17
5주차. 클래스  (0) 2020.12.16
4주차 과제. 제어문 + 과제  (0) 2020.12.04

댓글