hoon's bLog

Java Object class 객체클래스 본문

IT/Java

Java Object class 객체클래스

개발한기발자 2021. 1. 6. 14:43
반응형

안녕하세요.

이번 포스팅은 Object 클래스에 대해서 해볼까 합니다.

앞서 우리가 알아봤던 상속에 대해서 정리했었는데,

자바에서 상속이란 필수적인 요소라고 할 수 있습니다.

package com.test.JavaStudy

class 클래스명(){ }

왜냐! 위 코드는 아래 코드와 같기 때문이죠!

package com.test.JavaStudy

class 클래스명() extends Object { }

헉! 소름....

저도 Object 클래스가 그냥 다양한 기능들과 메서드들을 가지고 있구나 싶었는데,

이렇게 생략되어 모든 클래스의 부모인지는 꿈에도 몰랐습니다..(반성깊이깊이ㅠㅠㅠㅠ)

이처럼 자바에서 모든 클래스는 암시적으로 ,Object 클래스를 상속받고 있던 것이였습니다.

즉 Object 클래스는 모든 클래스의 조상이라 할 수 있죠

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html

 

Object (Java SE 11 & JDK 11 )

Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup. The general contract of fi

docs.oracle.com

위 링크는 java 11 버전 기준으로 작성된 API 문서입니다. 꼭 참고하시길 바랍니다!

 

위의 그림은 Object 클래스가 가지고 있는 메소드를 보여주고 있죠?

다시 말해서 자바의 객체는 위의 메소드들을 반드시 가지고 있다고 할 수 있습니다.

이 중에 중요하면서 입문 단계에서 이해할 수 있는 API들을 살펴보실까요?

toString() 메소드

toString() 메소드는 해당 인스턴스에 대한 정보를 문자열로 반환합니다.

이때 반환되는 문자열은 클래스 이름과 함께 구분자로 '@'가 사용되며,

그 뒤로 16진수 해시 코드(hash code)가 추가됩니다.

16진수 해시 코드 값은 인스턴스의 주소를 가리키는 값으로, 인스턴스마다 모두 다르게 반환됩니다.

다음 예제는 toString() 메소드를 이용하여 인스턴스의 정보를 출력하는 예제입니다.

Test test1 = new Test();
Test test2 = new Test();

System.out.println(test1.toString());
System.out.println(test2.toString());

실행결과
//Test@15db9742
//Test@6d06d69c

equals() 메소드

equals() 메소드는 해당 인스턴스를 매개변수로 전달받는 참조 변수와 비교하여, 그 결과를 반환합니다.

이때 참조 변수가 가리키는 값을 비교하므로, 서로 다른 두 객체는 언제나 false를 반환하게 됩니다.

다음 예제는 equals() 메소드를 이용하여 두 인스턴스를 서로 비교하는 예제입니다.

Test test1 = new Test();
Test test2 = new Test();

System.out.println(test1.equals(test2));
test1 = test2; // 두 참조 변수가 같은 주소를 가리킴.
System.out.println(test1.equals(test2));

실행결과
//false
//true

clone() 메소드

clone() 메소드는 해당 인스턴스를 복제하여, 새로운 인스턴스를 생성해 반환합니다.

하지만 Object 클래스의 clone() 메소드는 단지 필드의 값만을 복사하므로,

필드의 값이 배열이나 인스턴스면 제대로 복제할 수 없습니다.

따라서 이러한 경우에는 해당 클래스에서 clone() 메소드를 오버라이딩하여,

복제가 제대로 이루어지도록 재정의해야 합니다.

이러한 clone() 메소드는 데이터의 보호를 이유로,

Cloneable 인터페이스를 구현한 클래스의 인스턴스만이 사용할 수 있습니다.

다음 예제는 clone() 메소드를 이용하여 인스턴스를 복제하는 예제입니다.

import java.util.*;
class Car implements Cloneable {
    private String modelName;
    private ArrayList<String> owners = new ArrayList<String>(); //1


    public String getModelName() { return this.modelName; }                    // modelName의 값을 반환함
    public void setModelName(String modelName) { this.modelName = modelName; } // modelName의 값을 설정함
    public ArrayList getOwners() { return this.owners; }                      // owners의 값을 반환함
    public void setOwners(String ownerName) { this.owners.add(ownerName); }   // owners의 값을 추가함

