ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java 항해일지 - 6. 상속
    공부일기/자바 스터디 2020. 12. 24. 11:23

    상속이란?

    기존의 클래스를 재사용해 새로운 클래스를 작성하는 일이다. 상속을 통해 클래스를 작성하게 되면 보다 적은 양의 코드로 새로운 클래스 작성이 가능하고, 코드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다.

    코드의 재사용성을 높이고, 코드의 중복을 제거해 프로그램의 생산성과 유지보수에 크게 기여할 수 있다.

     

    • 자바 상속의 특징

      • extends : 새로 작성하고자 하는 클래스의 이름 뒤에 상속받고자 하는 클래스의 이름을 extends 키워드와 사용하면 된다.

      • 단일 상속 : 두 개이상의 클래스로부터 상속을 받을 수 없다. 다중 상속을 허용하게 될 경우 여러 클래스로부터 상속받을 수 있어 복합적인 기능을 가진 클래스를 작성하기 쉽지만, 클래스 간의 관계가 매우 복잡해지고 상속받은 멤버의 이름이 같은 경우 구별할 수 있는 방법이 없다는 단점이 있다. 따라서 자바에서는 클래스 간의 관계가 보다 명확해지고 코드를 더욱 신뢰할 수 있게 만들어주는 단일 상속만을 허용한다.

      • 모든 클래스의 조상인 Object 클래스 : 다른 클래스로부터 상속 받지 않은 모든 클래스들은 자동적으로  Object 클래스로부터 상속받는다. 따라서 Object 클래스에 정의된 멤버들을 사용할 수 있고 주요 메서드로는 toString()과 equals(Object o)가 있다.
        • toString() : 객체 자신의 정보를 문자열로 반환한다.
        • equals(Object o) : 객체 자신과 객체 o가 같은 객체인지 알려준다(true/false).

      • 오버라이딩 : 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것. 아래에서 자세히 다룬다.
    package JavaStudy;
    
    public class Tv {
        boolean power;
        int channel;
    
        void power() {
            power = !power;
        }
    
        void channelUp() {
            ++channel;
        }
        void channelDown() {
            --channel;
        }
    }
    package JavaStudy;
    
    public class SmartTv extends Tv { // Tv 클래스를 상속받은 SmartTv 클래스
        boolean caption; // 자막기능 on/off 를 true/false 로 표현
    
        void showCaption(String text) { // 자막 기능 on일 때 자막을 표시한다.
            if (caption) {
                System.out.println(text);
            }
        }
    }
    

    Tv클래스와 Tv클래스를 상속받아 자막기능이 추가된 SmartTv 클래스가 있다. 이 두 클래스는 서로 상속 관계에 있다고 하고, 상속해주는 클래스(Tv)를 '조상 클래스', 상속 받는 클래스(SmartTv)를 '자손 클래스'라고 한다.

    자손 클래스는 조상 클래스의 모든 멤버를 상속받기(생성자와 초기화 블럭 제외) 때문에, SmartTv클래스는 Tv클래스의 멤버들을 모두 포함하고 있다. 만약 조상 클래스인 Tv 클래스에 volume이라는 정수형 변수를 멤버변수로 추가하게 될 경우 자손 클래스인 SmartTv 클래스는 자동적으로 volume 이라는 멤버변수가 추가된다.

     

    아래 예제를 통해 자손 클래스는 조상 클래스의 모든 멤버를 상속받음을 알 수 있다.

    package JavaStudy;
    
    public class Main {
        public static void main(String[] args) {
            SmartTv smartTv = new SmartTv();
            smartTv.channel = 10;
            smartTv.channelDown();
    
            System.out.println(smartTv.channel);
    
            smartTv.showCaption("This is Caption");
            smartTv.caption = true;
            smartTv.showCaption("This is Caption");
        }
    }
    

    실행결과

     

    super란?

    super

    조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다. 지난 주 멤버변수와 지역변수의 이름이 같을 때 this를 붙여서 둘을 구별했듯이 상속받은 멤버와 자신의 멤버 이름이 같을 땐 super를 붙여 구별할 수 있다.

    아래 예제를 통해 더 자세히 살펴보자.

     

    package JavaStudy;
    
    public class Tv {
        int width = 50;
        int height = 30;
    }
    package JavaStudy;
    
    public class BigTv extends Tv { // Tv 클래스를 상속받은 BigTv 클래스
        int width = 100;
        int height = 50;
    
        void printSize() {
            System.out.println("width = " + width + ", " + "height = " + height);
            System.out.println("width = " + this.width + ", " + "height = " + this.height);
            System.out.println("width = " + super.width + ", " + "height = " + super.height);
        }
    }
    

    super.width와 super.height는 상속받은 Tv의 멤버 값인 50과 30일 것이다. 정말 그런지 실행해봤다. 

    package JavaStudy;
    
    public class Main {
        public static void main(String[] args) {
            BigTv bigTv = new BigTv();
            bigTv.printSize();
        }
    }
    

     

    모든 인스턴스 메서드에는 this와 super가 지역변수로 존재하며 각각 자신이 속한 인스턴스의 주소가 자동으로 저장된다. 어떤 대상을 가리키느냐만 다르고 누군가를 가리킨다는 그 근본적인 기능은 동일하다. this의 경우 자신의 멤버를, super의 경우 조상의 멤버를 가리킨다.

     

    super()

    this()와 같이 super()도 생성자다. this()의 경우 같은 클래스의 다른 생성자를 호출하는데 사용되고, super()의 경우 조상의 생성자를 호출하는데 사용된다.

    package JavaStudy;
    
    public class Graph {
        int x;
        int y;
    
        Graph(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    package JavaStudy;
    
    public class Graph3D extends Graph { // Graph 클래스를 상속받은 Graph3D 클래스
        int z;
    
        Graph3D(int x, int y, int z) {
    //        this.x = x;  조상의 멤버에 해당
    //        this.y = y;  조상의 멤버에 해당
            super(x, y); // 조상 클래스의 생성자 Graph(int x, int y)를 호출
            this.z = z;
        }
    }
    

     

     

    오버라이딩이란?

    조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것이 오버라이딩이다. 어떤 메서드를 사용할 때 상속받은 메서드를 그대로 사용할 수도 있지만, 자손 클래스에 맞게 변경해야 하는 경우엔 조상의 메서드를 오버라이딩한다.

    위에서 예제로 들었던 2차원 좌표 위의 한 점을 표현하기 위한 Graph클래스가 있고, 이를 상속받아 3차원 좌표 위의 한 점을 나타내는 Graph3D 클래스를 다시 보자.

     

    package JavaStudy;
    
    public class Graph {
        int x;
        int y;
    
        String getLocation() {
            return "x : " + x + "y : " + y;
        }
    }
    package JavaStudy;
    
    public class Graph3D extends Graph {
        int z;
    
        String getLocation() {
            return "x : " + x + "y : " + y + "z : " + z;
        }
    }

    Graph클래스의 getLocation()은 x와 y 좌표를 문자열로 반환한다. 두 클래스는 서로 상속 관계있어 Graph3D 클래스의 경우 자동으로 getLocation을 받지만 이 경우 z의 위치를 표현할 수 없다. 그래서 새로운 메서드가 필요한데 사실 getLocation()의 역할은 좌표 평면 위의 한 점의 위치를 나타낸다는 점에서 두 클래스 모두에서 동일한 역할을 한다. 그래서 이 메서드를 Graph3D 클래스에 맞게 z의 위치도 문자열로 반환할 수 있또록 오버라이딩 한 것이다.

    동일한 기능을 하는 메서드의 경우 새로운 메서드를 작성하는 것 보다 오버라이딩을 해 코드의 재사용성을 높이는 것이 올바른 선택일 것이다.

     

    • 오버 라이딩의 조건
      • 선언부가 조상 클래스의 메서드와 일치해야 한다.
      • 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.

        만약 조상 클래스에 정의된 메서드의 접근 제어자가 protected라면 이를 오버라이딩하는 자손 클래스의 메서드는 접근제어자가 protected 이거나 그 보다 범위가 더 넓은 public만 올 수 있다.


      • 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.

        예외 처리의 경우 9주차에 배우기 때문에 9주차에 다시 알아보겠다!

     

     

    • 오버로딩 vs 오버라이딩

    4주차에 공부한 오버로딩과 오버라이딩의 이름이 비슷해 혼동하기 쉬울 것 같아 따로 정리해봤다.

     

    오버로딩 : 기존에 없는 새로운 메서드를 추가하는 것.

    오버라이딩 : 조상으로부터 상속받은 메서드의 내용을 변경하는 것.

     

     

    메서드 디스패치란?

    어떤 메서드를 실행할지 결정하는 것으로 정적 메소드 디스패치와 동적 메서드 디스패치가 있다.

     

    정적 메서드 디스패치는 컴파일 되는 시점에 컴파일러가 어떤 클래스의 메서드를 수행하는지 알고 있다. 따라서 바이트코드에도 남게 된다.

     

    반면 동적 메서드 디스패치는 어떤 메서드를 실행하는지 컴파일 시점에는 모른다. 다만 추상클래스의 메서드나 인터페이스를 타입으로 메서드를 호출한다. 런타임 시점에 호출 객체를 확인하고 해당 객체의 메서드를 호출하게 되어 바이트코드에도 남지 않는다.

     

    예제를 통해 확인해보자.

    정적 메서드 디스패치 (Static Dispatch)

    public static void main(String[] args) {
        new Printer().print();
    }
    
    class Printer {
        void print() {
            System.out.println("출력됩니다.");
        }
    }

     

    동적 메서드 디스패치 (Dynamic Dispatch)

    public static void main(String[] args) {
        Printer printer = new PrinterImpl();
        printer.print();
    }
    
    abstract class Printer {
        abstract void print();
    }
    
    class RazorPrinter extends Printer {
        void run() {
            System.out.println("출력됩니다.");
        }
    }

     

    더블 메서드 디스패치 (Double Dispatch)

    더블 메서드 디스패치는 동적 디스패치를 두 번 하는 것이다. 구현체 별로 메서드가 다르게 구현되어야 할 경우 if문을 사용해 각 구현체별 분기를 나눠 실행하지 않게 만들 수 있다고 한다.

     

    스터디 라이브를 보는 중 다른 스터디원 분 중에 정리를 잘 해놓으신 예가 있어서 가져왔다.

    (참고. leemoono.tistory.com/20)

     

    이 예제에서 구현체들은 크게 히어로, 빌런으로 나뉘고 각각 수퍼히어로, 하이퍼히어로/ 수퍼빌런, 하이퍼빌런으로 나뉜다.

    각 객체들이 다른 객체를 공격할때 if문을 사용하지 않고 호출되는 객체를 확인하고 해당 객체의 메서드를 실행하여 각 객체별로 다른 로직이 적용된다.

     

    public abstract class Unit {
        int hp;
        int attackPoint;
    
        public Unit(int hp, int attackPoint) {
            this.hp = hp;
            this.attackPoint = attackPoint;
        }
    
    }

    먼저 히어로와 빌런을 모두 포함하는 추상 클래스를 만든다. 체력과 공격력을 지니고 있다.

     

    public abstract class Hero extends Unit {
    
        int level;
    
        public Hero(int hp, int attackPoint, int level) {
            super(hp, attackPoint);
            this.level = level;
        }
    
        //1. goto  hyperHero.attack()  or  superHero.attack()
        public abstract void attack(Villain villain);  
    
        public void isAttackedBy(SuperVillain superVillain) {
            hp -= (superVillain.attackPoint * 2 - level);
        }
    
        public void isAttackedBy(HyperVillain hyperVillain) {
            hp -= (hyperVillain.attackPoint);
        }
    }

    다음으로 유닛클래스를 상속받은 히어로 클래스를 만들었다. 추상 메서드인 공격과 수퍼빌런, 하이퍼빌런으로 부터 공격받았을 때 실행되는 로직이 다른 것을 확인할 수 있다.

     

    public abstract class Villain extends Unit {
    
        int defensePoint;
    
        public Villain(int hp, int attackPoint, int defensePoint) {
            super(hp, attackPoint);
            this.defensePoint = defensePoint;
        }
    
        //1. goto  hyperVillain.attack()  or  superVillain.attack() 
        public abstract void attack(Hero hero);
    
        public void isAttackedBy(SuperHero superHero) {
            hp -= (superHero.attackPoint + superHero.level - defensePoint);
        }
    
        public void isAttackedBy(HyperHero hyperHero) {
            hp -= (hyperHero.level * 3);
        }
    }

    빌런 클래스도 마찬가지로 유닛클래스를 상속받아 히어로를 공격하는 경우, 히어로들에게 공격 받는 경우의 로직들이 다르게 구현돼 있다.

     

    public class HyperHero extends Hero {
    
        public HyperHero(int hp, int attackPoint, int level) {
            super(hp, attackPoint, level);
        }
    
        //2. goto villain.isAttackedBy(superHero)  or  villain.isAttackedBy(hyperHero)
        @Override
        public void attack(Villain villain) {
            villain.isAttackedBy(this);
        }
    
    }
    public class SuperHero extends Hero{
    
        public SuperHero(int hp, int attackPoint, int level) {
            super(hp, attackPoint, level);
        }
    
        //2. goto villain.isAttackedBy(superHero)  or  villain.isAttackedBy(hyperHero)
        @Override
        public void attack(Villain villain) {
            villain.isAttackedBy(this);
        }
    }

    다음으로는 각각 하이퍼히어로와 수퍼히어로 클래스다. 모두 히어로 클래스를 상속받아 히어로 클래스의 attack메서드를 오버라이딩했다. this를 통해 자기자신을 객체로 넘겨 해당 객체를 확인하고 각 객체에 따라 다른 isAttackedBy 메서드가 실행된다.

     

     

    public class HyperVillain extends Villain {
    
        public HyperVillain(int hp, int attackPoint, int defensePoint) {
            super(hp, attackPoint, defensePoint);
        }
    
        //2. goto hero.isAttackedBy(superVillain)  or  hero.isAttackedBy(hyperVillain)
        @Override
        public void attack(Hero hero) {
            hero.isAttackedBy(this);
        }
    }
    public class SuperVillain extends Villain{
    
        public SuperVillain(int hp, int attackPoint, int defensePoint) {
            super(hp, attackPoint, defensePoint);
        }
    
        //2. goto hero.isAttackedBy(superVillain)  or  hero.isAttackedBy(hyperVillain)
        @Override
        public void attack(Hero hero) {
            hero.isAttackedBy(this);
        }
    }

    하이퍼빌런과 수퍼빌런도 위와 마찬가지로 자기자신을 this로 넘겨 히어로를 공격할 때 어떤 객체로 부터 공격받느냐에 따라 다른 로직이 적용되는 것을 확인할 수 있다.

     

    만약 다음 단계의 혹은 더 약한 단계의 히어로나 빌런이 추가된다면 해당 부분을 추가해서 넣어주기만 하면 되고, 이런 경우 좀 더 유연한 코드가 되는 것을 알 수 있다. 이런 패턴을 방문자 패턴이라고 부른다고 한다.

     

    추상 클래스란?

    지난 주 스터디 범위인 클래스에서 클래스를 설계도에 비유했다. 클래스가 설계도라면, 추상 클래스는 미완성 설계도에 비유할 수 있다. 추상 클래스는 미완성 메서드(추상 메서드)를 포함하고 있다는 의미이다. 미완성 설계도로는 제품을 만들 수 없는 것처럼 추상 클래스만으로는 객체를 생성할 수 없다. 추상 클래스를 상속받는 자손 클래스에서 정의되지 않은 메서드를 완성시켜 객체를 생성해야 한다.

     

    추상 클래스 자체로 클래스의 역할을 할 수 없지만 조상 클래스로서 중요한 의미를 갖는다. 추상 클래스는 abstract를 선언부에 붙이면 된다. 추상 클래스도 생성자를 지녔으며, 멤버변수와 메서드도 가질 수 있다.

     

    상속이 자손 클래스를 만드는데 조상 클래스를 사용하는 것이면, 추상화는 기존 클래스의 공통된 부분들을 추출해 조상 클래스를 만드는 것이라 비유할 수 있다. 쉽게 이해할 수 있도록 유명했던 게임 스타크래프트에 나오는 유닛들로 추상 클래스에 대한 예를 만들어 봤다.

     

    class Marine {
        int hp, mp; // 체력과 마나
        int x, y; // 현재 위치
        void move(int x, int y);
        void stop();
        void stimPack();
    }
    
    class HighTemplar {
        int hp, mp; // 체력과 마나
        int x, y; // 현재 위치
        void move(int x, int y);
        void stop();
        void PsionicStrom();
    }
    
    class Defiler {
        int hp, mp; // 체력과 마나
        int x, y; // 현재 위치
        void move(int x, int y);
        void stop();
        void DarkSwarm();
    }

    세 개의 유닛클래스에 각각 공통된 부분이 존재한다. 이 부분을 뽑아 Unit 클래스로 정의하고 Unit 클래스를 상속받게 만들어보면 다음과 같다.

    abstract class Unit {
        int hp, mp; // 체력과 마나
        int x, y; // 현재 위치
        void move(int x, int y);
        void stop();
    }
    
    
    class Marine extends Unit {
        void move(int x, int y);
        void stimPack();
    }
    
    class HighTemplar {
        void move(int x, int y);
        void PsionicStrom();
    }
    
    class Defiler {
        void move(int x, int y);
        void DarkSwarm();
    }

     

    이렇게 abstract를 사용해 추상 클래스를 만들게 되면 코드의 재사용성이 높아진다.

     

     

    final 키워드

    final은 '변경될 수 없는'의 의미로 사용된다. 거의 모든 대상에 사용할 수 있으며 변수에 사용되면 값을 변경할 수 없는 상수가 되고, 메서드에 사용하게되면 오버라이딩을 할 수 없게 된다. 클래스에 사용할 경우는 자신을 확장하는 자손 클래스를 정의하지 못하게 된다.

     

    이를 표로 나타내면 아래와 같다.

    제어자 대상 의미
    final 클래스 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다.
    final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다.
    (자손 클래스를 갖지 못한다)
    메서드 변경될 수 없는 메서드로 만든다. 
    final로 지정된 메서드는 오버라이딩을 통해 재정의 할 수 없다.
    멤버변수 변수 앞에 final이 붙게 되면, 값을 변경할 수 없는 상수가 된다.
    지역변수

     

     

    클래스와 메소드, 각 변수들을 변경하려하면 컴파일 에러가 나는 것을 확인할 수 있다.

     

     

     

     

    Object 클래스란?

    자바 상속의 특징에서 설명했던 것처럼 Object 클래스란 모든 클래스들의 최고 조상 클래스이다. 따라서 Object 클래스에 정의된 멤버들은 모든 클래스에서 바로 사용 가능하다. Object 클래스내에는 멤버변수는 없고 오직 11개의 메서드만 가지고 있다. 이 메서드들은 모든 인스턴스가 가져야 할 기본적인 것들이고 주요한 메서드로는 toString()과 equals(Object o)가 있다.

     

    equals()

    equals()의 역할은 매개변수로 객체의 참조변수를 받아 비교해 그 결과를 boolean 값으로 알려주는 것이다.

    실제 Object클래스에 정의되어 있는 equals 메서드를 보면 다음과 같다

     

    두 객체의 같고 다름을 참조변수의 값으로 판단하고, 문자열을 비교하여 같으면 true, 다르면 false를 반환한다.

     

     

    package JavaStudy;
    
    public final class Equal {
        public static void main(String[] args) {
            Value v1 = new Value(10);
            Value v2 = new Value(10);
            
            if (v1.equals(v2)) {
                System.out.println("서로 같음");
            } else {
                System.out.println("서로 다름");
            }
        }
    }

    각 인스턴스가 저장된 주소가 다르므로 서로 다름이 출력된다.

     

    package JavaStudy;
    
    public final class Equal {
        public static void main(String[] args) {
            String str1 = "파이널";
            String str2 = "파이널";
    
            if (str1.equals(str2)) {
                System.out.println(str1 +"과"+ str2+ "은 서로 같음");
            } else {
                System.out.println(str1 +"과"+ str2+ "은 서로 다름");
            }
    
            if (str1 == str2) {
                System.out.println(str1 +"과"+ str2+ "은 서로 같음");
            } else {
                System.out.println(str1 +"과"+ str2+ "은 서로 다름");
            }
        }
    }

     

    두 객체가 같으므로 서로 같음이 출력된다.

    그렇다면 아래와 같은 경우는 어떨까?

     

    package JavaStudy;
    
    public final class Equal {
        public static void main(String[] args) {
            String str1 = new String("파이널");
            String str2 = new String("파이널");
    
            if (str1.equals(str2)) {
                System.out.println(str1 +"과 "+ str2+ "은 서로 같음");
            } else {
                System.out.println(str1 +"과 "+ str2+ "은 서로 다름");
            }
    
            if (str1 == str2) {
                System.out.println(str1 +"과 "+ str2+ "은 서로 같음");
            } else {
                System.out.println(str1 +"과 "+ str2+ "은 서로 다름");
            }
        }
    }

     

    String 클래스에 관한 내용이지만 파이썬을 배우고 처음 자바로 넘어왔을 때 == 연산자가 String에 적용이 안되는 것에 놀랐던 기억이 있어서 equal 메서드를 배우는 부분에 참고할 수 있도록 넣어봤다. 

    equals() 메서드는 인스턴스의 주소에 저장된 값을 비교해 두 값이 서로 같으면 true를 반환했지만 == 연산자의 경우 두 값이 저장된 위치만을 비교했기 때문에 false가 반환됐다.

    첫 번째 예제에서는 문자열 리터럴 "파이널"의 주소가 str1에 저장되었고, 마찬가지로 같은 주소가 str2에도 저장됐다.

    그러나 두 번째 예제에서는 새로운 String 인스턴스를 각각 만들었기 때문에 메모리에 저장된 주소는 서로 다르다.

    그래서 단순히 주소를 비교하는 == 연산자의 경우 false를 반환하게 되는 것이다.

     

     

    equal()의 오버라이딩

    package JavaStudy;
    
    public class Person {
        int id;
    
        Person(int id) {
            this.id = id;
        }
    
        public boolean equals(Object o) {
            if (o instanceof Person) {
                return this.id == ((Person)o).id;
            }
            return false;
        }
    }
    
    package JavaStudy;
    
    public final class Equal {
        public static void main(String[] args) {
            Person p1 = new Person(123456789);
            Person p2 = new Person(123456789);
    
            if (p1.equals(p2)) {
                System.out.println("같음");
            } else {
                System.out.println("다름");
            }
        }
    }

    Person이란 클래스에서 equals메서드를 오버라이딩해 객체에 저장된 내용을 비교하도록 변경했다. 이제 결과를 출력해보면 어떻게 나올까?

    예상했듯이 정답은 같은 값을 가졌다고 나온다. 

    이런식으로 equals메서드가 Person 인스턴스의 주소값이 아닌 멤버변수 id의 값을 비교해 서로 다른 인스턴스지만 같은 id를 가지는 경우 true가 반환되도록 할 수 있다.

     

     

    toString()

    인스턴스에 대한 정보를 문자열로 제공할 목적으로 toString() 메서드가 Object 클래스에 정의되어 있다. 인스턴스의 정보를 제공한다는 것은 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 것이다.

    클래스를 작성할 때 toString()을 오버라이딩 하지 않는다면, 클래스 이름과 해시코드(메모리에 값이 저장된 주소)가 출력된다. 

     

     

    package JavaStudy;
    
    public final class Equal {
        public static void main(String[] args) {
            Person p1 = new Person(123456789);
            Person p2 = new Person(123456789);
    
            System.out.println(p1.toString());
            System.out.println(p2.toString());
        }
    }

     

    위의 Person 클래스에서 toString()을 오버라이딩해 사용자의 아이디값을 제공할 수 있도록 바꿔보면 아래와 같다.

    package JavaStudy;
    
    public final class Equal {
        public static void main(String[] args) {
            Person p1 = new Person(123456789);
            Person p2 = new Person(123456789);
    
            System.out.println(p1.toString());
            System.out.println(p2.toString());
        }
    }

     

    toString을 오버라이딩 해 원하는 정보를 보다 보기 좋게 얻을 수 있게 됐다. 이 때, Object 클래스에 정의된 toString()의 접근 제어자가 public 이므로 Person 클래스의 toString() 접근제어자도 public으로 했다. 자손 클래스에서의 접근 제어자 범위는 조상 클래스보다 좁을 수 없다!

     

     

     

    Object 클래스의 11가지 메서드

    Object 클래스의 메서드 설명
    protected Object clone() 객체 자신의 복사본을 반환한다.
    public boolean equals(Object o) 객체 자신과 객체 o가 같은 객체인지 알려준다. (같으면 true)
    참조 변수의 값으로 비교한다.
    protected void finalize() 객체가 소멸될 때 GC에 의해 자동적으로 호출된다.
    public Class getClass() 객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환한다.
    public int hasCode() 객체 자신의 해시코드를 반환한다.
    public String toString() 객체 자신의 정보를 문자열로 반환한다.
    public void notify() 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다.
    public void notifyAll() 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다.
    public void wait()
    public void wait(long timeout)
    public void wait(long timeout, int nanos)
    다른 쓰레드가 notifyt()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게 한다.
    timeout = 1/1000 초, nanos = 1/10^9 초

     

     

     

     

    참고.

    dbbymoon.tistory.com/9

    multifrontgarden.tistory.com/133

     

Designed by Tistory.