공부/Java

자바 Object 클래스 복습하기

Stair 2024. 9. 18. 16:04
반응형

java.lang

자바가 기본으로 제공하는 라이브러리 중에 가장 기본이 되는 것이 java.lang이다.

자바 언어를 이루는 가장 기본이 되는 클래스들을 보관하는 패키지를 뜻한다.

 

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

Object : 모든 자바 객체의 부모 클래스

String : 문자열

Integer, Long, Double : 래퍼타입, 기본형 데이터 타입을 객체로  만든 것

Class : 클래스 메타 정보

System : 시스템과 관련된 기본 기능들을 제공

 

여기 나열한 클래스들은 자바 언어의 기본을 이루기 때문에 반드시 잘 알아야한다.

 

+java.lang 패키지는 모든 자바 애플리케이션에 자동으로 import되기 때문에 import를 하지 않아도 된다.

import java.lang.System;

public class LangMain {

    public static void main(String[] args) {
        System.out.println("hello java");
    }
}

System.out.println의 System은 java.lang.System이지만 import를 해줄 필요는 없다.

 

 

자바 Object 클래스

 

 

//부모가 없으면 묵시적으로 Object 클래스를 상속받는다.
public class Parent { //extends Object{
    
    public void parentMethod(){
        System.out.println("Parent.parentMethod");
    }
}

 

클래스에 상속 받을 부모 클래스가 없으면 묵시적으로  Object클래스를 상속 받는다.

 

public class Child extends Parent{
    public void childMethod(){
        System.out.println("Child.childMethod");
    }
}

 

public class ObjectMain {
    public static void main(String[] args) {
        Child child = new Child();
        child.childMethod();
        child.parentMethod();

        //toString()은 Object 클래스의 메서드
        String string = child.toString();

    }
}

Child는 Parent를 상속받고 Parent는 Object를 상속받기 때문에 Object 클래스에 있는 메서드는 toString()을 사용할 수 있다.

 

결론 : 자바에서 모든 객체의 최종 부모는 Object

 

자베에서 Object 클래스가 최상위 부모 클래스인 이유는 다음과 같다

1. 공통 기능 제공 : 객체의 정보를 제공하고, 이 객체가 다른 객체와 같은지 비교하고, 객체가 어떤 클래스로 만들어졌는지 확인하는 기능을 제공하기 위해, 즉 공통의 기능을 제공하기 위해 만들어졌다.

2. 다형성의 기본 구현 : 부모는 자식을 담을 수 있다. Object는 모든 클래스의 부모 클래스이다. 따라서 모든 객체를 참조할 수 있다.(Object는 모든 객체를 다 담을 수 있다. 타입이 다른 객체를 어딘가에 보관해야 한다면 Object에 보관하면 된다)

 

 

Object 다형성

Object는 모든 클래스의 부모 클래스이다 따라서  Object는 모든 객체를 참조할 수 있다.

public class Car {
    public void move(){
        System.out.println("자동차 이동");
    }
}
public class Dog {

    public void sound(){
        System.out.println("멍멍");
    }
}
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){
//        ((Dog)obj).sound();

        if(obj instanceof Dog dog){
            dog.sound();
        }else if(obj instanceof Car car) {
            car.move();
        }

    }
}

 

action 메서드를 보면 Object 타입의 매개변수를 사용한다. Object는 모든 객체의 부모이다. 따라서 어떤 객체든지 인자로 전달할 수 있다.

따라서 dog, car 모두 인자로 받을 수 있다.

하지만 Object타입엔 sound, move메서드가 없기 때문에 다운캐스팅을 해줘야 한다.

 

 

 

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};
//        Object[] objects =new Object[] {dog,car,object};

        size(objects);

    }
    

    private static void size(Object[] objects) {
        System.out.println("전달된 객체의 수는: " + objects.length);
    }
}

Object 타입을 사용한 덕분에 세상의 모든 객체를 담을 수 있는 배열을 만들 수 있다.

 

 size() 메서드는 배열에 담긴 객체의 수를 세는 역할을 담당한다.

이 메서드는 Object 타입만 사용한다. Object 타입의 배열은 세상의 모든 객체를 담을 수 있기 때문에, 새로운 클래스가 추가되거나 변경되어도 메서드를 수정하지 않아도 된다.

 

 

 

toString()

toString()  : Object가 제공하는 toString()메서드는 기본적으로 패키지를 포함한 객체의 이름과 객체의 참조값을 16진수로 제공한다.

public class ToStringMain1 {

    public static void main(String[] args) {
        Object o = new Object();
        String string = o.toString();

        //toString()반환값 출력
        System.out.println(string);
        //object 직접 출력
        System.out.println(o);
    }
}

 

java.lang.Object@b4c966a
java.lang.Object@b4c966a

 

그런데 위의 코드를 실행해보면 실행 결과가 똑같음을 알 수 있다.

System.out.println을 파고 들어가다보면 결국 toString()을 호출하는 부분이 나타난다.

