들어가며
Java에서 모든 클래스는 자동으로 Object 클래스를 상속받습니다.
우리가 직접 extends를 쓰지 않아도, 컴파일러가 자동으로 처리합니다.
이 글에서는 Object 클래스가 무엇인지, 왜 모든 객체를 담을 수 있는지, 다형성과 어떤 관계인지 정리해보겠습니다.
Object 클래스란?
Object는 Java의 최상위(root) 클래스입니다.
Java의 모든 클래스는 명시적으로 extends를 쓰지 않아도 자동으로 Object를 상속받습니다.
// 우리가 작성하는 코드
class Member {
String name;
}
// 컴파일러가 실제로 처리하는 코드
class Member extends Object {
String name;
}
즉, Java의 모든 클래스는 Object의 자식 클래스입니다.
Object ← 최상위 클래스
/ \
Animal Member ← 모든 클래스가 Object 상속
/ \
Dog Cat
모든 객체를 담을 수 있다
Object가 최상위 클래스이기 때문에, 업캐스팅을 통해 어떤 객체든 Object 타입으로 받을 수 있습니다.
Object obj1 = new Member(); // Member 객체
Object obj2 = new Dog(); // Dog 객체
Object obj3 = "hello"; // String 객체
Object obj4 = 123; // Integer 객체 (오토박싱)
Object[] arr = { new Member(), new Dog(), "hello", 123 };
타입이 전혀 다른 객체들을 하나의 배열이나 변수에 담을 수 있습니다.
다형성과의 관계
Object는 모든 클래스의 부모이기 때문에 가장 넓은 범위의 다형성을 제공합니다.
어떤 타입이 올지 모를 때 Object를 사용해 유연하게 처리할 수 있습니다.
// 어떤 객체든 출력하는 메서드
public static void printInfo(Object obj) {
System.out.println(obj.toString());
}
printInfo(new Member("홍길동", 25)); // Member 전달
printInfo(new Dog()); // Dog 전달
printInfo("hello"); // String 전달
매개변수를 Object로 받으면 어떤 타입이든 받을 수 있습니다.
Object의 주요 메서드
Object 클래스에는 모든 객체가 공통으로 사용할 수 있는 메서드가 정의되어 있습니다.
| 메서드 | 설명 |
|---|---|
toString() |
객체를 문자열로 표현 |
equals(Object obj) |
객체 동등성 비교 |
hashCode() |
객체의 해시 코드 반환 |
getClass() |
객체의 클래스 정보 반환 |
toString()
기본 구현은 클래스명@해시코드 형식으로 출력됩니다.
의미 있는 정보를 출력하려면 오버라이딩해야 합니다.
class Member {
String name;
int age;
Member(String name, int age) {
this.name = name;
this.age = age;
}
// 오버라이딩 전
// toString() → "Member@1b6d3586"
@Override
public String toString() {
return "Member{name=" + name + ", age=" + age + "}";
}
}
Member m = new Member("홍길동", 25);
System.out.println(m); // "Member{name=홍길동, age=25}"
equals()
기본 구현은 ==와 같이 주소를 비교합니다.
내용을 비교하려면 오버라이딩해야 합니다.
class Member {
String name;
int age;
Member(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Member)) return false;
Member other = (Member) obj;
return this.name.equals(other.name) && this.age == other.age;
}
}
Member m1 = new Member("홍길동", 25);
Member m2 = new Member("홍길동", 25);
System.out.println(m1 == m2); // false - 주소 비교
System.out.println(m1.equals(m2)); // true - 내용 비교
getClass()
실행 중에 객체의 실제 타입을 확인할 수 있습니다.
Object obj = new Member("홍길동", 25);
System.out.println(obj.getClass()); // class Member
System.out.println(obj.getClass().getName()); // "Member"
Object와 제네릭
Object로 모든 타입을 담을 수 있지만, 꺼낼 때 다운캐스팅이 필요하고 타입 안전성이 낮습니다.
이 문제를 해결하기 위해 Java 5부터 제네릭(Generic) 이 도입됐습니다.
// Object 사용 - 다운캐스팅 필요, 타입 오류 런타임에 발견
Object obj = new Member("홍길동", 25);
Member m = (Member) obj; // 명시적 형변환 필요
// 잘못된 타입으로 꺼내면 런타임 오류
Object obj2 = "hello";
Member m2 = (Member) obj2; // ClassCastException!
// 제네릭 사용 - 타입 안전, 형변환 불필요
List<Member> list = new ArrayList<>();
list.add(new Member("홍길동", 25));
Member member = list.get(0); // 형변환 없이 바로 사용
| 구분 | Object 사용 | 제네릭 사용 |
|---|---|---|
| 타입 안전성 | 낮음 (런타임 오류 가능) | 높음 (컴파일 시 오류 발견) |
| 형변환 | 다운캐스팅 필요 | 불필요 |
| 유연성 | 모든 타입 수용 | 지정한 타입만 수용 |
정리
Object는 Java의 최상위 클래스로, 모든 클래스는 자동으로Object를 상속받는다.Object타입으로 어떤 객체든 담을 수 있어 가장 넓은 범위의 다형성을 제공한다.toString(),equals(),hashCode(),getClass()등 공통 메서드를 제공하며, 필요에 따라 오버라이딩해서 사용한다.- Object로 모든 타입을 처리하면 다운캐스팅이 필요하고 타입 안전성이 낮아지므로, 타입이 정해진 경우에는 제네릭을 사용하는 것이 좋다.
'-- 오늘 있었던 개발 일기' 카테고리의 다른 글
| static 메서드란? - 객체 생성 없이 바로 호출 (0) | 2026.04.09 |
|---|---|
| 참조변수는 주소를 가져야 한다! (0) | 2026.04.08 |
| 상대경로와 절대경로 (0) | 2026.04.04 |
| 스트림(Stream)이란? - 입출력과 병렬처리 (0) | 2026.04.02 |
| 추상 메서드(Abstract Method)란? (0) | 2026.03.31 |