ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] 6주차 과제 : 자바의 상속
    Java/온라인 자바 스터디 2020. 12. 28. 11:46
    반응형

    목표

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


    학습할 것

    자바 상속의 특징

    super 키워드

    메소드 오버라이딩

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

    추상 클래스

    final 키워드

    Object 클래스


    자바 상속의 특징

    상속이란?

    객제 지향에서의 상속은 상위 클래스의 특성을 하위 클래스에서 상속(특성 상속)하고 거기에 더해 필요한 특성을 추가, 즉 확장해서 사용할 수 있다는 의미입니다.


    상속의 특징

     - 객체 지향의 상속은 상위 클래스의 특성을 재사용하는 것이다.

     - 객체 지향의 상속은 상위 클래스의 특성을 확장하는 것이다.

     - 객체 지향의 상속은 is a kind of 관계를 만족해야 한다. 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    package extends01;
     
    public class Animal {
     
        public String name;
         
        Animal() {
            name = "동물";
        };
     
        public void getClassName() {
            System.out.println(name + " 클래스입니다.");
        }
    }
     
     
    package extends01;
     
    public class Mammals extends Animal {
        // 하위 클래스 is a kind of 상위 클래스
        // 포유류 is a kind of 동물 -> 포유류는 동물의 한 분류다.
        Mammals() {
            name = "포유류";
        }
    }
     
    package extends01;
     
    public class Birds extends Animal {
        // 하위 클래스 is a kind of 상위 클래스
        // 조류 is a kind of 동물 -> 조류는 동물의 한 분류다.
        Birds() {
            name = "조류";
        }
    }
     
    package extends01;
     
    public class Whale extends Mammals {
        // 하위 클래스 is a kind of 상위 클래스
        // 고래 is a kind of 포유류 -> 고래는 포유류의 한 분류다.
        // 고래 is a kind of 동물 -> 고래는 동물의 한 분류다.
        Whale() {
            name = "고래";
        }
    }
     
    package extends01;
     
    public class Bat extends Mammals {
        // 하위 클래스 is a kind of 상위 클래스
        // 박쥐 is a kind of 포유류 -> 박쥐는 포유류의 한 분류다.
        // 박쥐 is a kind of 동물 -> 박쥐는 동물의 한 분류다.
        Bat() {
            name = "박쥐";
        }
    }
     
    package extends01;
     
    public class Sparrow extends Birds {
        // 하위 클래스 is a kind of 상위 클래스
        // 참새 is a kind of 조류 -> 참새는 조류의 한 분류다.
        // 참새 is a kind of 동물 -> 참새는 동물의 한 분류다.
        Sparrow() {
            name = "참새";
        }
    }
     
    package extends01;
     
    public class Penguin extends Birds {
        // 하위 클래스 is a kind of 상위 클래스
        // 펭귄 is a kind of 조류 -> 펭귄은 조류의 한 분류다.
        // 펭귄 is a kind of 동물 -> 펭귄은 동물의 한 분류다.
        Penguin() {
            name = "펭귄";
        }
    }
     
    package extends01;
     
    public class Main {
     
        public static void main(String[] args) {
     
            Animal animal = new Animal();
            Mammals mammals = new Mammals();
            Birds birds = new Birds();
            Whale whale = new Whale();
            Bat bat = new Bat();
            Sparrow sparrow = new Sparrow();
            Penguin penguin = new Penguin();
     
            animal.getClassName();  // 동물 클래스입니다.
            mammals.getClassName(); // 포유류 클래스입니다.
            birds.getClassName();   // 조류 클래스입니다.
            whale.getClassName();   // 고래 클래스입니다.
            bat.getClassName();     // 박쥐 클래스입니다.
            sparrow.getClassName(); // 참새 클래스입니다.
            penguin.getClassName(); // 펭귄 클래스입니다.
        }
    }
    cs

    상위 클래스에서만 getClassName() 메서드를 구현했지만 모든 하위 클래스의 객체에서 getClassName() 메서드를 사용할 수 있다. 상속한다는 것은 이렇게 상위 클래스의 특성을 상속한다는 의미이다.


    super 키워드

    단일 상속만을 지원하는 자바에서 super는 바로 위 상위 클래스의 인스턴스를 지칭하는 키워드이다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package super01;
     
    public class Animal {
     
        void method(){
            System.out.println("동물");
        }
    }
     
    package super01;
     
    public class Birds extends Animal {
     
        void method(){
            // super 키워드를 이용해 상위 클래스의 인스턴스 메서드를 호출
            super.method(); 
            System.out.println("조류");
        }
    }
     
    package super01;
     
    public class Penguin extends Birds {
     
        void method() {
            // super 키워드를 이용해 상위 클래스의 인스턴스 메서드를 호출
            super.method();
            // Syntax error on token "super", Identifier expected
            // super.super.method();
            System.out.println("펭귄");
        }
    }
     
    package super01;
     
    public class Main {
     
        public static void main(String[] args){
            
            Penguin penguin = new Penguin();
            penguin.method(); // 동물 조류 펭귄
        }
    }
    cs

    super 키워드로 바로 위의 상위 클래스 인스턴스에는 접근할 수 있지만 super.super 형태로 상위의 상위 클래스의 인스턴스에는 접근이 불가능하다.


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

    Method Dispatch는 어떤 메소드를 호출할지 결정하여 실행시키는 과정을 말하며, Static Method Dispatch와 Dynamic Method Dispatch로 구분된다.


    Static Method Dispatch

    자바에서 객체 생성은 런타임시에 호출된다. 즉 컴파일타임에 알수있는건 타입에 대한 정보이다. 타입자체가 Dispatch라는 구현클래스이기때문에 해당 메서드를 호출하면 어떤 메서드가 호출될지 정적으로 정해진다. 이에대한 정보는 컴파일이 종료된 후 바이트코드에도 드러나게된다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package dispatch01;
     
    public class Dispatch {
     
        public String method(){
            return "Static Dispatch";
        }
    }
     
    package dispatch01;
     
    public class Main {
     
        public static void main(String[] arg) {
        
            Dispatch dispatch = new Dispatch();
            
            System.out.println(dispatch.method());
        }
    }
    cs


    Dynamic Method Dispatch

    인터페이스를 타입으로 메서드를 호출한다. 컴파일러는 타입에 대한 정보를 알고있으므로 런타임시에 호출 객체를 확인해 해당 객체의 메서드를 호출한다. 런타임시에 호출 객체를 알 수 있으므로 바이트코드에도 어떤 객체의 메서드를 호출해야하는지 드러나지 않는다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package dispatch01;
     
    public class Dispatch implements Dispatchable {
     
        public String method() {
            return "Dynamic Dispatch";
        }
    }
     
    package dispatch01;
     
    interface Dispatchable {
     
        String method();
    }
     
    package dispatch01;
     
    public class Main {
     
        public static void main(String[] arg) {
        
            Dispatch dispatch = new Dispatch();
            
            System.out.println(dispatch.method());
        }
    }
    cs

    메소드 오버라이딩

    같은 메서드 이름, 같은 인자 목록으로 상위 클래스의 메서드를 재정의하는 것이다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package override01;
     
    public class Animal {
     
        public String name;
         
        public void getClassName() {
            System.out.println("동물 클래스입니다.");
        }
    }
     
    package override01;
     
    public class Penguin extends Animal {
     
        @Override
        public void getClassName() {
            System.out.println("펭귄 클래스입니다.");
        }
    }
     
    package override01;
     
    public class Main {
     
        public static void main(String[] agrs){
            
            Penguin penguin = new Penguin();
            penguin.getClassName(); // 펭귄 클래스입니다.
        }
    }
    cs


    추상 클래스

    선언부는 있는데 구현부가 없는 메서드를 말한다. 추상 메서드를 하나라도 갖고 있는 클래스는 반드시 추상 클래스로 선언해야한다. 물론 추상 메서드 없이도 추상 클래스를 선언할 수 있다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    package abstract01;
     
    public abstract class Animal {
     
        // 추상 메서드는 하위 클래스에게 메서드의 구현을 강제한다. 오버라이딩 강제한다.
        abstract void getCry();
    }
     
    package abstract01;
     
    public class Mouse extends Animal{
        
        // getCry 메서드를 구현하지 않으면, 다음과 같은 에러가 발생한다.
        // The type Mouse must implement the inherited abstract method Animal.getCry()
        void getCry(){
            System.out.println("쥐 - 찍! 찍!");
        }
    }
     
    package abstract01;
     
    public class Cat extends Animal{
        
        // getCry 메서드를 구현하지 않으면, 다음과 같은 에러가 발생한다.
        // The type Cat must implement the inherited abstract method Animal.getCry()
        void getCry(){
            System.out.println("고양이 - 야옹! 야옹!");
        }
    }
     
    package abstract01;
     
    public class Dog extends Animal{
     
        // getCry 메서드를 구현하지 않으면, 다음과 같은 에러가 발생한다.
        // The type Dog must implement the inherited abstract method Animal.getCry()
        void getCry(){
            System.out.println("강아지 - 멍! 멍!");
        }
    }
     
    package abstract01;
     
    public class Calf extends Animal{
     
        // getCry 메서드를 구현하지 않으면, 다음과 같은 에러가 발생한다.
        // The type Calf must implement the inherited abstract method Animal.getCry()
        void getCry(){
            System.out.println("송아지 - 음메! 음메!");
        }
    }
     
    package abstract01;
     
    public class Chick extends Animal{
     
        // getCry 메서드를 구현하지 않으면, 다음과 같은 에러가 발생한다.
        // The type Chick must implement the inherited abstract method Animal.getCry()
        void getCry(){
            System.out.println("병아리 - 삐약! 삐약!");
        }
    }
     
    package abstract01;
     
    public class Main {
     
        public static void main(String[] agrs){
            
            // Cannot instantiate the type Animal
            // 추상 클래스는 인스턴스, 즉 객체를 만들 수 없는 클래스이다.
            // Animal animal1 = new Animal(); 
                    
            Animal[] animals = new Animal[5];        
            animals[0= new Mouse();
            animals[1= new Cat();
            animals[2= new Dog();
            animals[3= new Calf();
            animals[4= new Chick();
            
            for(int a=0; a<animals.length; a++){
                animals[a].getCry();
            }
        }
    }
    cs

    추상 클래스의 특징

     - 추상 클래스는 인스턴스, 즉 객체를 만들 수 없다. 즉, new를 사용할 수 없다.

     - 추상 메서드는 하위 클래스에게 메서드의 구현을 강제한다. 오버라이딩 강제한다.

     - 추상 메서드를 포함하는 클래스는 반드시 추상 클래스여야 한다.


    final 키워드

    final은 마지막, 최종이라는 의미를 가진 단어다. final 키워드가 나타날 수 있는 곳은 딱 세 군데다. 사실 객체 지향 언어의 구성 요소는 딱 세 가지 뿐이다. 바로 클래스, 변수 메소드다.


    final과 클래스

    클래스에 final이 붙으면, 상속을 허라하지 않겠다는 의미다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package final01;
     
    public final class Animal {
        // 클래스에 final이 붙으면, 상속을 허라하지 않겠다는 의미다.
    }
     
    package final01;
     
    public class Birds extends Animal{    
        // 컴파일러가 다음과 같은 에러를 표시한다.
        // The type Birds cannot subclass the final class Animal
        // Birds 타입은 최종 클래스인 Animal의 하위 분류가 될 수 없다.
    }
    cs


    final과 변수

    변수에 final이 붙으면, 변경 불가능한 상수가 된다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package final01;
     
    public class Animal {
     
        final static int 정적상수1 = 1;
        final static int 정적상수2;
        
        final int 객체상수1 = 1;
        final int 객체상수2;
        
        static {
     
            정적상수2 = 2;
            
            // 상수는 한 번 초기화하되면 값을 변경할 수 없다.
            // 정적상수2 = 4;
        }
        
        Animal01() {
     
            객체상수2 = 2;
            
            // 상수는 한 번 초기화하되면 값을 변경할 수 없다.
            // 객체상수2 = 4;
            
            final int 지역상수1 = 1;
            final int 지역상수2;
            
            지역상수2 = 2;
        }
    }
    cs

    정적 상수는 선언 시에, 또는 정적 생성자에 해당하는 static 블록 내부에서 초기화가 가능하다. 객체 상수 역시 선언 시에, 또는 객체 생성자 또는 인스턴스 블록에서 초기화할 수 있다. 지역 상수 역시 선언 시에, 또는 최초 한 번만 초기화가 가능하다.


    final과 메서드

    메서드에 final이 붙으면, 오버라이딩을 금지하게 된다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package final01;
     
    public class Animal02 {
     
        final void method(){
            System.out.println("동물");
        }
    }
     
    package final01;
     
    public class Birds02 extends Animal02{    
     
        void method(){
            // Cannot override the final method from Animal02
            System.out.println("조류");
        }
    }
    cs


    출처 : 스프링 입문을 위한 자바 객체 지향의 원리와 이해


    Object 클래스

    java.lang.Object 클래스는 자바 API의 모든 클래스와 사용자가 정의한 모든 클래스의 최상위 클래스이다. 즉, 모든 자바 클래스들은 Object 클래스로부터 상속받는다.


    Object 클래스의 메서드

    protected Object clone() 

    객체 자신의 복사본을 반환한다.

    public boolean equals(Object obj) 

    객체 자신과 객체 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) 동안 기다리게 한다.


    참조 : https://github.com/whiteship/live-study/issues/6



    반응형

    댓글

Designed by Tistory.