본문 바로가기

Java 기본 문법 - 참조 서적 [이것이 자바다 - 한빛미디어]/9. 기본 API 클래스

2. Java 자바 [API] - Object 클래스, Object 클래스의 메소드 1

 

 

자바의 클래스를 선언할 때 extends 키워드로 다른 클래스를 상속하지 않으면

암시적으로 java.lang.Object 클래스를 상속하게 된다.

 

따라서 자바의 모든 클래스는 Object 클래스의 자식이거나 자손 클래스가 된다.

 

즉, Object는 자바의 최상위 부모 클래스에 해당된다.

 

최상위 Object 클래스는 모든 클래스가 상속한다.

 

- Object 클래스는 필드가 없고 메소드로 구성되어 있다.

  이 메소드들은 모든 클래스들이 Object 클래스를 상속하므로, 모든 클래스에서 이용할 수 있다.

 

 

Object 클래스의 메소드들 1


1. 객체 비교 equals()

 

형태

public boolean equals(Object obj) { . . . }

 

- 매개 타입 : Object모든 객체가 매개 값으로 대입될 수 있다.

  (Object가 최상위 타입이므로 모든 객체는 Object 타입으로 자동 타입 변환될 수 있기 때문이다.)

 

- 리턴 타입 : boolean 두 객체가 동일한 객체라면 true, 그렇지 않으면 false 를 리턴한다.

 

 

equals() 메소드는 비교 연산자인 “==” 과 동일한 결과가 리턴 된다.

 

Object obj1 = new Object();

Object obj2 = new Object();

 

boolean result = obj1.equals(obj2);     // 기준 객체 : obj1 비교 객체 : obj2 true 리턴

boolean result = (obj1 == obj2);          // 위의 실행문과 동일한 결과 true 리턴

 

 

논리적으로 동등하다는 의미 : 같은 객체이건 다른 객체이건 상관없이 저장하고 있는 데이터가 동일

 

예) String 객체의 equals() 메소드는 String 객체의 번지값을 비교하는 것이 아닌 문자열이 동일한지 조사해서

      같다면 true, 그렇지 않다면 false를 리턴한다.

 

즉, String 클래스가 Object의 equals() 메소드를 재정의(오버라이딩)해서 번지 비교가 아닌

문자열 비교로 변경했기 때문이다.

 

Object 클래스의 equals() 메소드는 직접 사용되지 않고 하위 클래스에서 재정의(오버라이딩) 되어서

논리적으로 동등비교 시 이용된다.

 

예) Member 객체는 다르지만 id 필드 값이 같으면 논리적으로 동등한 객체로 취급하고 싶을 경우

Object 클래스의 equals() 메소드를 재정의해서 id 필드 값이 같음을 비교한다.

 

 

equals() 메소드를 재정의할 시,

 

매개값(비교 객체)이 기준 객체와 동일한 타입의 객체인지 먼저 확인해야 한다.

Object 타입의 매개 변수는 모든 객체가 매개값으로 제공될 수 있기 때문에 instanceof 연산자

기준 객체와 동일한 타입인지 제일 먼저 확인해야 한다.

 

만약 비교 객체가 다른 타입이라면 equals() 메소드는 false를 리턴해야 한다.

비교 객체가 동일한 타입이라면 기준 객체 타입으로 강제 타입 변환해서 필드 값이 동일한지 검사한다.

 

예) Member.java : 객체 동등 비교 (equals 메소드 오버라이딩)

 

public class Member {
    public String id;

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

    @Override    // Object의 equals() 메소드 오버라이딩
    public boolean equals(Object obj) {
        if(obj instanceof Member) {         // 매개값 obj가 Member 타입인지 확인
            Member member = (Member) obj;   // Member 타입으로 강제 타입 변환
            if(id.equals(member.id)) {      // id 필드 값이 동일한지 검사, 동일하다면 true 리턴
                return true;
            }
        }
        return false;   // 매개값 obj가 Member 타입이 아니거나 id 필드값이 다른경우 false
    }
}

 

