자바

Object 클래스란?

Blue_bull 2025. 2. 9. 13:03

📌 Object 클래스란?

Object 클래스는 자바에서 모든 클래스의 최상위 부모 클래스로, 자바의 모든 클래스는 자동으로 Object 클래스를 상속받음.

public class Test { 
    // 자동으로 extends Object 가 포함됨
}

📌 개발자가 extends Object를 명시하지 않아도 자동으로 상속됨.


1️⃣ Object 클래스가 하는 역할

(1) 모든 클래스가 공통적으로 가져야 할 기능 제공

  • 모든 객체에서 공통적으로 필요한 기능을 제공하여, 표준화된 동작을 할 수 있도록 설계됨.
  • 예를 들어, 객체 비교(equals()), 해시 코드(hashCode()), 문자열 변환(toString()) 등의 기능이 있음.

(2) 다형성(Polymorphism) 지원

  • Object 타입을 사용하면 어떤 클래스의 객체든 다룰 수 있음.
    Object obj = new String("Hello");
    Object obj2 = new Integer(100);
    📌 모든 클래스는 Object로 업캐스팅 가능.

2️⃣ Object 클래스의 주요 메서드

메서드 역할 기본 동작
equals(Object obj) 두 객체가 같은지 비교 == 연산자와 동일 (오버라이딩 가능)
hashCode() 객체의 해시코드 반환 기본적으로 메모리 주소 기반
toString() 객체를 문자열로 변환 "클래스이름@해시코드" 형식
getClass() 객체의 클래스 정보를 반환 Class<?> 타입 반환
clone() 객체를 복제 Cloneable 인터페이스 필요
finalize() 가비지 컬렉션 실행 전 호출됨 현재는 거의 사용되지 않음

3️⃣ 주요 메서드 동작 예제

(1) equals() → 객체 비교

  • 기본적으로 == 연산자와 같은 참조(주소) 비교를 수행하지만, 오버라이딩하면 값 비교 가능.

    class Person {
      String name;
    
      public Person(String name) {
          this.name = name;
      }
    
      @Override
      public boolean equals(Object obj) {
          if (this == obj) return true;
          if (obj == null || getClass() != obj.getClass()) return false;
          Person person = (Person) obj;
          return name.equals(person.name);
      }
    }
    

public class Main {
public static void main(String[] args) {
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");

    System.out.println(p1.equals(p2)); // true (값 비교)
    System.out.println(p1 == p2);      // false (주소 비교)
}

}

📌 `==`은 **메모리 주소 비교**, `equals()`는 **값 비교**를 하도록 오버라이딩 가능.

---

