[JAVA] 최상위 클래스 Object에 대해

    728x90

    안녕하세요

    로로봉입니다 : )

    오늘은 자바의 최상위 클래스인 Object에 대해 알아보도록 하겠습니다.

    자바의 모든 클래스는 Object 클래스를 상속받습니다. 즉, Object 클래스는 자바의 최상위 클래스라고 합니다.

    컴파일러는 아무런 클래스도 상속하지 않으면 자동으로 extends Object를 상속합니다.

    아래와 같이 클래스 A를 상속받아 클래스 B를 만들었을 때 부모 클래스인 클래스 A는 extends를 붙이지 않아 아무것도 상속하지 않았습니다.

    class A {
       ...
    }
    class B extends A {
       ...
    }

    하지만 컴파일러는 컴파일 과정에서 자동으로 클래스 A에 extends Object를 추가하고 컴파일합니다.

    따라서 상속 관계가 Object ← A ← B 이렇게 만들어 집니다.

    class A extends Object {	// extends Object가 자동으로 추가됨.
       ...
    }
    class B extends A {
       ...
    }

    그래서 자바의 모든 클래스는 어떤 객체로 만들든지 Object 타입으로 선언할 수 있습니다.

    Object a = new A();
    Object b = new B();

    이 부분은 큰 장점이라고 합니다. 왜냐하면 메서드 오버로딩에서 Object 타입으로 오버로딩하는 경우 사용자가 직접 만든 클래스 타입도 전달할 수 있기 때문입니다.

    예를 들어 System.out.println() 메서드는 클래스 타입도 매개변수로 전달이 가능합니다.

    그 이유는 println 메서드가 println(Object x)로 오버로딩되어 있기 때문입니다.

    기본 자료형 이외의 Object를 입력매개변수로 메서드 오버로딩해 놓았기 때문에 다양한 타입과 다양한 클래스 객체도 전달이 가능한 것입니다.

    Object 클래스는 자바의 최상위 부모 클래스 입니다. 이 의미는 모든 클래스가 Object 클래스가 가지고 있는 메서드를 포함하고 있다는 뜻입니다.


    1) Object 클래스의 주요 메서드

    Object 클래스의 대표적인 메서드는 아래와 같습니다.

    변환타입 메서드명 주요 기능
    String toString() - Object 객체의 정보
    - 일반적으로 오버라이딩해서 사용
    boolean equals(Object obj) - 입력매개변수 obj객체와 stack 메모리값 비교
    - 등가 비교 연산자 == 와 동일한 결과
    int hashCode() - 객체의 hashCode() 값 리턴, Hashtable, HashMap 등의 동등 비교에 사용
    void wait()
    wait(long timeout)
    wait(long timeout, int nanos)
    - 현재의 쓰레드를 일시정지
    - 보통 notify() 또는 notifyAll()로 일시정지 해제
    - 동기화 블록에서만 사용 가능
    void notify()
    notifyAll()
    - wait()를 이용해 일시정지 상태의 1개의 쓰레드또는 전체 쓰레드 일시정지 해제
    - 동기화 블로게서만 사용 가능

     위 메서드들을 간단히 설명하자면 toString()은 객체 정보를 문자열로 출력하는 메서드입니다.

    equals(Object obj)는 등가 비교 연산(==)과 동일하게 스택 메모리값을 비교합니다.

    hashCode()는 객체의 위치 정보와 관련된 것으로, 이후에 배우게 될 Hashtable이나 HashMap에서 동일 객체 여부를 판단할 때 사용됩니다.

    wait()는 현재의 쓰레드를 일시정지하는 명령이고, notify()는 일시정지 중인 쓰레드를 다시 동작시키는 명령입니다.

    wait()와 notify()는 쓰레드와 동기화에 대해 알아볼 때 자세히 알아보도록 하겠습니다.


    toString() - 객체 정보를 문자열로 출력

    Object 클래스의 toString() 메서드는 객체 정보를 문자열로 리턴하는 메서드입니다. 여기서 객체 정보는 '패키지명.클래스명@해시코드'로 나타납니다. 해시코드는 객체가 저장된 위치돠 관련된 값입니다.

    실제 객체의 정보를 표현하고자 할 때는 대부분 클래스명이나 숫자로 나열된 해시코드보다는 객체에 포함돼 있는 필드값을 출력합니다. 따라서 자식 클래스에서는 toString() 메서드를 오버라이딩해 사용합니다.

    toString() 메서드를 오버라이딩하는 것을 예를 들어보겠습니다.

    class A {
       int a = 3;
       int b = 4;
    }
    
    class B {
       int a = 3;
       int b = 4;
       
       public String toString() {
          return "필드값 a = " + a + ", b = " + b;
       }
    }
    
    public class Test {
       public static void main(String[] args) {
          A a = new A(); 
          B b = new B();
          
          System.out.pringf("%x\n", a.hashCode());		// 16진수로 주소값
          System.out.println(a);				// a.toString() 자동으로 붙음
          System.out.pringln(b);				// b.toString() 자동으로 붙음
       }
    }
       }
    }

    클래스 A와 클래스 B는 아무것도 상송하지 않았으므로 컴파일러가 자동으로 extends Object를 삽입합니다.

    println() 메서드는 객체를 출력하면 자동으로 객체 내의 toString() 메서드를 호출합니다.

    따라서 System.out.println(a) 의 경우 System.out.println(a.toString()) 과 같이 됩니다.

    위에서 얘기했듯이 toString() 메서드는 '패키지명.클래스명@해시코드'로 출력됩니다.

    그래서 B클래스와 같이 자식 클래스에서 toString() 메서드를 오버라이딩해 사용하는 것이 일방적입니다.

    출력결과
    
    8783e431
    tc01.TestProject.A@8783e431
    필드값 a = 3, b = 4

    출력 결과를 보면 hashCode() 를 출력하여 위치값이 출력되고, a객체를 출력해서 a.toString() 메서드가 호출되어 '패지명.클래스명@해시코드'가 출력된 것을 볼 수 있습니다.

    마지막으로 b.toString()의 경우 자식 클래스에서 오버라이딩했던 toString() 메서드가 호출되어 필드값 a = 3, b = 4가 출력 된 것을 볼 수 있습니다.


    equals(Object obj) - 스택 메모리의 값 비교

    equals(Object obj) 메서드는 입력 매개변수로 넘어온 객체와 자기 객체의 스택 메모리 변숫값을 비교해 그 결과를 true 또는 false로 리턴하는 메서드입니다.

    기본 자료형이 아닌 객체의 스택 메모리값을 비교하므로 실제 데이터의 값이 아닌 실제 데이터의 위치를 비교하는 것입니다. 즉, 등가 비교 연산(==)과 완벽하게 동일한 기능을 수행합니다.

    스택 메모리 값을 비교하기 때문에 아래와 같이 객체 내부의 값은 동일하지만 실제 객체는 다른 곳에 위치하게 되면 모두 false값이 나오는 것을 볼 수 있습니다.

    class A {
       String name;
       A(String name) {
          this.name = name;
       }
    }
    
    public class TestClass {
       public static void main(String[] args) {
          A aa1 = new A("Hello");
          A aa2 = new A("Hello");
          System.out.println(aa1 == aa2);		// false
          System.out.pringln(aa1.equals(aa2));	// false
       }
    }

    만일 실제 내용을 비교하고자 할 때는 equals() 메서드를 오버라이딩해서 사용해야 합니다.

    아래와 같이 클래스 B에 equals() 메서드를 오버라이딩하여 결과를 확인해보겠습니다.

    class B {
       String name;
       B(String name) {
          this.name = name;
       }
       
       @Override
       public boolean equals(Object obj) {
          if (obj instanceof B) {
             if (this.name == ((B) obj).name)
                return true;
          }
          return false;
       }
    }
    
    public class TestProject {
       public static void main(String[] args) {
          B b1 = new B("Hello");
          B b2 = new B("Hello");
          System.out.println(b1 == b2);		// false
          System.out.pringln(b1.equals(b2));	// false

     위와 같이 equals를 오버라이딩 했다면 객체 안에 name 값이 동일한 경우 true를 반환하는 것을 볼 수 있습니다.

    hashCode() - 객체의 위치와 연관된 값

    hashCode() 메서드는 객체의 위치와 관련된 값으로, 실제 위치를 나타내는 값은 아닙니다.

    객체의 위칫값을 기준으로 생성된 고윳값 정도로 생각하는 것이 적절합니다.

    앞에서 두 객체의 내용을 비교하기 위해서는 equals() 메서드를 오버라이딩하는 것으로 충분했습니다.

    하지만 Hashtable, HashMap 등에서 동등 비교를 하고자 할 때는 hashCode() 까지 오버라이딩해야 합니다.

    HashMap 자료 구조는 데이터를 (Key, Value)의 쌍으로 저장하고, Key 값은 중복되지 않습니다.

    따라서 Key 값이 서로 같은지를 확인해야 하는데, 이 과정은 다음과 같이 2단계로 구성됩니다.

    첫 번째 단계는 두 객체의 hashCode() 값을 비교합니다. 일단 두 객체의 hashCode()값이 동일할 때 equals() 메서드를 호출하고, 이 값이 true이면 같은 객체로 인식합니다.

    HashMap에서 두 객체가 동일하기 위해서는 hashCode() 값과 equals() 메서드가 둘 다 true를 반환해야 합니다.

    Hash~ 형태의 자료 구조에서는 동등 비교를 위해 hashCode() 결과값을 비교하므로 필요할 때마다 equals() 메서드와 함께 추가로 오버라이딩해야한 다는 사실만 기억하면 됩니다.

    Hash관련된 자료구조를 알아볼 때 보다 자세히 알아보겠습니다.

     

    좋아요 ♥ + 구독 부탁드립니다 : )

    728x90
    반응형

    댓글