[자바 스터디] 6주차 과제: 상속

yunu·2022년 4월 15일
0
post-thumbnail

6주차 과제: 상속

목표

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

학습할 것

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

자바 상속의 특징

상속

  • 상위클래스에서 정의한 필드와 메서드를 하위클래스도 동일하게 사용할 수 있게 물려받는 것
  • 상속 관계(일반화/특수화)를 결정하는 것은 객체의 상태를 표현하는 데이터가 아니라 행동
  • 생성자와 초기화 블럭은 상속되지 않음
  • 자식 클래스가 부모 클래스를 물려받고 그 이외 더 많은 행동을 하니까 기능의 관점에서 보면 벤다이어그램을 위의 그림과 같이 표현할 수 있음
  • 하지만 상속 관계의 일반화와 특수화 관점에서 보면 부모 클래스가 더 일반적이고 자식 클래스가 특수한 경우이기 때문에 일반적인 부모 클래스에 좀 더 특수한 경우인 자식 클래스가 포함되어 있게 표현하는게 더 상속 관계를 이해하는데 도움이 될 것 같음

"A는 B다" 와 "A에는 B가 있다" 관계

  • A는 B다 테스트를 통해 어떤 것이 다른 것을 확장하는지 확인하여 상속관계 확인
  • 만약 A에는 B가 있다 라고 표현해야 된다면 A 객체에 B 인스턴스 변수가 들어간다고 할 수 있음

자바 상속의 특징

  • 다단계 상속이 가능하고, 다중 상속을 지원하지 않음
  • 모든 클래스는 Object 클래스의 자식 클래스

다중 상속을 지원하지 않는 이유 : 다이아몬드 문제

  • Afunc 메서드를 B, C에서 func 메서드를 오버라이딩을 했을 때 DB, C 중 어떤 func 메서드를 상속받아야 할지 모르기 때문에 컴파일 에러가 발생

super 키워드

  • 자식 클래스에서 부모 클래스를 가리키는 키워드
  • super 키워드를 통해 자식 클래스는 부모 클래스의 필드나 메서드를 호출
  • 부모 클래스에 디폴트 생성자 이외 생성자가 있다면 자식 클래스에서 super(param...)을 호출해서 부모 클래스의 생성자로 부모의 필드를 초기화 해주어야 함, 부모 클래스에 디폴트 생성자만 있을 경우는 명시적으로 super()를 해주지 않더라도 컴파일러가 자동으로 호출해줌
  • thissuper는 처음 탐색 위치말고 다른게 없는 것 같음
public Constructor() {
	super(); // 호출해주지 않더라도 컴파일러가 자동으로 해줌
    ...
}

public Constructor(params) {
	super(params); // 디폴트 생성자 이외의 생성자는 꼭 호출해주어야 됨
    ...
}

메소드 오버라이딩

  • 메소드의 행동을 재정의 해주는 것
  • 메소드 오버라이딩의 조건은 메소드명, 매개변수 그리고 반환타입이 같아야 함, 자바에서는 공변 반환 타입 가능하여 메소드가 오버라이딩 될 때 더 좁은 타입을 교체 가능
  • 인스턴스 변수는 오버라이드할 필요 없음 -> 인스턴스 변수에서 특별한 행동을 정의하는 것이 아니기 때문
class Parent {
	public void func1() {}
    protected Parent func2() {}
}

class Child extends Parent {
	@Override
    public void func1() {}
    
    /* 반환 타입이 바뀌었지만 더 좁은 타입으로 바뀌었으므로 OK
     * 접근 제어자도 바뀌었지만 더 넓은 범위로 바뀌었으므로 OK
     */
    @Override
    public Child func2() {}
}

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

  • 어떤 메소드를 호출할 지 런타임에 결정하는 것
  • 런타임 시점에 할당된 객체의 타비을 보고 메소드를 실행함
  • 같은 타입의 객체로 여러 타입의 객체를 초기화할 수 있어 한 타입으로 여러 타입의 기능을 실행 가능 (다형성)
