Object 클래스
자바에는 여러 클래스들이 존재한다.
자바에서 모든 객체의 최종 부모는 Object이다 (메시아 ㄷㄷ)
자바에서 Object 클래스가 최상위 부모 클래스인 이유
- 공통 기능 제공
- 다형성의 기본 구현
Object가 제공하는 공통 기능
- 객체의 정보를 제공하는 toString()
- 객체의 같음을 비교하는 equlas()
- 객체의 클래스 정보를 제공하는 getClass()
- 기타 여러가지 기능
다형성의 기본 구현
- 부모는 자식을 담을 수 있다. Obejct는 모든 클래스의 부모 클래스. 따라서 모든 객체를 참조할 수 있다.
- Object는 모든 객체를 다 담을 수 있다 ex) Object obj = new Child();
-Object 다형성
public class ObjectPolyExample1 {
public static void main(String[] args) {
Dog dog = new Dog();
Car car = new Car();
action(dog);
action(car);
}
private static void action(Object obj) {
//obj.sound(); //컴파일 오류, Object에는 sound()가 없다.
//obj.move(); //컴파일 오류, Obeject에는 move()가 없다.
//객체에 맞는 다운 캐스팅 필요
if (obj instanceof Dog dog) {
dog.sound();
}else if (obj instanceof Car car) {
car.move();
}
}
}
부모는 자식을 담을 수 있다. 즉 action메서드에 obj 매개변수는 어떤 객체든지 인자로 전달 가능하다
- action(dog);, action(car);
단, 자식은 부모를 담을 수 없다. Object에는 sound()가 없고, Object는 최종 부모이므로 더는 올라가서 찾을 수 없다.
그래서 Dog 인스턴스의 sound()를 호출하려면 다운 캐스팅을 해야한다.
이전에 배웠던 다형적 참조와, 한계를 생각해보면 같은 원리인 느낌이다.
다형적 참조는 가능하지만, 메서드 오버라이딩(다른 객체의 메서드가 정의되있지 않기 때문)이 안되기 때문에 다형성을 활용하기에는 한계가 있다. 그럼 Object를 언제 활용하면 좋을까?
-Object 배열
public class ObjectPolyExample2 {
public static void main(String[] args) {
Dog dog = new Dog();
Car car = new Car();
Object object = new Object();
Object[] objects = {dog, car, object};
size(objects);
}
private static void size(Object[] objects) {
System.out.println("전달된 객체의 수는" + objects.length);
}
}

