[Java] Object 클래스

szlee·2024년 8월 13일
0

Java

목록 보기
24/34

< 김영한의 실전 자바 - 중급편 > 강의를 보고 이해한 내용을 바탕으로 합니다.





java.lang

java.lang 패키지
자바가 기본으로 제공하는 라이브러리.
(자바 언어를 이루는 가장 기본이 되는 클래스들을 보관하는 패키지를 말한다.)
모든 자바 애플리케이션에 자동으로 임포트 된다.

java.lang 패키지의 대표적인 클래스

  • Object : 모든 자바 객체의 부모 클래스
  • String : 문자열
  • Integer, Long, Double : 기본형 데이터 타입을 객체로 만든 것. 래퍼 타입
  • Class : 클래스 메타 정보
  • System : 시스템과 관련된 기본 기능들 제공



Object 클래스

자바에서 모든 클래스의 최상위 부모 클래스.
부모가 없으면 묵시적으로 Object 클래스를 상속받는다. (자바가 extends Object 코드를 넣어준다.)

  • 묵시적 : 개발자가 코드에 직접 기술하지 않아도 시스템 또는 컴파일러에 의해 자동으로 수행됨. 묵시적으로 상속 받으면 메모리에도 함께 생성된다.
  • 명시적 : 개발자가 코드에 직접 기술해서 작동

자바에서 Object 클래스가 최상위 부모 클래스인 이유?

공통 기능을 제공받을 수 있고 다형성의 기본을 구현할 수 있기 때문이다.

1. 공통 기능 제공

객체의 정보를 제공, 이 객체가 다른 객체와 같은지 비교, 객체가 어떤 클래스로 만들어졌는지 확인하는 기능. --> 객체를 만들때마다 새로운 메서드를 만들기 번거롭다.
그리고 만들어도 개발자끼리 메서드 이름이 다르니 일관성이 없다.
Object는 모든 객체에 필요한 공통 기능을 제공한다. 최상위 부모 클래스이기 때문에 모든 객체는 공통 기능을 편리하게 상속 받을 수 있다.

Object가 제공하는 기능

  • toString() : 객체의 정보 제공
  • equals() : 객체의 같음을 비교
  • getClass() : 객체의 클래스 정보 제공
  • 기타 등등

=> 프로그래밍이 단순화되고 일관성을 가지게 된다.

2. 다형성의 기본 구현

Object는 모든 클래스의 부모 클래스이다. 따라서 모든 객체를 참조할 수 있다.
모든 자바 객체는 Object 타입으로 처리될 수 있으며 이는 다양한 타입의 객체를 통합적으로 처리할 수 있게 해준다.
Object는 모든 객체를 다 담을 수 있으므로 타입이 다른 객체들을 어딘가에 보관해야 한다면 Object에 보관하면 된다.



Object 다형성

Object 다형성 한계

class Car {
 	public void move() {
 		System.out.println("자동차 이동");
 	}
}
class Dog {
 	public void sound() {
 		System.out.println("멍멍");
 	}
}
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(); //컴파일 오류, Object는 move()가 없다.
 	//객체에 맞는 다운캐스팅 필요
 	if (obj instanceof Dog dog) {
 		dog.sound();
 	} else if (obj instanceof Car car) {
 		car.move();
 	}
 }

Object는 모든 타입의 부모이므로 모든 객체를 담을 수 있다.
따라서 아래와 같이 코드를 변경할 수 있다.

Object dog = new Dog(); //Dog -> Object
Object car = new Car(); //Car -> Object

Object를 통해 전달 받은 객체를 호출하려면 각 객체에 맞는 다운캐스팅 과정이 필요하다.

다형성을 제대로 활용하려면 다형적 참조 + 메서드 오버라이딩을 함께 사용해야 한다.
그러나 Object에는 다른 객체의 메서드가 정의되어 있지 않다. 따라서 메서드 오버라이딩을 활용할 수 없다. => 각 객체의 기능을 호출하려면 다운캐스팅을 해야한다.
즉, 다형적 참조는 가능하지만 메서드 오버라이딩이 안되기 때문에 다형성을 활용하기엔 한계가 있다.

그러면 object를 언제 활용하면 좋은가?

Object 배열

Object는 모든 타입의 객체를 담을 수 있기 때문에 Object배열(Object[])은 모든 객체를 담을 수 있는 배열을 만들 수 있다.

public static void main(String[] args) {
 	Dog dog = new Dog();
 	Car car = new Car();
 	Object object = new Object(); //Object 인스턴스도 만들 수 있다.
 	Object[] objects = {dog, car, object};
}
Object[] objects = {dog, car, object};
//쉽게 풀어서 설명하면 다음과 같다.
Object objects[0] = new Dog();
Object objects[1] = new Car();
Object objects[2] = new Object();

위와 같은 Object 개념이 없다면 모든 객체를 저장할 수 있는 배열을 만들 수 없다.