class Parent {
	public void func() {
    	System.out.println("parent class");
    }
}

class Child extends Parent {
	@Override
    public void func() {
    	System.out.println("child class");
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
        Parent parent = new Child();
        
        child.func(); // 정적 디스패치
        parent.func(); // 동적 디스패치
    }
}

/*
output :
child class
child class

모두 같은 메소드가 실행
*/

추상 클래스

  • 추상 클래스는 인스턴스로 생성 X, 추상 클래스를 구현하려면 상속을 받은 클래스를 통해 인스턴스로 생성해야 됨
  • 추상 메서드를 포함하고 있다는 것을 제외하고 일반 클래스와 동일함, 생성자, 멤버변수 그리고 메서드 모두 가질 수 있음
  • 추상 메서드를 포함하고 있지 않더라도 추상클래스를 직접 인스턴스로 생성하지 못하게 하는 용도로 사용할 수 있을 것 같음
abstract class 클래스명 {
	...
    (접근제어자) abstract void abstractMethod();
}

final 키워드

  • 다시 무언가를 정의내리는 것을 막는 키워드
  • 변수에 final을 사용하면 불변하는 것이 맞지만 메모리를 새롭게 할당하는 것을 막는다는 것을 의미
  • final이 붙은 변수는 일반적으로 선언과 동시에 초기화를 동시에 하지만 생성자에서 초기화 가능, 그러므로 생성자 이전 초기화하는 과정들에서도 모두 final 변수를 초기화할 수 있음
final class 클래스명 {	// 부모가 될 수 없는 클래스
	final 타입 인스턴스변수 =;	// 값을 변경할 수 없는 멤버변수(상수)
    
    final 타입 메서드명() {	// 오버라이딩할 수 없는 메서드(변경불가)
    	final 타입 지역변수 =;	// 값을 변경할 수 없는 지역변수(상수)
    }
  • 프리미티브 타입의 값이 곧 데이터이기 때문에 final로 정의내리면 값을 바꿀 수 없음
  • 레퍼런스 타입의 값은 실제 사용하는 데이터가 아닌 사용하는 데이터(인스턴스)의 주소값이기 때문에 final로 정의내리더라도 주소값을 바꾸지 않는 이상 데이터를 바꿀 수 있음

Object 클래스

  • 모든 클래스의 최고 조상 클래스이므로 모든 클래스를 Object 타입으로 초기화 할 수 있음
  • 컴파일러에서 자동으로 extends Object 를 추가해줌
class Object {
    String toString() {...}; // 문자열로 반환
    boolean equals(Object obj) {...}; // 같은 지 여부 반환
  	protected Object clone() {...}; // 인스턴스를 복제하여 새로운 인스턴스를 생성해 반환
    protected void finalize() {...}; // 가비지컬렉터(GC)가 객체 리소스를 삭제
    Class<T> getClass() {...}; // 객체의 클래스 타입 반환
    int hashCode() {...}; // 객체의 해시 코드값 반환
    void notify() {...}; // 객체의 대기중인 하나의 스레드를 다시 실행할 때 호출
    void notifyAll() {...}; // 객체의 대기중인 모든 스레드를 다시 실행할 때 호출
    void wait() {...}; // 객체의 다른 스레드가 notify() 또는 notifyAll() 메소드 실행할 때까지 현재 스레드를 일시적으로 대기시킴
    void wait(long timeout) {...}; // 객체의 다른 스레드가 notify() 또는 notifyAll() 메소드 실행하거나 timeout시간이 지날 때까지 현재 스레드를 일시적으로 대기시킴
    void wait(long timeout, int nanos) {...}; // 객체의 다른 스레드가 notify() 또는 notifyAll() 메소드 실행하거나 timeout시간이 지나거나 다른 스레드가 현재 스레드를 인터럽트할 때까지 현재 스레드를 일시적으로 대기시킴
}

출처:
객체지향의 사실과 오해
https://leemoono.tistory.com/20
https://yadon079.github.io/2020/java%20study%20halle/week-06

profile
rip

0개의 댓글