-Object가 없다면?
- void action(Object obj)와 같이 모든 객체를 받을 수 있는 메서드를 만들 수 없다.
- Object [] objects 처럼 모든 객체를 저장할 수 있는 배열을 만들 수 없다.
물론, 직접 obj라는 부모 클래스를 만들고, 모든 클래스에서 obj를 상속받으면 된다.
하지만 하나의 프로젝트를 넘어서 전세계 모든 개발자가 비슷한 클래스를 만들 것이고, 호환의 문제가 발생하게 된다.
직접 내가 작성해봐야겠다!!
- obj라는 빈 클래스를 생성. (하지만 이 클래스 또한 최상위 클래스 Object 클래스 상속받고 있음!)
- Dog과 Car는 obj를 상속.
- 그 이후 main()에서 처리.
public class ObjectPolyExample2 {
public static void main(String[] args) {
Dog dog = new Dog();
Car car = new Car();
Obj obj = new Obj(); //
Obj[]objs = {dog, car,obj};
size(objs);
}
private static void size(Obj[] objs) {
System.out.println("전달된 객체의 수는" + objs.length);
}
}
이렇게 처리해야된다.
즉 , 이러한 것 때문에 별도의 공통 상위 클래스를 만들지 않아도 모든 클래스는 Object 클래스를 묵시적으로 상속 받기 때문에, Object를 통해 모든 객체를 다룰 수 있다.
-toString()
Object.toString() 메서드는 객체의 정보를 문자열 형태로 제공한다. 그래서 디버깅과 로깅에 유용하게 사용된 다.
이 메서드는 Object 클래스에 정의되므로 모든 클래스에서 상속받아 사용할 수 있다.
public class ToStringMain1 {
public static void main(String[] args) {
Object object = new Object();
String str = object.toString();
//to.String() 반환값 출력
System.out.println(str);
//object 직접 추력
System.out.println(object);
}
}
//실행 값:
java.lang.Object@36baf30c
java.lang.Object@36baf30c
이처럼 같은 실행 결과가 나오는 걸 알 수 있었다.
Println을 사용할 때, toString() 을 직접 호출할 필요 없이 객체를 바로 전달하면 객체의 정보를 출력 할 수 있다.
toString()오버라이딩 하지만 Object.String() 메서드가 클래스 정보와 참조값을 제공하지만,
이 정보(java.lang.Object@36baf30c) 만으로는 객체의 상태를 적절히 나타내지 못한다.
그래서 보통 toString() 을 재정의(오버라이딩)해서 보다 유용한 정보를 제공하는 것이 일반적이다.
간단한 예제로 이해하자.
1.Car,Dog,ObjectPrinter Class
public class Car {
private String carName;
public Car(String carName) {
this.carName = carName;
}
}
----------------------------------------------------------
public class Dog {
private String dogName;
private int age;
public Dog(String dogName, int age) {
this.dogName = dogName;
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"dogName='" + dogName + '\'' +
", age=" + age +
'}';
}
}
--------------------------------------------------------------
public class ObjectPrinter {
public static void print(Object obj) {
String string = "객체 정보 출력 " + obj.toString();
System.out.println(string);
}
}
Car,Dog 클래스를 각자의 이름을 받는 변수를 설정하여 생성자를 만들었고,
Dog 클래스에는 toString()메서드를 오버라이딩하였다.
2.Main Class
public class ToStringMain2 {
public static void main(String[] args) {
Car car = new Car("Model Y");
Dog dog1 = new Dog("멍멍이1", 2);
Dog dog2 = new Dog("멍멍이2", 5);
System.out.println("1.단순 toString 호출 ");
System.out.println(car.toString());
System.out.println(dog1.toString());
System.out.println(dog2.toString());
System.out.println("2.prinln 내부에서 toString 호출 ");
System.out.println(car);
System.out.println(dog1);
System.out.println(dog2);
System.out.println("3.Object 다형성 활용 ");
ObjectPrinter.print(car);
ObjectPrinter.print(dog1);
ObjectPrinter.print(dog2);
}
}
실행값 :
1.단순 toString 호출
lang.object.tostring.Car@5ca881b5
Dog{dogName='멍멍이1', age=2}
Dog{dogName='멍멍이2', age=5}
2.prinln 내부에서 toString 호출
lang.object.tostring.Car@5ca881b5
Dog{dogName='멍멍이1', age=2}
Dog{dogName='멍멍이2', age=5}
3.Object 다형성 활용
객체 정보 출력 lang.object.tostring.Car@5ca881b5
객체 정보 출력 Dog{dogName='멍멍이1', age=2}
객체 정보 출력 Dog{dogName='멍멍이2', age=5}
1,2 의 결과
- 재정의 하지 않는 Car클래스의 toSring메서드를 오버라이딩 하지 않았기 때문에 참조값만 찍히는 것을 알 수 있었다.
3의 결과
- 마찬가지로 재정의 된 ObjectPrinter.print의 메서드로 간 이후, toString() 메서드가 Dog class에서 재정의 되었기 때문에 오버라이딩 된 것이 우선적으로 호출 됨에 따라 Dog 인스턴스의 toString() 메서드가 호출 된 것을 알 수 있다.
*반대로 객체의 참조값을 직접 출력하고싶다면?
String refvalue =Integer.toHexString(System.identityHashCode(dog1));
System.out.println(refvalue);
String refvalue2 =Integer.toHexString(System.identityHashCode(dog2));
System.out.println(refvalue2);
참고하자.
Object와 OCP

ObjectPrinter 와 Object 를 사용하는 구조는 다형성을 매우 잘 활용하고 있다. 다형성을 잘 활용한다는 것은 다 형적 참조와 메서드 오버라이딩을 적절하게 사용한다는 뜻이다.
ObjectPrinter 의 print() 메서드와 전체 구조를 분석해보자.
- 다형적 참조: print(Object obj) , Object 타입을 매개변수로 사용해서 다형적 참조를 사용한다. Car , Dog 인스턴스를 포함한 세상의 모든 객체 인스턴스를 인수로 받을 수 있다.
- 메서드 오버라이딩: Object 는 모든 클래스의 부모이다. 따라서 Dog , Car 와 같은 구체적인 클래스는Object 가 가지고 있는 toString() 메서드를 오버라이딩 할 수 있다. 따라서 print(Object obj) 메서드는 Dog , Car 와 같은 구체적인 타입에 의존(사용)하지 않고, 추상적인 Object 타입에 의존하면서 런타임에 각 인스턴스의 toString() 을 호출할 수 있다.
-Sysytem.out.println()

