ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스터디 6주차] java 상속
    프로그래밍 언어/JAVA 2021. 6. 18. 14:55
    더보기

    목표: 자바의 상속에 대해 학습

     

    1. 자바 상속의 특징

    실생활에서 사용하는 상속이라는 뜻은 자식이 부모에게 재산 및 신분상의 지위를 물려받는 것을 말한다.

    자바에서도 비슷한 뜻을 가진다.

    자식 클래스가 부모 클래스가 가지고 있는 필드나 메서드를 그대로 물려받을 수 있다.

    부모 클래스상위 클래스라고 부르기도 하고 자식 클래스하위 클래스, 파생 클래스라고 부르기도 한다.

     

    이렇게 상속이라는 기능을 사용하게 되면 코드의 중복을 줄일 수 있다.

    이미 구현된 클래스의 기능을 다른 클래스에서 사용하고 싶다면 다시 똑같은 코드를 구현하는 것이 아니라 상속을 통해 코드 구현없이 기능을 사용할 수 잇다.

    예제 코드를 보며 알아보자.

    1. classExample1이라는 부모 클래스
    2. classExample2라는 자식 클래스

    코드를 보면 classExample1이라는 클래스를 만들어 field1과 method1을 생성했다.

    그리고 classExample2를 만들어 extends키워드를 사용해 classExample1을 상속받았다.

    그 후, 새로운 classExample2 객체인 a를 선언하여 접근해본 결과(맨 마지막 사진) classExample2에 선언한 field2, method2이외에 상속받은 field1, method1도 가지고 있는 것을 볼 수 있다.

     

    위 코드에서 봤듯이 상속을 받기 위해서는 extends 키워드를 사용하면 된다.

    class 자식 클래스명 extends 부모 클래스명 {
    
    
    }

    여기서 알아야할 중요한 점은 자바에서는 다중 속성을 허용하지 않아 extends 뒤에는 단 하나의 부모 클래스만 와야 한다.

     

    이렇게 손쉽게 부모 클래스로부터 상속을 받아 사용할 수 있지만 모든 필드와 메소드들을 받을 수 있는 것은 아니다.

    부모 클래스에서 private 접근 제한을 갖는 필드와 메소드는 상속 받을 수 없고

    부모 클래스와 자식 클래스가 서로 다른 패키지에 존재한다면, default 접근 제한을 갖는 필드와 메소드도 상속 받을 수 없다.

     

     

     

    2. super 키워드

    super 키워드는 무엇일까?

    super 키워드가 무엇인지 알기 전에 왜 필요한지부터 알아보자.

    우리가 부모 클래스를 만들고 부모 클래스를 상속 받은 자식 클래스를 만들고 난 후, 자식 객체를 생성하면 메모리에는 부모 객체가 먼저 생성되고 자식 객체가 그 다음에 생성된다.

    클래스에서 배웠지만 객체를 생성할 때에는 클래스의 생성자를 호출해야만 한다.

    하지만, 자식 클래스의 코드를 보면(위 2번 사진 참고) 어디에도 부모 클래스의 생성자에 대한 코드는 없다.

    이를 위해 super 키워드를 사용하는 것이다.

    super 키워드는 부모 객체를 생성하기 위해 부모 생성자를 호출하는 용도로 사용된다.

    우리는 상속을 받고자 할 때 자식 클래스 안에 super()를 직접적으로 선언하지 않았지만 컴퍼일러에 의해 생성된다.

    super( )는 부모의 기본 생성자를 호출하는데 직접 명시적으로 부모 생성자를 호출하고 싶다면 아래와 같이 코드를 작성하면 된다.

    자식 클래스명( 매개변수1, 매개변수2, ...) {
        super(매개값, ...);
    }

    super(매개값, ...)은 매개값의 타입과 일치하는 부모 생성자를 호출한다.

    만약 부모 클래스에 기본 생성자가 없고 매개 변수가 있는 생성자만 있다면 자식 생성자에서 반드시 부모 생성자 호출을 위해 super(매개값,...)을 써주어야 한다.

    super(매개값, ...)은 반드시 자식 생성자 첫 줄에 위치해야한다는 것이 원칙이다!!

     

    이외에도 super 키워드는 다른 역할을 할 수 있다.

    아래에서 언급하는 오버라이딩을 사용하게 되면 상속받은 부모 클래스의 메소드는 숨겨지고 오버라이딩된 자식 메소드만 호출된다.

    하지만, 자식 클래스 내부에서 오버라이딩되어 바뀐 메소드가 아닌 원래의 부모 클래스의 메소드를 호출하고 싶다면 super 키워드를 붙여서 부모 메소드를 호출 할 수 있다.

    super.부모 메서드명( );

    super 키워드는 부모 객체를 참조하고 있기 때문에 부모 메소드에 직접 접근이 가능하다.

     

     

     

    3. 메소드 오버라이딩(Overriding)

    우리는 이전 주 차인 클래스에서 메소드와 생성자를 만들 때 오버로딩(Overloading)에 대해 배웠다.

    다시 오버라이딩과 오버로딩 단어가 비슷해 개념이 서로 헷갈릴 수 있어 오버로딩의 개념을 다시 얘기하며 시작하려 한다.

    오버로딩(Overloading)은 생성자에서는 매개 변수를 달리하는 생성자를 여러 개 선언하는 것을 말하고

    메소드에서는 클래스 내에 같은 이름의 메소드를 매개 변수의 타입, 개수, 순서를 달리하여 여러 개 선언하는 것을 말한다.

     

    그러면 오늘 알아 볼 오버라이딩(Overriding)은 무엇일까?

    오버라이딩(Overriding)은 부모로부터 상속받은 메소드가 구현하는 기능을 바꾸고 싶을 때 자식 클래스에서 동일한 메소드를 재정의하는 것을 말한다.

    오버라이딩을 사용하면 상속받은 부모 객체의 메소드는 숨겨지기때문에 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출된다.

     

    메소드를 오버라이딩할 때에는 아래와 같은 규칙이 존재한다.

    - 부모의 메소드와 동일한 시그니처(리턴 타입, 메소드 이름, 매개 변수 리스트)를 가져야 한다.

    - 접근 제한을 더 강하게 오버라이딩할 수 없다.

    (접근 제한의 강한 순서는 private, default, protected, public 순으로 예를 들어 상속받은 메소드의 접근 제한이 public이라면 오버라이딩 할 때에는 protected, default, private로 선언할 수 없다는 얘기다.)

    - 새로운 예외(exception)를 throws할 수 없다.

     

     

     

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

    메소드 디스패치(Method Dispath)는 어떠한 메소드를 호출할지 결정하는 것을 의미한다.

    오늘 알아볼 다이나믹 메소드 디스패치 이외에도 정적 메소드 디스패치. 더블 디스패치가 존재한다.

    그러면 정적 메소드 디스패치(Static Method Dispatch)부터 알아보자.

    정적 메소드 디스패치는 컴파일 시점에서 컴파일러가 특정 메소드를 호출할 것이라는 것을 명확하게 알고 있는 경우를 말한다. 즉, 런타임(실행 시점)이 되지 않아도 미리 어떠한 메소드를 호출할지 결정되는 것이다.

    정적 메소드 디스패치를 알 수 있는 예제는 오버라이딩이다.

    우리는 부모 클래스에서 메소드1을 선언하고 자식 클래스에서 메소드 1을 오버라이딩하여 실행문을 바꿀 수 있다.

    그 후, 자식 클래스 객체를 만들어 메소드1에 접근한다면 부모 클래스의 메소드1이 아닌 자식 클래스에서 오버라이딩한 메소드 1에 접근하는 것을 우리는 명확히 알고 있다.

    컴파일러 역시 이러한 메소드 디스패치를 알고 있고 이를 정적 메소드 디스패치라고 한다.

     

    다음으로 다이나믹 메소드 디스패치(Dynamic Method Dispatch)에 대해 알아보자.

    다이나믹 메소드 디스패치는 컴파일러나 어떤 메소드를 호출해야하는지 모르는 경우를 말한다.

    즉, 컴파일 타임에는 메서드의 클래스 타입이 정해져있지 않지만 런타임에 정해져서 메서드를 호출하는 것을 말한다.

     

    마지막으로 더블 디스패치(Double Dispatch)이다.

    더블 디스패치는 다이나믹 메소드 디스패치를 두번 하는 것을 말한다.

     

     

    5. 추상 클래스

    추상 클래스에서 추상(abstract)라는 말의 의미는 여러 사물 또는 개념 따위의 개별자들로 부터 공통점을 파악하고 추려내는 것이다.

    실생활에 추상이라는 단어를 사용할 경우를 떠올려보면 컴퓨터, 프린터, 전화기 등의 공통되는 특성을 얘기하면 기계라는 것이 있다. 여기서 기계는 구체적인 실체보다는 추상적인 것이라고 볼 수 있다.

    그러면 클래스에서 말하는 추상 클래스란 무엇일까?

    객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 한다면 이 클래스들의 공통적인 특성을 추출해서 선언한 클래스를 추상 클래스라고 한다.

    추상 클래스(부모)와 실체 클래스(자식)는 상속의 관계를 가지고 있다.

    실체 클래스는 추상 클래스의 모든 필드, 메소드를 물려받고 추가적인 필드와 메소드를 가질 수 있다.

    추상 클래스는 객체를 직접 생성해서 사용할 수 없다.

    (즉, new 연산자를 사용해서 인스턴스를 생성할 수 없다는 것이다.)

     

    그러면 굳이 추상 클래스를 만드는 이유는 무엇일까?

    첫번째 이유는 실체 클래스들의 공통된 필드와 메소드의 이름을 통일화하기 위해서이다.

    실체 클래스들마다 동일한 데이터를 담고 있는 필드나 동일한 기능을 하는 메소드이지만 이름이 다르다면 객체마다 사용 방법이 달라지게 된다.

    이를 보완하기 위해 추상 클래스에서 필드와 메소드를 선언해 실체 클래스들이 상속함으로써 필드와 메소드 이름을 통일화 시킬 수 있다.

    두번째 이유는 실체 클래스를 작성할 때 시간을 절약할 수 있다.

    공통적인 필드와 메소드는 추상 클래스에서 선언하고 실체 클래스들마다 다른 점만 실체 클래스에 선언하면 작성하는데 시간을 절약할 수 있다.

     

    그러면 추상 클래스는 어떻게 선언할까?

    추상 클래스를 선언하고자 할때에는 abstract 키워드를 붙여 선언하면 된다.

    new 연산자로 직접 생산자를 호출할 수 없지만 실체 클래스(자식 클래스) 객체가 생성될 때 super( )를 호출해서 추상 클래스(부모 클래스) 객체를 생성하므로 추상 클래스도 반드시 생성자가 있어야한다.

     

    추상 클래스이외에 추상 메소드도 존재한다.

    추상 메소드는 무엇일까?

    추상 메소드는 추상 클래스에서 선언되어지는 것으로 메소드의 선언부만 있고 메소드 실행 내용인 중괄호{ }가 없는 메소드를 말한다.

    [public | protected] abstract 리턴타입 메소드명(    ) ;

    이와 같은 형식으로 추상 클래스에서 선언한 추상 메소드는 추상 클래스를 상속한 자식 클래스가 반드시 추상 메소드를 오버라이딩해서 실행 내용을 작성해야한다.

    (추상 메소드를 오버라이딩하지 않는다면 컴파일 에러가 발생한다.)

    이러한 추상 메소드를 사용하는 경우는 메소드 이름만 통일화하고 실행 내용은 실체 클래스마다 달라야 하는 경우와 해당 메소드가 반드시 실행 내용을 채우도록 강요하고 싶을 경우에 사용한다.

     

     

     

    6. final 키워드

    final 키워드는 클래스, 필드, 메소드 선언 시에 사용되는 것으로 해당(클래스, 필드, 메소드) 선언이 최종상태이고 절대 수정될 수 없음을 뜻한다.

     

    먼저, 필드를 선언할 때 final을 사용한 것을 살펴보자.

    final 타입 필드 [= 초기값];

    final 필드는 초기값이 저장되면 프로그램 실행 도중에 수정할 수 없고 초기값은 주는 방법은 위의 코드처럼 필드 선언 시 주거나 생성자에서 외부 데이터를 받아 주는 것뿐이다.

    만약, 초기화되지 않은 final 필드를 그대로 남겨두면 컴파일 에러가 발생한다.

     

    다음으로 클래스에 final 키워드를 사용한 경우를 알아보자.

    클래스를 선언할 때 final 키워드를 사용하면 상속할 수 없는 클래스가 된다.

    public final class 클래스명 {
    }

    final class로 만들어진 finalClass

    위의 코드처럼 final class로 생성된 finalClass를 새로운 클래스인 finalClassExample에서 상속받으려고 했는데 할 수 없다는 경고문이 나온 것을 볼 수 있다.

     

    마지막으로 final 키워드를 사용한 메소드를 알아보자.

    메소드를 선언할 때 final 키워드를 붙이면 오버라이딩(overriding)을 할 수 없는 메소드가 된다.

    final method를 가지고 있는 클래스

    위의 코드를 보면 finalMethod 클래스에 final 키워드와 함께 method를 선언했다.

    그 후, 다른 클래스인 finalMethodExample 클래스에서 finalMethod를 상속받고 method( )를 오버라이딩 하려고 했지만 위와 같은 경고문이 발생하여 오버라이딩이 되지 않는 것을 볼 수 있다.

     

     

     

    7. Object 클래스

    object 클래스는 자바의 최상위 클래스로 모든 클래스는 정의할 때부터 object 클래스를 상속한다.

    그러므로 모든 클래스는 object 클래스의 메서드를 호출이 가능하다.

    object 클래스의 메서드는 아래와 같다.

    메소드 설명
    boolean equals(Object obj) 두 객체가 같은 지 비교한다.(같으면 true, 틀리면 false)
    String toString() 객체의 문자열을 반환한다.
    protected Object clone() 객체를 복사한다.
    protected void finalize() 가비지 컬렉션 직전에 객체의 리소스를 정리할때 호출한다.
    Class getClass() 객체의 클레스형을 반환한다.
    int hashCode() 객체의 코드값을 반환한다.
    void notify() wait된 스레드 실행을 재개할 때 호출한다.
    void notifyAll() wait된 모든 스레드 실행을 재개할 때 호출한다.
    void wait() 스레드를 일시적으로 중지할 때 호출한다.
    void wait(long timeout) 주어진 시간만큼 스레드를 일시적으로 중지할 때 호출한다.
    void wait(long timeout, int nanos)

     

    댓글

Designed by Tistory.