MemberExample.java : 객체 동등 비교 실행 클래스

 

public class MemberExample {
    public static void main(String[] args) {
        Member obj1 = new Member(“blue”);
        Member obj2 = new Member(“blue”);
        Member obj3 = new Member(“red”);

        if(obj1.equals(obj2)) {  // 매개값이 Member 타입이고 id 필드 값도 동일하므로 true
            System.out.println(“obj1 과 obj2는 동등합니다.”);
        } else {
            System.out.println(“obj1 과 obj2는 동등하지 않습니다.”);
        }

        if(obj1.equals(obj3)) { // 매개값이 Member 타입이지만, id 필드 값이 동일하지 않아 false
            System.out.println(“obj1과 obj2는 동등합니다.”);
        } else {
            System.out.println(“obj1과 obj3은 동등하지 않습니다.”);
        }
    }
}

 


2. 객체 해시 코드 hashCode()

객체 해시코드 : 객체를 식별할 하나의 정수값

 

Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어서 리턴한다.

따라서 객체마다 다른 값을 가지고 있다.

 

논리적 동등 비교 시 hashCode()를 오버라이딩할 필요성이 있는데,

컬렉션 프레임워크에서 HashSet, HashMap, Hashtable은 아래와 같은 방법으로 두 객체가 동등한지 비교한다.

 

 

우선 hashCode() 메소드를 실행해서 리턴된 해시코드 값이 같은지 검사한다.

해시코드 값이 다르면 다른 객체로 판단하고, 같으면 equals() 메소드로 다시 검사한다.

따라서 hashCode() 메소드가 true 가 나와도 equals()의 리턴 값이 다르면 다른 객체가 된다.

 

key.java : Object의 hasCode() 메소드를 재정의 하지 않음

 

public class Key {
    public int number;

    public Key(int number) {   // 생성자
        this.number = number;
    }
    @Override    // Object 의 equals() 재정의
    public boolean equals(Object obj) {
        if(obj instanceof Key) {                     // Key 타입과 같다면
            Key compareKey = (Key) obj;              // 강제 타입 변환
            if(this.number == compareKey.number) {  // number 필드 값이 같으면 true
                return true;
            }
        }
        return false;
    }
}

 

이런 경우 HashMap 의 식별키로 Key 객체를 사용하면 저장된 값을 찾아올 수 없다.

number 필드값이 같더라도 hashCode() 메소드에서 리턴하는 해시코드가 다르기 때문에 다른 식별키로

인식하기 때문이다.

 

“new Key(1)” 객체로 “홍길동” 을 저장하고 다시 “new Key(1)”객체로 저장된 “홍길동”을 읽으려고

시도했을 때 null이 나온다!!

 

KeyExample.java : 다른 키로 인식

 

public class KeyExample {
    public static void main(String[] args) {
        // Key 객체를 식별키로 사용해서 String 값을 저장하는 HashMap 객체 생성
        HashMap<Key, String> hashMap = new HashMap<Key, String>();

        // 식별키 “new Key(1)” 로 “홍길동”을 저장함
        hashMap.put(new Key(1), “홍길동”);

        // 식별키 “new Key(1)” 로 “홍길동”을 읽어옴
        String value = hashMap.get(new Key(1));
        System.out.println(value);                 // null 출력
    }
}

 

 

의도한 대로 “홍길동” 을 읽으려면 아래와 같이 재정의한 hashCode() 메소드를 Key 클래스에 추가한다.

 

Key.java : hashCode() 메소드 재정의 추가

 

public class Key {
    public int number;