따라서 println()을 사용할 때, toString()을 직접 호출할 필요 없이 객체를 바로 전달하면 객체의 정보를 출력할 수 있다.

 

toString()오버라이딩

Object.toString() 메서드가 클래스 정보와 참조값을 제공하지만, 이 정보만으로는 객체의 상태를 적절히 나타내지 못한다. 그래서 보통  toString()을 재정의해서 보다 유용한 정보를 제공하는 것이 일반적이다.

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 ToStringMain2 {

    public static void main(String[] args) {
        Car car = new Car("모델 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("1. println 내부에서 toString 호출");
        System.out.println(car);
        System.out.println(dog1);
        System.out.println(dog2);

    }
}

따라서 위처럼 toString()을 굳이 호출하지 않아도 println내부에서 toString()을 호출하기 때문에 안적어도 괜찮다.

public class ObjectPrinter {
    public static void print(Object obj){
        String string = "객체 정보 출력: " + obj.toString();
        System.out.println();
    }
}
System.out.println("3. Object 다형성 활용");
ObjectPrinter.print(car);
ObjectPrinter.print(dog1);
ObjectPrinter.print(dog2);

ObjectPrinter는 Object를 받기 때문에 car와 dog 전부인수로 받을 수 있게 된다.

 

 

Object와 OCP

만약 Object가 없고, 또 object가 제공하는 toString()이 없다면 서로 아무 관계가 없는 객체의 정보를 출력하기 어려울 것이다.

자바는 객체의 정보를 사용할 때 다형적 참조 문제를 해결해 줄 Object클래스와 메서드 오버라이딩 문제를 해결해줄 Object.toString()메서드가 있다. ObjectPrinter클래스는 추상적인 Object클래스를 사용한다.

(추상적 : Animal과 Dog, Cat과 같이 Animal같은 부모 타입으로 올라갈수록 개념은 더 추상적이게 되고, Dog, Cat과 같이 하위타입으로 내려갈수록 개념은 더 구체적이게 된다.)

ObjectP rint와 Object를 사용하는 구조는 다형성을 매우 잘 활용하고 있다. 다형성을 잘 활용한다는 것은 다형적 참조와 메서드 오버라이딩을 적절하게 사용한다는 뜻이다.

 

지금까지 설명한 ObjectPrinter.print()는 사실 System.out.println()의 작동 방식과 같다.

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

 

 

 

equals()

Object는 동등성 비교를 위한 equals() 메서드를 제공한다.

자바는 두 객체가 같다라는 표현을 2가지로 분리해서 제공한다.

1. 동일성(Identity) : ==연산자를 사용해서 두 객체의 참조가 동일한 객체를 가리키고 있는지 확인

2. 동등성(Equality) : equals() 메서드를 사용하여 두 객체가 논리적으로 동등한지 확인

 

"동일"은 100% 완전히 같음을 의미한다. 반면 "동등"은 같은 가치나 수준을 의미하지만 그 형태나 외관이 다를 수 있다.

 

동일성 : 물리적으로 같은 메모리에 있는 객체 인스턴스인이 참조값을 확인하는 것

동등성 : 논리적으로 같은지 확인하는 것

public class EqualsMainV1 {

    public static void main(String[] args) {
        UserV1 userV1 = new UserV1("id-100");
        UserV1 userV2 = new UserV1("id-100");


        System.out.println("identity = " + (userV1 == userV2));
        System.out.println("equality = " + userV1.equals(userV2));
    }
}

 

방금까지 주구장창 동일성과 동등성의 차이를 설명하였으나 막상 위의 코드를 실행해본다면

identity = false
equality = false

둘 다 false가 나온 것을 볼 수 있다.

x001 == x002는 당연히 다르다

equals는 왜 그럴까?

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

Object가 기본으로 제공하는 equals는 ==으로 비교를 한다.

따라서 참조값 비교가 되어 false가 된다. 그렇기 때문에 동등성 비교를 사용하고 싶으면 equals() 메서드를 재정의 해야한다. 그렇지 않으면 Object는 동일성 비교를 기본으로 제공한다.

public class UserV2 {

    private String id;

    public UserV2(String id) {
        this.id = id;
    }

    public boolean equals(Object obj){
        UserV2 user = (UserV2) obj;
        return id.equals(user.id);
    }

//    @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);
//    }
}
public class EqualsMainV2 {

    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));
    }
}

 

equals를 오버라이딩하여 id를 비교하는 equals메서드를 만들었다.

사실 위의 구현한 equals()는 아주 간단히 만들어진 버전이고 실제로 정확하게 동작하려면 밑에 주석 친 부분의 equals를 구현해야한다.

자바 제네레이터에 equals를 가지고 만들자 혼자 절대 못만든다

반응형

'공부 > Java' 카테고리의 다른 글

자바 String 클래스 복습하기  (1) 2024.09.19
자바 불변 객체 복습하기  (1) 2024.09.18
자바 예외처리 2  (5) 2024.09.16
자바 예외처리 1  (0) 2024.09.15
자바 익명 클래스  (1) 2024.09.14