본문 바로가기
-- 오늘 있었던 개발 일기

Object 클래스 - Java 최상위 클래스

by code study 2026. 4. 6.

들어가며

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로 모든 타입을 처리하면 다운캐스팅이 필요하고 타입 안전성이 낮아지므로, 타입이 정해진 경우에는 제네릭을 사용하는 것이 좋다.