    public Key(int number) {   // 생성자
        this.number = number;
    }
    @Override    // Object 의 equals() 재정의
    public boolean equals(Object obj) {
        if(obj instanceof Key) {                     // Key 타입과 같다면
            Key compareKey = (Key) obj;              // 강제 타입 변환
            if(this.number == compareKey.number) {  // number 필드 값이 같으면 true
                return true;
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        return number;
    }
}

 

hashCode() 리턴값을 number 필드값으로 했기 때문에 저장할 때의 “new Key(1)”과

읽을 때의 “new Key(1)”은 같은 해시코드가 리턴된다.

 

저장할 때의 new Key(1) 과 읽을 때의 new Key(1) 은 사실 다른 객체이지만

HashMap은 hashCode()의 리턴값이 같고, equals() 리턴값이 true 가 나오지 때문에

동등 객체로 평가한다.

 

즉, 같은 식별키로 인식한다는 의미이다. 결론적으로 말해서 객체의 동등 비교를 위해서는

Object의 equals() 메소드만 재정의하지 말고 hashCode() 메소드도 재정의해서

논리적 동등 객체일 경우 동일한 해시코드가 리턴되도록 해야 한다.

 

 

Member.java : hashCode() 메소드 재정의 추가

 

public class Member {
    public String id;

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

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Member) {
            Member member = (Member) obj;
            if(id.equals(member.id)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }
}

 

id 필드값이 같을 경우, 같은 해시코드를 리턴하기 위해

String의 hashCode() 메소드의 리턴값을 활용한다. String 의 hashCode()는 같은 문자열일 경우

동일한 해시코드를 리턴한다.

 

 


3. 객체 문자 정보 toString()

Object 클래스의 toString() 메소드는 객체의 문자 정보를 리턴한다.

 

객체 문자 정보 : 객체를 문자열로 표현한 값

기본적으로 Object 클래스의 toString() 메소드는 “클래스명@16진수해시코드”로 구성된 문자 정보를 리턴

 

Object obj = new Object();

System.out.println(obj.toString());

 

[실행 결과]

java.lang.Object@de6ced

 

 

toString() 메소드의 리턴값은 자바 어플리케이션에서 별 값어치가 없는 정보이므로,

 

Object의 하위 클래스는 toString() 매소드를 재정의(오버라이딩)하여

간결하고 유익한 정보를 리턴하도록 되어 있다.

 

예) java.util 패키지의 Date 클래스는 toString() 메소드를 오버라이딩하여

      현재 시스템의 날짜와 시간 정보를 리턴한다.

 

String 클래스는 toString() 메소드를 오버라이딩하여 저장하고 있는 문자열을 리턴한다.

 

 

ToStringExample.java : 객체의 문자 정보

 

public class ToStringExample {
    public static void main(String[] args) {
        Object obj1 = new Object();
        Date obj2 = new Date();
        System.out.println(obj1.toString());
        System.out.println(obj2.toString());
    }
}

 

[실행 결과]

java.lang.Object@1b15692

Wed Nov 13 09:33:06 KST 2020

 

 

 

예) SmartPhone 클래스에서 toString() 메소드를 오버라이딩해서 제작회사와 운영체제를 리턴

 

smartPhone.java : 객체의 문자 정보 (toString()) 메소드

 

public class SmartPhone {
    private String company;
    private String os;

    public SmartPhone(String company, String os) {   // 생성자
        this.company = company;
        this.os = os;
    }

    @Override
    public String toString() {  // toString 오버라이딩
        return company + “, ” + os;
    }
}

 

SmartPhoneExample.java : 객체의 문자 정보 실행 클래스

 

public class SmartPhoneExample {
    public static void main(String[] args) {
        SmartPhone myPhone = new SmartPhone(“구글”, “안드로이드”);

        String strObj = myPhone.toString();
        System.out.println(strObj);   // 구글, 안드로이드 출력
        System.out.println(myPhone);  // 구글, 안드로이드 출력
    }
}

 

System.out.println() 메소드를 콘솔에 출력하기 위해 사용되었다. 이 메소드의 메개값은 콘솔에 출력할 내용인데,

매개값이 기본 타입(byte, short, int, long, float, double, boolean)일 경우 해당 값을 그대로 출력한다.

 

만약 매개값으로 객체를 주면,

객체의 toString() 메소드를 호출해서 리턴값을 받아서 출력하도록 되어 있다.