[Java] Object 클래스

이준영·2023년 8월 22일
0

🟫 Java

목록 보기
9/21
post-thumbnail

자바의 모든 객체의 최상위 클래스는 Object 클래스라는 것을 다들 알고 있을 것입니다.
이 말이 의미하는 것은 모든 클래스의 부모를 따라 올라가다보면 결국은 Object 클래스를 상속해 내려왔었다는 사실을 알 수 있습니다. 이러한 Object클래스가 어떤 역할을 하고 영향을 미치는지 한번 정리해 보려고 합니다.

Object 클래스

finalize()

Object 클래스에 대해 공부하면서 finalize라는 소멸자가 있다는 사실을 처음 알았습니다.

Protected void finalize() throws Throwable

이 메소드는 GC을 통해 힙 영역의 인스턴스가 소멸(Sweep) 될 때 자동으로 호출 되는 메소드입니다.

Object클래스의 메소드라 오버라이드 해서 사용할 수 있지만, 프로그램이 실행되는 전체 과정중에 소멸자가 반드시 호출 할 것이라는 보장이 없기에 (프로그램이 종료되면 따로 소멸자를 호출하지 않고 그냥 전부 종료) 사용하기에 좋은 메소드는 아닙니다.

equals()

Object 에 참조값을 비교하는 메소드인 equals()도 존재합니다. 필자는 Equals() 메소드를 언제나 String 의 값을 비교할 경우에만 사용해 왔기에 == 연산자와는 다른 역할을 하는 메소드로 인지하고 있었는데 본질적으로 == 연산자와 똑같다는 사실에 놀라웠습니다.

String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true

==연산자: 피연산자의 참조값이 일치하는가 비교


그렇다면 String의 "equals()" 와 "==" 은 어떻게 다를까?

이미 알고 있거나 예상 했겠지만 String 클래스 내부에서 Object 클래스의 equals() 메소드를 오버라이딩 해서 사용하고 있기 때문입니다.


실제 String 클래스 내부 equals() 메소드

위의 코드를 하나씩 살펴보면
첫 if문을 통해 this 와 비교대상 객체의 참조값을 비교합니다. -> 자기자신인지 확인, 동일하면 true 반환

이후 객체가 String의 인스턴스인지 판단하고, 문자열의 인코딩 방식을 판단하고, 이후 마지막 String의 내부 클래스인 StringLatin1의 equals를 통해 값을 비교 한 후 그 결과를 반환한다고 합니다.

이렇게 Object의 equals() 메소드를 오버라이딩 함으로써 프로그래머가 원하는 방식으로 동등 비교를 할 수 있습니다.


clone()

클론 메소드를 호출하면 똑같이 복사된 인스턴스의 Object 형 참조값이 반환됩니다.

clone() 메소드는 cloneable 인터페이스가 구현되어 있어야 호출할 수 있어서 인터페이스의 메소드라고 생각할 수 있는데, Clone 메소드는 Object 클래스의 메소드입니다.

얕은 복사와 깊은 복사

복사하면 빠질 수 없는 이야기 중 하나가 얕은 복사와 깊은 복사 입니다. 간단히 설명을 해보자면

얕은복사: 새로운 인스턴스로 복제해서 참조하는것이 아니고 현존하는 참조값을 그대로 다른 참조변수로 복제해 사용하는 것으로 참조하는 데이터를 수정하면 원본 데이터도 값이 변하게 됩니다. 이런 복사를 얕은 복사라고 합니다.

깊은복사: 깊은복사는 새로운 참조변수에 원본과 똑같은 값을 가진 새로운 인스턴스를 참조할 수 있도록 복제하는 것을 말합니다. 이 경우 전혀 다른 두 인스턴스이기 때문에 복제된 객체의 값을 수정해도 원본에 영향이 가지 않습니다.

clone() 얕은 복사 예시
// 점의 좌표를 나타내는 포인트 클래스
class Point implements Cloneable {
    private int xPos;
    private int yPos;
    
    public Point(int x, int y) {
        xPos = x;
        yPos = y;
    }
    
}

// 사각형 클래스
class Rectangle implements Cloneable {
    private Point upperLeft;     
    private Point lowerRight;     

    public Rectangle(int x1, int y1, int x2, int y2) {
        upperLeft = new Point(x1, y1);
        lowerRight = new Point(x2, y2);
    }


    @Override
    public Object clone() throws CloneNotSupportedException {
        Rectangle copy = (Rectangle)super.clone();

        return copy;
    }

}

위와 같은 상황에서 아래 코드를 실행시키면 어떻게 될까?

Rectangle rec = new Rectangle(1, 1, 9, 9);
Rectangle cpy = (Rectangle) rec.clone();

보기에는 새로운 인스턴스를 할당받아 cpy에 참조값을 전달하고 있기에 완전히 복제된 것처럼 보일 수 있다.

하지만 내부적으론 이렇게 Rectangle 내부의 Point 변수는 원본 데이터와 같은 인스턴스를 참조하게 된다. 이러한 오류를 범하지 않도록 clone() 메소드를 호출 할 경우 주의해야한다. 이러한 문제를 해결한 깊은 복사의 예시도 살펴보자


clone() 깊은 복사 예시
// 점의 좌표를 나타내는 포인트 클래스
class Point implements Cloneable {
	...
    // 생략
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

// 사각형 클래스
class Rectangle implements Cloneable {
    ...
    // 생략
    @Override
    public Object clone() throws CloneNotSupportedException {
        Rectangle copy = (Rectangle)super.clone();
        
        copy.upperLeft = (Point)upperLeft.clone();
        copy.lowerRight = (Point)lowerRight.clone();

        return copy;
    }

}

방법은 간단하다. 위와 같이 복제한 인스턴스의 Point 참조값도 clone을 통해 참조하면 완전히 같은 형태를 지닌 복제된 인스턴스를 반환받을 수 있다.


참고
profile
작은 걸음이라도 꾸준히

0개의 댓글