📌 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 == p2
는 false
, p1.hashCode() == p2.hashCode()
는 true
인 이유
1️⃣ ==
연산자와 hashCode()
의 차이
✅ ==
연산자는 객체의 메모리 주소(참조 값)를 비교
✅ hashCode()
는 객체의 내용(값)에 따라 계산된 정수값을 반환
즉, ==
는 객체가 같은 메모리 주소를 가리켜야 true
가 됨,
반면 hashCode()
는 동일한 데이터가 저장되어 있으면 같은 해시코드를 가질 수 있음.
2️⃣ p1
과 p2
는 서로 다른 객체(주소가 다름)
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
📌 new
키워드를 사용하면 항상 새로운 객체가 생성됨.
📌 즉, p1
과 p2
는 메모리에서 다른 위치를 차지하고 있음.
✅ 메모리 주소 비교
System.out.println(p1 == p2); // false
📌 p1
과 p2
는 다른 메모리 공간을 차지하고 있으므로 ==
비교 시 false
가 출력됨.
3️⃣ hashCode()
는 같은 값이 나오지만, 객체 주소는 다름
우리가 hashCode()
를 다음과 같이 오버라이딩했기 때문:
@Override
public int hashCode() {
return name.hashCode() + age;
}
✅ 해시코드 비교
System.out.println(p1.hashCode() == p2.hashCode()); // true
✔ p1.name
과 p2.name
이 "Alice"
로 같으므로 name.hashCode()
값이 같음.
✔ p1.age
와 p2.age
도 25
로 같으므로, 최종 hashCode()
값이 같음.
✔ 따라서 hashCode()
비교는 true
를 반환.
하지만 p1
과 p2
는 여전히 다른 객체(메모리 주소 다름).
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()
을 사용하기 위한 조건
- 클래스가
Cloneable
인터페이스를 구현해야 함. clone()
메서드를public
으로 오버라이딩해야 함.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();
을 호출해야 정상적으로 동작함.
📌 객체가 메모리에서 복제되지만, p1
과 p2
는 서로 다른 메모리 주소를 가짐.
📌 만약 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이 관리하므로 보장되지 않음.
📌 대신 AutoCloseable
과 try-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()
가 호출될 때 다시 실행됨.
📌 멀티스레딩에서 객체 간의 실행 순서를 제어할 때 Object
의 wait()
과 notify()
를 사용함.
4️⃣ getClass()
를 활용한 리플렉션(Reflection)
✔ Object
의 getClass()
메서드를 사용하면, 런타임에 클래스 정보를 가져올 수 있음.
✔ 리플렉션(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 |