우와한 개발자

[Java] Object 클래스(equals / hashCode / toString / 스레드)

by 우와한개발자

1. Object 클래스란?

  • Object 클래스는 자바의 모든 클래스의 최상위(root) 클래스
  • 클래스를 선언할 때 extends를 명시하지 않으면, 컴파일러가 자동으로 extends Object를 삽입
  • 즉, 자바의 모든 클래스는 직접 또는 간접적으로 Object 클래스를 상속 받음
// 아래 두 선언은 동일
public class Person { ... }
public class Person extends Object { ... }

 

2. Object 클래스의 주요 메서드

메서드 반환 타입 설명
equals(Object obj) boolean 두 객체의 내용(값) 이 같은지 비교. 재정의 필요
hashCode() int 객체의 해시코드 값 반환. equals()와 함께 재정의 필요
toString() String 객체를 문자열로 반환. 재정의하지 않으면 클래스명@해시코드 출력
clone() Object 객체를 복제하여 반환 (Cloneable 구현 필요, protected)
wait() void 현재 스레드를 대기 상태로 전환 (final)
wait(long timeout) void 지정 시간(ms) 동안 대기 (final)
wait(long timeout, int nanos) void 지정 시간(ms+ns) 동안 대기 (final)
notify() void 대기 중인 스레드 하나를 깨움 (final)
notifyAll() void 대기 중인 모든 스레드를 깨움 (final)
finalize() void GC가 객체를 메모리에서 제거하기 직전에 호출 (protected)
⚠️ wait(), notify(), notifyAll()은 final이라 재정의(Override) 불가하고 나머지 메서드는 모두 재정의 가능

 

1)  객체 동등 비교: == vs equals()

  • == 연산자 객체 변수끼리 비교할 때 주소(참조값) 를 비교(내용이 같아도 서로 다른 객체이면 false)
  • 값(내용) 을 비교하려면 equals() 메서드를 사용
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
String s4 = new String("Hello");

System.out.println(s1 == s2);        // true  (같은 주소 참조)
System.out.println(s1 == s3);        // false (다른 객체)
System.out.println(s3 == s4);        // false (다른 객체)
System.out.println(s1.equals(s2));   // true  (값 비교)
System.out.println(s1.equals(s3));   // true  (값 비교)
System.out.println(s3.equals(s4));   // true  (값 비교)

 

2) equals()와 hashCode() 재정의

  • 직접 만든 클래스에서 객체 동등 비교를 하려면 equals() 메서드를 재정의해야 함.
  • 그런데 equals()만 재정의하면 HashSet, HashMap 같은 해시 기반 자료구조에서 문제 발생하기 때문에 equals()와 hashCode() 를 둘다 재정의 해야함.
// 해시 기반 자료구조에서의 동등 비교 순서
hashCode() 값 비교
→ 다르면 → 다른 객체 (equals() 호출 안 함)
→ 같으면 → equals() 로 최종 비교 (true면 같은 객체 / false면 다른 객체)

 

3) toString()

  • Object의 기본 toString()은 클래스명@해시코드(16진수) 형태를 반환
  • 의미 있는 정보를 출력하려면 재정의해야함
⚠️ String, Integer, Date 같은 Java API 클래스는 toString()이 이미 재정의되어 있어서 값이 바로 출력

 

3. 스레드 생명주기와 관련된 Object 메서드

1) wait() / notify() / notifyAll()

  • wait(), notify(), notifyAll()은 스레드 간 통신을 위한 메서드
  • synchronized 블록 안에서만 사용 가능
  • final이라 재정의 불가
⚠️ synchronized와의 관계
- synchronized 키워드를 붙이면 해당 스레드가 lock flag 를 가지고 Runnable ↔ Running 상태를 오가며 처리하는 동안 다른 스레드가 끼어들지 못함. 이때 lock을 기다리는 스레드들이Lock Pool에서 대기하게 됨.
// synchronized 없는 버전 (문제 발생)
public void push(int i) {
    stack.add(i);  // t1이 여기까지 실행 후
    i++;           // t2가 끼어들면 stackpoint가 꼬여 데이터 깨짐!
}

// synchronized 적용 (해결)
public synchronized void push(int i) {
    stack.add(i);
    i++;
}​

메서드 이동하는 상태 설명
wait() Running → Wait Pool 락을 반납하고 무한 대기
notify() Wait Pool → Lock Pool 대기 스레드 1개를 깨워 lock 경쟁 참여
notifyAll() Wait Pool → Lock Pool 대기 중인 모든 스레드를 깨워 lock 경쟁 참여

 

2) finalize()

  • GC(Garbage Collector)가 객체를 메모리에서 제거하기 직전에 자동 호출되는 메서드
  • 파일, 네트워크 연결 등 자원 해제 용도로 재정의해서 사용 가능
  • protected라 직접 호출보다는 GC에 의해 자동 호출됨
@Override
protected void finalize() throws Throwable {
    // 객체 소멸 직전에 처리할 작업 (자원 해제 등)
    System.out.println("객체 소멸 직전 호출");
}
⚠️ finalize()는 GC가 언제 실행될지 보장이 없기 때문에 자원 해제를 finalize()에만 의존하는 것은 위험함.
- 명시적으로 자원을 해제하는 별도 메서드(예: close())를 따로 두는 것이 권장됨.
- GC를 강제로 빨리 실행시키고 싶을 때 System.gc()를 호출할 수 있지만, 이 역시 즉시 실행을 보장하지 않고 성능 저하를 유발할 수 있어 사용을 권장하지 않음.

 

블로그의 정보

우와한개발자 님의 블로그

우와한개발자

활동하기