### ✅ **(2) `hashCode()` → 객체를 숫자로 변환**
- 해시 기반 자료구조(`HashMap`, `HashSet`)에서 빠르게 검색하기 위해 사용됨.
```java
@Override
public int hashCode() {
    return name.hashCode(); // String의 hashCode() 사용
}

📌 같은 값을 가지는 객체는 같은 hashCode()를 반환하도록 구현해야 함.


(3) toString() → 객체를 문자열로 변환

  • 기본 toString()"클래스이름@해시코드" 형식으로 출력됨.
    Person p = new Person("Alice");
    System.out.println(p.toString()); // 기본: Person@5e91993f
    ✔ 오버라이딩하면 의미 있는 문자열 반환 가능:
    @Override
    public String toString() {
      return "Person{name='" + name + "'}";
    }
    출력:
    Person{name='Alice'}

(4) getClass() → 클래스 정보 가져오기

  • 런타임 시 객체의 클래스 정보를 확인할 수 있음.
    Person p = new Person("Alice");
    System.out.println(p.getClass().getName()); // 출력: Person

4️⃣ Object 클래스를 상속받는 이유

(1) 모든 객체가 공통적인 기능(equals(), hashCode() 등)을 사용할 수 있도록 표준화

  • 모든 객체에서 공통적으로 필요한 기능을 Object 클래스에서 제공함.

(2) 다형성(Polymorphism) 지원

  • Object 타입을 사용하면 어떤 객체든 저장 가능.
    Object obj1 = "Hello";
    Object obj2 = new Integer(10);
    Object obj3 = new ArrayList<>();
    📌 어떤 객체든 Object 타입으로 다룰 수 있음.

(3) 컬렉션 프레임워크에서 Object 타입 활용

  • ArrayList, HashMap 등의 컬렉션 클래스는 내부적으로 Object 타입을 사용하여 모든 타입의 객체를 저장 가능.
    ArrayList<Object> list = new ArrayList<>();
    list.add("Alice");
    list.add(10);
    list.add(new Person("Bob"));
    📌 모든 객체를 저장할 수 있음.

5️⃣ Object 클래스가 자동 상속되는 이유

이유 설명
표준화된 메서드 제공 equals(), hashCode(), toString()
다형성(Polymorphism) 지원 모든 객체가 Object 타입으로 다뤄질 수 있음
컬렉션 활용 가능 ArrayList, HashMap 등에서 다양한 타입 저장 가능
리플렉션(Reflection) 활용 getClass()를 통해 런타임 클래스 정보 확인 가능

🚀 핵심 요약

Object모든 클래스의 최상위 부모 클래스이며, 자동으로 상속됨.
equals(), hashCode(), toString()모든 객체가 가져야 할 기능을 제공.
Object 타입을 사용하면 모든 객체를 다룰 수 있는 다형성을 제공.
✅ 컬렉션(ArrayList, HashMap 등)에서 모든 타입을 저장할 수 있도록 Object 타입을 활용.

👉 자바의 모든 객체가 공통적인 동작을 가질 수 있도록 Object 클래스를 자동 상속하도록 설계한 것! 🚀


1️⃣ Object 클래스가 제공하는 핵심 기능

📌 Object 클래스에는 모든 객체가 기본적으로 가져야 하는 메서드들이 정의됨.

(1) equals() → 두 객체가 같은지 비교하는 기능

public boolean equals(Object obj) {
    return (this == obj);
}

✔ 기본 구현은 == 연산자(주소 비교)지만, equals()를 오버라이딩하면 객체의 내용 비교 가능.


(2) hashCode() → 객체의 해시코드 반환 (데이터 구조에서 활용)

public native int hashCode();

HashMap, HashSet 등의 컬렉션에서 객체를 빠르게 찾기 위해 사용됨.
equals()를 오버라이딩하면 hashCode()도 함께 오버라이딩해야 함.


(3) toString() → 객체 정보를 문자열로 반환

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

✔ 기본 구현은 "클래스이름@해시코드" 형태지만, 오버라이딩하면 의미 있는 값 반환 가능.


(4) getClass() → 객체의 클래스 정보를 반환

public final native Class<?> getClass();

✔ 런타임 시 객체의 클래스 정보를 가져올 때 사용됨.


(5) clone() → 객체 복사 (깊은/얕은 복사)

protected native Object clone() throws CloneNotSupportedException;

Cloneable 인터페이스를 구현한 경우 사용 가능.
clone()을 오버라이딩하여 깊은 복사(Deep Copy) 가능.


(6) finalize() → 객체가 GC(가비지 컬렉션) 되기 전 호출됨

protected void finalize() throws Throwable { }

✔ 현재는 거의 사용되지 않는 메서드(GC가 호출 시점을 보장하지 않기 때문).
✔ 대신 try-with-resources 문법이나 AutoCloseable 인터페이스 사용 권장.


2️⃣ Object 클래스가 자동 상속됨으로써 얻는 이점

객체 비교 (equals(), hashCode())를 표준화하여 컬렉션에서 활용 가능
객체를 문자열로 표현 (toString())할 수 있어 디버깅이 편리
클래스 정보 (getClass())를 얻을 수 있어 리플렉션(Reflection) 활용 가능
객체 복제 (clone()) 기능 제공
모든 객체가 Object 타입으로 다룰 수 있어 다형성(Polymorphism) 지원


3️⃣ Object 클래스를 상속받아 활용하는 예제

class Person {
    String name;
    int age;

    // 생성자
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // equals() 오버라이딩: 내용 비교
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return age == person.age && name.equals(person.name);
    }

    // hashCode() 오버라이딩: 같은 객체는 같은 해시코드를 가져야 함
    @Override
    public int hashCode() {
        return name.hashCode() + age;
    }

    // toString() 오버라이딩: 의미 있는 정보 출력
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("Alice", 25);
        Person p2 = new Person("Alice", 25);

        // equals() 비교
        System.out.println(p1.equals(p2)); // true (내용이 같으므로)

        // hashCode() 비교
        System.out.println(p1.hashCode() == p2.hashCode()); // true (내용이 같으므로 같은 해시코드)

        // toString() 결과 출력
        System.out.println(p1.toString()); // Person{name='Alice', age=25}
    }
}

📌 equals(), hashCode(), toString()을 오버라이딩하면 객체 비교 및 출력이 의미 있게 동작함.


📌 결론: Object 클래스를 자동 상속하는 이유

✅ 모든 객체가 공통적으로 가져야 할 기능을 표준화 (equals(), hashCode(), toString() 등)
✅ 컬렉션 API와 연동하여 데이터 구조에서 활용 가능 (HashMap, HashSet 등)
✅ 다형성을 활용할 수 있도록 지원 (Object 타입으로 모든 객체를 받을 수 있음)
✅ 자바의 객체지향 철학에 맞춰 설계되었으며, 상속을 통해 강력한 기능을 제공

👉 결론적으로, Object 클래스를 자동으로 상속하게 설계한 것은 "객체를 효과적으로 관리하고, 표준화된 기능을 제공"하기 위해서야! 🚀

📌 p1 == p2false, p1.hashCode() == p2.hashCode()true인 이유


1️⃣ == 연산자와 hashCode()의 차이

== 연산자는 객체의 메모리 주소(참조 값)를 비교
hashCode()객체의 내용(값)에 따라 계산된 정수값을 반환

즉, ==객체가 같은 메모리 주소를 가리켜야 true가 됨,
반면 hashCode()동일한 데이터가 저장되어 있으면 같은 해시코드를 가질 수 있음.


2️⃣ p1p2는 서로 다른 객체(주소가 다름)

Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);

📌 new 키워드를 사용하면 항상 새로운 객체가 생성됨.
📌 즉, p1p2는 메모리에서 다른 위치를 차지하고 있음.

메모리 주소 비교

System.out.println(p1 == p2); // false

📌 p1p2다른 메모리 공간을 차지하고 있으므로 == 비교 시 false가 출력됨.


3️⃣ hashCode()는 같은 값이 나오지만, 객체 주소는 다름

우리가 hashCode()를 다음과 같이 오버라이딩했기 때문:

@Override
public int hashCode() {
    return name.hashCode() + age;
}

해시코드 비교

System.out.println(p1.hashCode() == p2.hashCode()); // true

p1.namep2.name"Alice"로 같으므로 name.hashCode() 값이 같음.
p1.agep2.age25로 같으므로, 최종 hashCode() 값이 같음.
✔ 따라서 hashCode() 비교는 true를 반환.

하지만 p1p2는 여전히 다른 객체(메모리 주소 다름).


4️⃣ 결론

비교 방식 의미 결과
p1 == p2 메모리 주소 비교 (같은 객체인가?) false
p1.hashCode() == p2.hashCode() 객체의 내용 기반 해시코드 비교 true
p1.equals(p2) 객체의 값이 같은가? (equals() 오버라이딩 기준) true

🚀 핵심 요약

==메모리 주소가 같아야 true (동일 객체일 때만 가능)
hashCode()객체의 속성을 바탕으로 계산된 정수 값
hashCode()가 같아도, 객체가 다르면 == 비교는 false가 됨.

👉 "객체의 메모리 주소는 다르지만, 같은 값을 가지고 있어서 hashCode()는 같게 나오는 것!" 🚀

📌 Object 클래스에 의해 암시적으로 이루어지는 더 알아야 할 내용

이미 Object 클래스의 주요 기능과 자동 상속, 암시적 코드들에 대해 잘 정리했지만, 더 깊이 알아야 할 중요한 추가 개념이 있어.


1️⃣ Object 클래스의 clone() 메서드가 동작하는 방식

clone() 메서드는 객체를 복제(Clone) 하는 기능을 제공하지만, 그냥 사용할 수 있는 것이 아니라 특정 조건을 충족해야 정상 동작함.
암시적으로 Cloneable 인터페이스를 구현한 경우에만 사용 가능.

(1) clone()을 사용하기 위한 조건

  1. 클래스가 Cloneable 인터페이스를 구현해야 함.
  2. clone() 메서드를 public으로 오버라이딩해야 함.
  3. super.clone()을 호출해야 함.
class Person implements Cloneable {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person p1 = new Person("Alice", 25);
        Person p2 = (Person) p1.clone(); // 복제

        System.out.println(p1); // Person{name='Alice', age=25}
        System.out.println(p2); // Person{name='Alice', age=25}
        System.out.println(p1 == p2); // false (메모리 주소 다름)
    }
}

📌 clone()super.clone();을 호출해야 정상적으로 동작함.
📌 객체가 메모리에서 복제되지만, p1p2는 서로 다른 메모리 주소를 가짐.
📌 만약 Cloneable을 구현하지 않으면 CloneNotSupportedException 예외가 발생함.


2️⃣ finalize()는 가비지 컬렉터(GC)와 관련 있음

Object 클래스의 finalize()객체가 가비지 컬렉션(GC)에 의해 제거되기 전에 실행되는 메서드.
✔ 하지만 현재는 거의 사용되지 않음 (자바 9에서 Deprecated).

(1) finalize() 기본 예제

class Person {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("객체가 GC에 의해 제거됨: " + this);
    }
}

public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        p = null; // 참조 제거
        System.gc(); // 강제 GC 실행 요청

        System.out.println("메인 메서드 종료");
    }
}

📌 실행 결과:

메인 메서드 종료
객체가 GC에 의해 제거됨: Person@xxxxxx

📌 객체가 제거될 때 finalize()가 호출되지만, 정확한 실행 시점은 JVM이 관리하므로 보장되지 않음.
📌 대신 AutoCloseabletry-with-resources를 사용해서 자원을 정리하는 것이 권장됨.


3️⃣ Object 클래스의 wait(), notify(), notifyAll()

Object 클래스에는 멀티스레딩에서 동기화(스레드 간의 실행 순서 조정)를 위한 wait(), notify(), notifyAll() 메서드가 포함됨.
✔ 모든 객체는 자동으로 Object 클래스를 상속받기 때문에, 모든 객체에서 wait()notify()를 사용할 수 있음.

(1) wait()notify()의 기본 동작

  • wait() → 현재 스레드를 일시 정지하고 다른 스레드의 notify()를 기다림.
  • notify() → 대기(wait) 중인 스레드 하나를 깨움.
  • notifyAll() → 대기 중인 모든 스레드를 깨움.
class SharedResource {
    synchronized void printMessage() {
        System.out.println(Thread.currentThread().getName() + " 실행 중...");
        try {
            wait(); // 현재 스레드 대기
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 다시 실행됨!");
    }

    synchronized void wakeUp() {
        notify(); // 대기 중인 스레드 하나 깨우기
    }
}

public class Main {
    public static void main(String[] args) {
        SharedResource resource = new SharedResource();

        Thread t1 = new Thread(resource::printMessage, "Thread-1");
        Thread t2 = new Thread(resource::wakeUp, "Thread-2");

        t1.start();
        try { Thread.sleep(1000); } catch (InterruptedException e) {} // t1이 먼저 실행되도록 대기
        t2.start();
    }
}

📌 실행 결과:

Thread-1 실행 중...
Thread-1 다시 실행됨!

📌 wait()가 실행되면 스레드가 멈추고, notify()가 호출될 때 다시 실행됨.
📌 멀티스레딩에서 객체 간의 실행 순서를 제어할 때 Objectwait()notify()를 사용함.


4️⃣ getClass()를 활용한 리플렉션(Reflection)

ObjectgetClass() 메서드를 사용하면, 런타임에 클래스 정보를 가져올 수 있음.
리플렉션(Reflection)을 활용하면, 동적으로 클래스를 분석하고 객체를 다룰 수 있음.

(1) getClass()를 사용한 클래스 정보 출력

class Person {}

public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        System.out.println("클래스 이름: " + p.getClass().getName()); // Person
    }
}

📌 getClass().getName()을 사용하면 객체의 클래스 정보를 런타임에 확인 가능.


(2) 리플렉션을 활용하여 동적으로 메서드 호출

import java.lang.reflect.Method;

class Example {
    public void showMessage() {
        System.out.println("안녕하세요!");
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Example obj = new Example();
        Class<?> cls = obj.getClass();

        // "showMessage"라는 메서드를 찾아 실행
        Method method = cls.getMethod("showMessage");
        method.invoke(obj);
    }
}

📌 실행 결과:

안녕하세요!

📌 리플렉션을 사용하면, 컴파일 타임이 아니라 런타임에 메서드를 동적으로 실행할 수 있음.
📌 플러그인 시스템, 프레임워크(Spring, Hibernate)에서 많이 사용됨.


📌 추가로 알아야 할 Object의 암시적 기능

기능 설명
clone() 객체 복사 (Cloneable 인터페이스 필요)
finalize() 가비지 컬렉션(GC) 전에 실행됨 (거의 사용되지 않음)
wait() / notify() 멀티스레딩에서 스레드 동기화
getClass() 런타임에 클래스 정보 조회
리플렉션(Reflection) Class.forName(), Method.invoke() 등으로 클래스 동적 분석

🚀 최종 정리

Object 클래스에는 멀티스레딩(wait()/notify()), 객체 복제(clone()), 클래스 정보(getClass()) 등의 기능이 포함됨.
finalize()거의 사용되지 않으며, 대신 try-with-resources를 사용해야 함.
getClass()를 활용한 리플렉션(Reflection)동적 프로그래밍 및 프레임워크에서 필수적인 기술.

👉 이제 Object 클래스를 완벽하게 이해했어! 🚀

'자바' 카테고리의 다른 글

다형성이란?  (0) 2025.02.09
상속(Inheritance)이란?  (0) 2025.02.09
super() 정리2  (0) 2025.02.09
매개변수가 없는 메서드와 매개변수가 없는 생성자 비교를 통한 생성자 이해  (0) 2025.02.08
final 키워드 특징  (0) 2025.02.08