toString()

Object.toString() 메서드는 객체의 정보를 문자열 형태로 제공한다.
-> 디버깅과 로깅에 유용하게 사용된다.
Object 클래스에 정의되어 있으므로 모든 클래스에서 상속받아 사용할 수 있다.

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

toString() 메서드는 기본적으로 패키지를 포함한 객체의 이름과 객체의 참조값(해시코드)를 16진수로 제공한다. java.lang.Object@a09ee92

println()과 toString()

public static void main(String[] args) {
 	Object object = new Object();
 	String string = object.toString();
    
 	//toString() 반환값 출력
 	System.out.println(string); //java.lang.Object@a09ee92
    
 	//object 직접 출력
 	System.out.println(object); //java.lang.Object@a09ee92
 }

toString()의 결과를 출력한 코드와 object를 println()에 직접 출력한 코드의 결과는 완전히 같다.
System.out.println() 메서드가 내부에서 toString()을 호출하기 때문이다.

toString() 오버라이딩

Object.toString() 메서드가 클래스 정보와 참조값을 제공하지만 이 정보만으로는 객체의 상태를 적절히 나타내기 어렵다. 그래서 보통 toString()을 재정의(오버라이딩)해서 보다 유용한 정보를 제공한다. (IDE 도움을 받아서 작성하면 편하다.)

public Dog(String dogName, int age) {
 	this.dogName = dogName;
 	this.age = age;
 }
 
 @Override
 public String toString() {
 	return "Dog{" +
 		"dogName='" + dogName + '\'' +
 		", age=" + age +
 	'}';
 }

toString()은 기본적으로 객체의 참조값을 출력하는데 이를 재정의하면 객체의 참조값을 출력할 수 없다. 다음과 같은 방법으로 객체의 참조값을 출력할 수 있다.

String refValue = Integer.toHexString(System.identityHashCode(dog1));
System.out.println("refValue = " + refValue);


Object와 OCP

만약 Object가 없고 Object가 제공하는 toString()이 없으면 서로 아무 관계 없는(공통의 부모가 없는) 객체의 정보를 출력하기 어려울 것이다. => 각각의 클래스마다 별도의 메서드를 작성해야 할 것이다.
즉, 구체적인 것에 의존하게 된다.
Object가 있기 때문에 추상적인 것(부모타입일수록 추상. 하위타입일수록 구체)에 의존하게 되고, 다형성을 잘 활용하게 된다.
(다형성을 잘 활용한다 = 다형적 참조 + 메서드 오버라이딩)

  • 다형적 참조 : Object 타입을 매개변수로 사용해서 다형적 참조를 사용한다.
  • 메서드 오버라이딩 : Object는 모든 클래스의 부모이므로 구체 클래스는 Object가 가진 toString() 메서드를 오버라이딩 할 수 있다.

OCP 원칙

  • Open : 새로운 클래스 추가. toString()을 오버라이딩하여 기능을 확장할 수 있다.
  • Closed : 새로운 클래스를 추가해도 Object와 toString()을 사용하는 클라이언트 코드를 변경하지 않아도 된다.

=> 다형적 참조, 메서드 오버라이딩, 크라이언트 코드가 구체에 의존하는 것이 아니라 추상인 Object에 의존하면서 OCP원칙을 지킬 수 있었다.

System.out.println()

System.out.println() 메서드도 Object 매개변수를 사용하고 내부에서 toString()을 호출하기 때문에 모든 객체의 정보(toString())를 편리하게 출력할 수 있다.



equals() 동일성과 동등성

Object는 동등성 비교를 위한 equals() 메서드를 제공한다.
자바는 두 객체가 같다라는 표현을 두가지로 분리해서 제공한다.

  • 동일성 : ==. 두 객체의 참조가 동일한 객체를 가리키는지 (물리적으로 같은 메모리에 있는 객체 인스턴스인지 참조값을 확인)
  • 동등성 : equals(). 두 객체가 논리적으로 동등한지
User a = new User("id-100") //참조 x001
User b = new User("id-100") //참조 x002

물리적으로 다른 메모리에 있는 객체이지만 논리적으로 같다. => 동일성은 다르나 동등성은 같다.

Object가 기본으로 제공하는 equals()는 ==으로 동일성 비교를 제공한다.

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

동등성이라는 개념은 각각의 클래스마다 다르다.
어떤 클래스는 주민등록번호 기반으로, 어떤 클래스는 고객 연락처 기반으로 등등.
따라서 동등성 비교를 사용하려면 equals() 메서드를 재정의해야한다.
그렇지 않으면 Object는 동일성 비교를 기본으로 제공한다.
IDE의 generator 단축키로 equals() 메서드를 구현하자.
(동등성 비교가 항상 필요한 것은 아니고 동등성 비교가 필요한 경우에만 equals()를 재정의하자.)

profile
🌱

0개의 댓글