    public Object clone() {
        try {
           Car clonedCar = (Car)super.clone(); //2
           // clonedCar.owners = (ArrayList)owners.clone(); //3
           return clonedCar;
         }catch (CloneNotSupportedException ex) { //4
            ex.printStackTrace();
            return null;
        }
    }
}

 

public class Object03 {
    public static void main(String[] args) {
       Car car01 = new Car(); //5
       car01.setModelName("아반떼");
       car01.setOwners("홍길동");
       System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners() + "\n"); //6

       Car car02 = (Car)car01.clone(); //7
       car02.setOwners("이순신"); //8
       System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners()); //9
       System.out.println("Car02 : " + car02.getModelName() + ", " + car02.getOwners()); //10
    }
}


실행결과
Car01 : 아반떼, [홍길동] 
Car02 : 아반떼, [홍길동, 이순신]
Car02 : 아반떼, [홍길동, 이순신]

위 예제의 2번 라인에서는 부모 클래스의 clone() 메소드를 호출하여 clone() 메소드를 오버라이딩하고 있습니다.

5번 라인에서는 Car 클래스의 인스턴스인 car01을 생성하고, 7번 라인에서는 오버라이딩한 clone() 메소드를 호출하여 복제를 수행하고 있습니다.

 

하지만 2번 라인처럼 clone() 메소드를 재정의하면, 필드의 값이 1번 라인처럼,

인스턴스일 때는 제대로 된 복제를 수행할 수 없습니다.

8번 라인에서는 복제된 인스턴스인 car02의 owners 필드에 새로운 값을 하나 추가합니다.

하지만 9번 라인의 실행 결과를 보면, 7번 라인의 결과와는 달리,

원본 인스턴스인 car01의 owners 필드에도 새로운 값이 추가되었음을 확인할 수 있습니다.

이처럼 단순히 부모 클래스의 clone() 메소드를 호출하여 clone() 메소드를 재정의하면,

배열이나 인스턴스인 필드는 복제되는 것이 아닌,

해당 배열이나 인스턴스를 가리키는 주소값만이 복제되는 것입니다.

 

따라서 정확한 복제를 위해서는 3번 라인처럼 배열이나 인스턴스인 필드에 대해서는,

별도로 clone() 메소드를 구현하여 호출해야 합니다.

3번 라인의 주석을 해제하고 결과보기를 다시 실행하면, 다음과 같이 정확한 실행 결과가 출력될 것입니다.

Car01 : 아반떼, [홍길동]
Car02 : 아반떼, [홍길동]
Car02 : 아반떼, [홍길동, 이순신]

 

Object 메소드

Object 클래스의 메소드는 다음과 같습니다.

메소드 설명
protected Object clone() 해당 객체의 복제본을 생성하여 반환함.
boolean equals(Object obj) 해당 객체와 전달받은 객체가 같은지 여부를 반환함.
protected void finalize() 해당 객체를 더는 아무도 참조하지 않아 가비지 컬렉터가 객체의 리소스를 정리하기 위해 호출함.
Class<T> getClass() 해당 객체의 클래스 타입을 반환함.
int hashCode() 해당 객체의 해시 코드값을 반환함.
void notify() 해당 객체의 대기(wait)하고 있는 하나의 스레드를 다시 실행할 때 호출함.
void notifyAll() 해당 객체의 대기(wait)하고 있는 모든 스레드를 다시 실행할 때 호출함.
String toString() 해당 객체의 정보를 문자열로 반환함.
void wait() 해당 객체의 다른 스레드가 notify()나 notifyAll() 메소드를 실행할 때까지 현재 스레드를 일시적으로 대기(wait)시킬 때 호출함.
void wait(long timeout) 해당 객체의 다른 스레드가 notify()나 notifyAll() 메소드를 실행하거나,
전달받은 시간이 지날 때까지 현재 스레드를 일시적으로 대기(wait)시킬 때 호출함.
void wait(long timeout, int nanos) 해당 객체의 다른 스레드가 notify()나 notifyAll() 메소드를 실행하거나 전달받은
시간이 지나거나 다른 스레드가 현재 스레드를 인터럽트(interrupt) 할 때까지,
현재 스레드를 일시적으로 대기(wait)시킬 때 호출함.

이렇게 클래스와, 그와 관련된 메서드에 대해서 알아봤습니다.

직접 코딩하면서 익히기! 잊지마세요!!!

 

오늘도 수고하셨습니다:)

 

​출처 : 자바의 정석 3rd Edition 내용 정리

728x90
반응형