티스토리 뷰

JAVA/JAVA기본

JAVA - Object 클래스란?

realizers 2022. 1. 16. 10:15
728x90
반응형

Object 클래스란


  • java.lang.Object 클래스는 모든 클래스의 최상위 클래스로 모든 클래스는 Object 클래스를 상속받습니다.
  • 따라서 모든 클래스는 Object 클래스의 메서드를 사용할 수 있고 일부 메서드를 재정의하여 사용할 수 있습니다.
  • 다만 final 메서드는 재정의할 수 없습니다.
  • java.lang.Object 패키지는 컴파일러에 의해 자동으로 import됩니다.
  • Object 클래스는 필드는 없고 메서드로 구성되어 있습니다.

 

equals()


  • 객체 간의 동일성을 비교하고 싶은 경우 == 을 사용하지 않고 equals() 메서드를 사용해야 합니다.
  • equals()메서드를 구현해야한다면 hashCode() 메서드도 함께 구현해야 합니다.
  • equals() 메서드의 경우 기본적으로 Object 클래스가 가진 equals() 메서드를 그대로 사용하는것을 권장합니다.
  • Primitive Type(원시 타입)을 비교하는 경우는 비교 연산자(==)을 사용하고 객체를 비교하는 경우는 equals() 메서드를 사용하면 됩니다.

💡 equals()메서드를 재정의하기 위한 5가지 조건

  • 재귀 : null이 아닌 x라는 객체의 x.equals(x)는 항상 true여야 합니다.
  • 대칭 : null이 아닌 x와 y 객체가 있을 때 y.equals(x)가 true를 리턴했다면 x.equals(y)도 반드시 true를 리턴해야합니다.
  • 타동적 : null아 아닌 x, y, z가 있을 때 x.equals(y)가 true를 리턴하고, y.equals(z)가 true를 리턴한다면 x.equals(z)는 반드시 true를 리턴해야합니다.
  • 일관 : null아닌 x와 y가 있을 때 객체가 변경되지 않는 상황에서는 몇 번을 호출하더라도 x.equals(y)의 결과는 항상 true이거나 false여야 합니다.
  • null과의 비교 : null이 아닌 x라는 객체의 x.equals(null) 결과는 항상 false여야 합니다.

 

equals()메서드를 재정의하지 않은 경우


  • 아래의 예제를 실행하면 false가 반환이 됩니다. 그 이유는 equals() 메서드의 기본 동작은 == 연산이기 때문에 false가 반환됩니다. 이 경우 equals() 메서드를 재정의하여 동일성을 갖추도록할 수 있습니다.
public class Member {
    String name;
    int registrationNumber; // 주민번호

    public Member(String name, int registrationNumber) {
        this.name = name;
        this.registrationNumber = registrationNumber;
    }

    public static void main(String[] args) {
        Member member1 = new Member("홍길동", 123);
        Member member2 = new Member("홍길동", 123);

        System.out.println(member1.equals(member2)); // false
    }
}

 

equals()메서드를 재정의한 경우


  • equals() 메서드를 재정의하여 주민번호가 같으면 같은 객체로 판별합니다.
  • equals() 메서드의 반환값이 true이면 두 객체가 같다는 의미인데 그렇다면 해시코드값도 같아야합니다. 하지만 아래의 예제의 해시코드를 출력하면 각각 다른 값이 출력되는데 이 이유는 hashCode() 메서드를 재정의 하지 않아서입니다.
public class Member {
    String name;
    int registrationNumber; // 주민번호

    public Member(String name, int registrationNumber) {
        this.name = name;
        this.registrationNumber = registrationNumber;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Member){
            return this.registrationNumber == ((Member) obj).registrationNumber;
        }
        return false;
    }

    public static void main(String[] args) {
        Member member1 = new Member("홍길동", 123);
        Member member2 = new Member("홍길동", 123);

        System.out.println(member1.equals(member2)); // true
        System.out.println(member1.hashCode()); // 234698513
        System.out.println(member2.hashCode()); // 1121172875
    }
}

 

 

hashCode()


  • 해시코드란 객체를 식별할 하나의 정수값입니다.
  • Object 클래스의 hashCode()메서드는 객체의 메모리 번지를 이용하여 해시코드를 만들어서 반환합니다. 따라서 각 객체마다 다른 해시코드 값을 가지고 있습니다.
  • 해시코드 값이 다르면 다른 객체로 판단하고 값이 같으면 equals() 메서드를 사용하여 다시 검사합니다. 해시코드 값이 같아도 equals() 메서드로 판별하여 true가 아니면 다른 객체가 됩니다.

 

💡 hashCode() 메서드를 재정의하기 위한 조건

  • 자바 애플리케이션이 수행되는 동안에 어떤 객체에 대해서 이 메서드가 호출될 때에는 항상 동일한 int값을 리턴해줘야 합니다. 하지만 자바를 실행할 때마다 같은 값이어야 할 필요는 없습니다.
  • 어떤 두개의 객체에 대하여 equals() 메서드를 사용하요 비교한 결과가 true일 경우에 두 객체의 hashCode() 메서드를 호출하면 동일한 int값을 반환해야합니다.
  • 두 객체를 equals() 메서드를 사용해 비교한 결과 false를 리턴했다고 해서 hashCode() 메서드를 호출한 int 값이 무조건 달라야 할 필요는 없습니다. 하지만 이 경우에 서로 다른 int 값을 제공하면 hashtable의 성능을 향상시키는데 도움이 됩니다.

 