자바 언어는 객체지향 언어 답게 언어 스스로도 객체지향의 특징을 매우 잘 활용한다. 우리가 지금까지 배운 toString() 메서드와 같이, 자바 언어가 기본으로 제공하는 다양한 메서드들은
개발자가 필요에 따라 오버라이딩해서 사용할 수 있도록 설계되어 있다.
equals() -1.동일성과 동등성
Object는 동등성 비교를 위한 equals() 메서드를 제공한다.
자바는 두 객체가 같다라는 표현을 2가지로 분리해서 제공한다.
- 동일성(Identity) : == 연산자를 사용해서 두 객체의 참조가 동일한 객체를 가르키고 있는지 확인
- 동등성(Equality) : equals() 메서드를 사용하여 두 객체가 논리적으로 동등한지 확인
단어 정리
“동일”은 완전히 같음을 의미. 반면 “동등”은 같은 가치나 수준을 의미하지만 그 형태나 외관 등이 완전히 같지는 않음.
쉽게 이야기해서 동일성은 물리적으로 같은 메모리에 있는 객체 인스턴스인지 참조값을 확인하는 것이고,
동등성은 논리적으로 같은지 확인하는 것!
예를 들어 같은 회원 번호를 가진 회원 객체가 2개 있다고 가정해보자
User a = new User("id-100) //참조 x001
User b = new User("id-100) //참조 x002
이 경우 물리적으로 다른 메모리에 있는 다른 객체이지만, 회원 번호를 기준으로 생각해보면 논리적으로는 같은회원으로 볼 수 있다!
문자의 경우도 마찬가지!
String s1 = "hello";
String s2 = "hello";
이 경우 물리적으로 각각의 “hello” 문자열이 다른 메모리에 존재할 수 있지만, 논리적으로는 같은 “hello”라는 문자열이다.
public class EqulasMainV1 {
public static void main(String[] args) {
UserV1 user1 = new UserV1("id-100");
UserV1 user2 = new UserV1("id-100");
System.out.println("identity =" + (user1==user2));
System.out.println("eqaulity = " + (user1.equals(user2)));
}
}
실행값 :
identity =false
eqaulity = false
분명 동등성은 논리적인 가치를 판단한다했는데 equals() 또한 false가 나왔다 왤까?
A :
동등성이라는 개념은 각각의 클래스 마다 다르다. 어떤 클래스는 주민등록번호를 기반으로 동등성을 처리할 수 있고, 어떤 클래스는 고객의 연락처를 기반으로 동등성을 처리할 수 있다. 어떤 클래스는 회원 번호를 기반으로 동등성을 처리할수 있다.
따라서 동등성 비교를 사용하고 싶으면 equals() 메서드를 재정의해야 한다.
그렇지 않으면 Object 는 동일성 비교를 기본으로 제공한다.
메서드를 재정의 해보자!
public class UserV2 {
private String id;
public UserV2(String id) {
this.id = id;
}
@Override
public boolean equals(Object obj) {
UserV2 user = (UserV2) obj; //user1. 이므로 user1이 this로 넘어온다.
return id.equals(user.id); //return user1.id.equals(user2.id);
}
// 메서드를 부른 주체가 this에 전달 ! obj=user2
}
----------------------------------------------------------------
public class EqulasMainV2 {
public static void main(String[] args) {
UserV2 user1 = new UserV2("id-100");
UserV2 user2 = new UserV2("id-100");
System.out.println("identity = " + (user1 == user2));
System.out.println("equality = " + (user1.equals(user2)));
}
}
- Object 의 equals() 메서드를 재정의했다.
- UserV2 의 동등성은 id (고객번호)로 비교한다.
- equals() 는 Object 타입을 매개변수로 사용한다. 따라서 객체의 특정 값을 사용하려면 다운캐스팅이 필요하다.
- 여기서는 현재 인스턴스( this )에 있는 id 문자열과 비교 대상으로 넘어온 객체의 id 문자열을 비교한다.
- UserV2 에 있는 id 는 String 이다. 문자열 비교는 == 이 아니라 equals() 를 사용해야 한다.
위 코드가 간단한 equals() 메서드 재정의였다면
실무에서는 이렇게 정확한 equals() (IDE 제공)을 사용한다라는 것만 참고하고 넘어가자.
@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
UserV2 userV2 = (UserV2) object;
return Objects.equals(id, userV2.id);
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
}
🙇♂️ 결론
Object 클래스 안에 내가 쓰는 엄청난 편리한 메서드들이 존재했었다.
자바 언어를 배우면서 System.out.println()을 엄청나게 적었었는데, 이것의 출처가 어디인지도 모르고 사용했었다.
자바를 배우면서 느끼는 점은 정말 어떻게 이걸 다 만들었지라는 생각이 들고 배울 때마다 한참 멀었다는 생각이 든다.
언젠가, 이 언어를 자유자재로 쓰고, 누군가가 이러한 질문을 했을 때 바로 대답이 나올 수 있을 정도로 언어를 마스터하는 날이 왔음 좋겠다!!
노래 듣고 자야겠다^^아이유 - 내 손을 JAVA ㅎㅎ.. (ㅋㅋ웃어봐요~ 웃어봐 웃어.)

'Language > ☕ Java' 카테고리의 다른 글
| [Java] String 클래스 (0) | 2024.08.23 |
|---|---|
| [Java] 불변 객체 (0) | 2024.08.20 |
| [Java] 다형성 역할과 구현 예제 (0) | 2024.08.15 |
| [Java] 다형성 2 (0) | 2024.08.14 |
| [Java] 다형성이란? (2) | 2024.08.13 |