hashCode()를 재정의하지 않은 경우


  • 아래 예제는 equals() 메서드를 재정의하여 주민등록번호가 같으면 같은 객체이지만 해시코드 값이 다른것을 확인할 수 있습니다. 그 이유는 hashCode() 메서드를 재정의하지 않아서 그렇습니다.
public class Member {
    String name;
    int registrationNumber; // 주민번호

    public Member(String name, int registrationNumber) {
        this.name = name;
        this.registrationNumber = registrationNumber;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Member){
            return this.registrationNumber == ((Member) obj).registrationNumber;
        }
        return false;
    }

    public static void main(String[] args) {
        Member member1 = new Member("홍길동", 123);
        Member member2 = new Member("홍길동", 123);

        System.out.println(member1.equals(member2)); // true
        System.out.println(member1.hashCode()); // 234698513
        System.out.println(member2.hashCode()); // 1121172875
    }
}

 

hashCode()를 재정의한 경우


  • 아래 예제는 hashCode() 메서드를 재정의하여 주민등록번호가 같으면 같은 해시코드를 반환합니다.
public class Member {
    String name;
    int registrationNumber; // 주민번호

    public Member(String name, int registrationNumber) {
        this.name = name;
        this.registrationNumber = registrationNumber;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Member){
            return this.registrationNumber == ((Member) obj).registrationNumber;
        }
        return false;
    }

    @Override
    public int hashCode() {
        return this.registrationNumber;
    }

    public static void main(String[] args) {
        Member member1 = new Member("홍길동", 123);
        Member member2 = new Member("홍길동", 123);

        System.out.println(member1.equals(member2)); // true
        System.out.println(member1.hashCode()); // 123
        System.out.println(member2.hashCode()); // 123
    }
}

 

toString()


  • toString() 메서드는 객체의 문자 정보를 반환합니다.
  • 객체의 문자 정보라함은 객체를 문자열로 표현한 값인데 기본적으로 클래스명@16진수해시코드로 구성된 문자정보입니다.
Object obj = new Object();
System.out.println(obj.toString()); // java.lang.Object@de6ced

toString() 재정의한 경우


public class Member {
    String name;
    int registrationNumber; // 주민번호

    public Member(String name, int registrationNumber) {
        this.name = name;
        this.registrationNumber = registrationNumber;
    }

    @Override
    public String toString() {
        return "이름 : " + this.name + ", 주민번호 : " + this.registrationNumber;
    }

    public static void main(String[] args) {
        Member member = new Member("홍길동", 123);

        System.out.println(member.toString()); // 이름 : 홍길동, 주민번호 : 123
    }
}

 

clone()


  • 객체 복제는 원본 객체의 필드값과 동일한 값을 가지는 새로운 객체를 생성하는 것입니다.
  • 어떠한 객체를 복제할려면 이 객체가 복사 가능한 객체라는 사실을 JVM에게 알려줘야하는데 이때 Cloneable이라는 인터페이스를 구현하면 됩니다.
  •  Cloneable인터페이스는 객체가 복제 가능한 객체라는 사실을 JVM에게 알려주는 구분자에 불과합니다. 즉  Cloneable은 일종의 약속입니다.
  • 객체지향 프로그래밍의 정보은닉에 위배될 가능성이 있으므로 복제할 객체는 Cloneable인터페이스를 명시해야합니다.

clone() 재정의한 경우


  • Cloneable 인터페이스를 implements해도 구현해야할 메서드는 없습니다. 이런 인터페이스를 마크 인터페이스라고 합니다.
  • 복제된 객체는 Object로 반환되므로 명시적인 캐스팅이 필요합니다.
  • 메모리가 복제되면서 CloneNotSupportedException이 발생할 수 있으므로 예외처리를 해주어야 합니다.
public class Member implements Cloneable{
    String name;
    int registrationNumber; // 주민번호

    public Member(String name, int registrationNumber) {
        this.name = name;
        this.registrationNumber = registrationNumber;
    }

    @Override
    public String toString() {
        return "이름 : " + this.name + ", 주민번호 : " + this.registrationNumber;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Member member1 = new Member("홍길동", 123);
        Member member2 = (Member)member1.clone();

        System.out.println(member1.toString()); // 이름 : 홍길동, 주민번호 : 123
        System.out.println(member2.toString()); // 이름 : 홍길동, 주민번호 : 123
    }
}

 

finalize()


  • 객체 소멸자(finalize())는 자바에서 참조하지 않는 배열이나 객체를 가바지 컬렉터가 힙영역에서 자동으로 소멸시킵니다.
  • GC는 객체를 소멸하기 직전에 마지막으로 객체의 소멸자를 실행시킵니다.
  • 많은 자바 전문가들이 이 메서드 사용을 여러가지 이유로 만류하고 있기 때문에 직정 구현하는 일은 상당히 적어보입니다.
  • 언제 실행될지 알수가 없으므로 실행을 보장하지 않습니다.
  • 성능을 저하시킵니다.
  • 예외 발생 시 무시가 됩니다. 보통 예외가 발생하면 stack trace가 출력되지만 finalize 내에선 무시되고 처리됩니다.

 

728x90
반응형