관리 메뉴

나구리의 개발공부기록

자바의 정석 기초편 ch9 - 1 ~ 6 [Object클래스, equals(), hashCode(), toString()] 본문

유튜브 공부/JAVA의 정석 기초편(유튜브)

자바의 정석 기초편 ch9 - 1 ~ 6 [Object클래스, equals(), hashCode(), toString()]

소소한나구리 2023. 12. 5. 15:02

1) Object 클래스

  • 모든 클래스의 최고조상
  • 오직 11개의 메서드만을 가지고 있음
  • notify(), wait() 등은 쓰레드와 관련된 메서드 - 13장에서 배움
  • protected라고 되어있는 메서드들은 오버라이딩하여 public으로 변경해야 다른곳에서 사용할 수 있음
Object클래스의 메서드 설명
protected Object clone() 객체 자신의 복사본을 반환, 객체 복사에 사용
public boolean equals(Object obj) 객체 자신과 객체 obj가 같은 객체인지 알려줌 (같으면 true), 객체 비교에 사용
protected void finalize() 객체가 소멸 될 때 가비지 컬렉터에 의해 자동으로 호출
이 때 수행되어야 하는 코드가 있을 때 오버라이딩

거의 사용안함
 - 마무리 작업에 사용하는 메서드인데 메모리가 부족한 상황에서 프로그램이 종료 되어야 하는데 finalize에 코드가 있으면 메모리정리에 시간이 많이 걸리게 되는 문제가 있음
public Class getClass() 객체 자신의 클래스 정보를 담고 있는 Class인스턴스를 반환

class Class { } -> 클래스 정보를 담기위한 클래스
public int hashCode() 객체 자신의 해시코드를 반환
public String toString() 객체 자신의 정보를 문자열로 반환
public void notify() 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨움
public void notifyAll() 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨움
public void wait()
public void wait(long timeout)
public void wait(long time out, int nanos)

다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게함
(timeout은 천분의 1초, nanos는 10의9제곱 분의 1초)

2) equals (Object obj)

  • 객체 자신(this)과 주어진 객체(obj)를 비교하여 같으면 true 다르면 false를 반환
  • Object클래스의 equals()는 객체의 주소를 비교 즉, 참조값을 비교함
  • iv값을 비교하기 위해 equals()를 이용할 경우 false가 나옴(모든 객체의 주소는 다름) -> 오버라이딩이 필요함
public boolean equals(Object obj) {
	return (this==obj);	// 파라미터로 입력받은 obj와 this(객체자신과)참조값을 비교
}

 

(1) 예제

  • ValueOver 클래스에서 equals()를 오버라이딩하여 참조값이 아닌 값을 변경하도록 변경
  • Value객체를 생성하여 참조값을 저장한 변수들을 비교하면 false가 나오고, ValueOver 객체를 생성하여 참조값을 저장한 변수들을 비교하면 실제 값을 비교하게 되므로 값이 같다면 true가 나옴
class Ex9_1 {
	public static void main(String[] args) {
		Value v1 = new Value(10);	// Value 객체 생성
		Value v2 = new Value(10);	

		if (v1.equals(v2))	// v1과 v2의 주소 비교
			System.out.println("v1과 v2는 같습니다.");
		else
			System.out.println("v1과 v2는 다릅니다.");
            
		ValueOver v3 = new ValueOver(10);	// ValueOver 객체 생성
		ValueOver v4 = new ValueOver(10);	

		if (v3.equals(v4))	// v3과 v4의 실제 값을 비교
			System.out.println("v3과 v4는 같습니다.");
		else
			System.out.println("v3과 v4는 다릅니다.");
	} // main
} 

class Value {
	int value;

	Value(int value) {
		this.value = value;
	}
}

// 오버라이딩 하여 서로다른 객체라도 값이 같으면 true를 반환하도록 변경
class ValueOver {
	int value;

	ValueOver(int value) {
		this.value = value;
	}
    
	// Object의 equals() 메서드를 주소가 아닌 실제 값을 비교하도록 오버라이딩
	public boolean equals(Object obj) {

		// 참조변수의 형변환 전에는 반드시 instanceof로 확인해야함.
		if(!(obj instanceof ValueOver)) return false;
		
		ValueOver v = (ValueOver)obj; // 조상타입의 obj를 ValueOver타입으로 형변환
		return this.value==v.value;   // 오버라이딩 하여 실제 값을 비교하도록 재정의
	}
}

 

(2) 예제2

  • 동일한 메커니즘의 예제
class Person {
	long id; // this.id

	// equals()메서드 오버라이딩
	public boolean equals(Object obj) {
		if(!(obj instanceof Person))
			return false;

//		Person p = (Person)obj;
//		return this.id == p.id;
		
		return id ==((Person)obj).id;	// 위 2줄코드를 한줄로(this. 생략)
	}

	Person(long id) {
		this.id = id;
	}
}

class Ex9_2 {
	public static void main(String[] args) {
		Person p1 = new Person(8011081111222L);
		Person p2 = new Person(8011081111222L);

		if(p1.equals(p2))
			System.out.println("p1과 p2는 같은 사람입니다.");
		else
			System.out.println("p1과 p2는 다른 사람입니다.");
	}
}

3) hashCode()

  • 객체의 해시코드를 정수타입으로 반환하는 메서드
  • 동등한 객체들은 같은 해시코드를 반환해야 하며, 이는 객체를 식별하고 비교하는 데 사용되어 객체의 지문이라고도 불림
  • 해시코드는 정수타입으로 표현되며 해싱알고리즘으로 생성됨(11장 컬렉션 프레임워크 추가로 배움)
  • equals()를 오버라이딩 하면, hashCode()도 오버라이딩 해야 하는데 이유는 equals()의 결과가 true이면 두 객체가 같다는 뜻인데 hashCode()가 다르면 논리상 어긋나기 때문, 즉 equals()와 hashCode()는 서로 일관성을 유지해야 함
  • 해시충돌로 인하여 서로 다른 객체라도 같은 해시코드를 가질 수 있는데 이를 위한 다양한 해결방법이 있음 -> 별도 검색 필요
public class Object {
	...
    // 네이티브메서드 : OS의 메서드(C언어),
    public native int hashCode();	// 내용이 없음 - 이미 작성되어있는 OS의 메서드를 사용
    
    // OS가 가지고있는(C언어로 작성되어있는)메서드들을 Java에서 사용할 수 있도록 함
    // JNI라는 기술도 있음

 

(1) 예제1

  • String클래스는 이미 equals()와 hashCode()가 오버라이딩 되어있어서 일관성있게 출력되므로 String클래스의 hashCode로 값을 출력해 보면됨
  • 오버라이딩 전의 해시코드를 보고싶다면 identityHashCode()를 사용하면됨
class Ex9_3 {
	public static void main(String[] args) {
		// String클래스에는 equals와 hashCode가 이미 오버라이딩 되어있음
		String str1 = new String("abc");
		String str2 = new String("abc");

		System.out.println(str1.equals(str2));	
		System.out.println(str1.hashCode()); // 오버라이딩 된 hashCode 출력
		System.out.println(str2.hashCode());
        
		// Object클래스의 hashCode 출력
		System.out.println(System.identityHashCode(str1));
		System.out.println(System.identityHashCode(str2));
	}
}

 

** 참고

  • 32bit JVM = 주소값 int 타입
  • 64bit(8byte) JVM = 주소값 Long 타입
  • 64bit의 JVM으로 해시코드를 만들면 8byte 에서 4byte로 바뀌어 정보의 손실이 있을 수 있음

4) toString()의 오버라이딩

  • 객체를 문자열로 변환하기 위한 메서드
  • Object의 toString()은 객체의 주소값을 반환(엄밀히 말하면 16진수로 표시된 해시코드값이 출력되는 것)
  • 주소값이 아닌 iv값(실제 논리적인 값)을 출력 하고자 하면 오버라이딩 해야 함
public String toString() {	// Object클래스의 toString
	return getClass().getName()+"@"+Integer.toHexString(hashCode());
	//      클래스객체   클래스이름                 16진       해시코드 반환
}

 

(1) toString(), equals(), hashCode() 오버라이딩 예제

  • equals()와 hashCode()를 오버라이딩하여 일관성을 유지
  • toString()을 오버라이딩하여 출력 형식을 커스텀하여 toString()를 호출하면 형식에 맞춰서 출력됨
import java.util.Objects;

class Card {
	String kind;
	int number;

	Card() {
		this("SPADE", 1);
	}

	Card(String kind, int number) {
		this.kind = kind;
		this.number = number;
	}
	
	// equals()를 오버라이딩하면 hashCode()도 오버라이딩 해야함.
	public int hashCode() {
		return Objects.hash(kind, number);
	}
	
	// equals()오버라이딩
	public boolean equals(Object obj) {
		if(!(obj instanceof Card))
			return false;
		
		Card c = (Card)obj;
		return this.kind.equals(c.kind) && this.number==c.number;
	}
	
	// Object클래스의 toString()을 오버라이딩
	public String toString() {
		return "kind:" + kind + ", number:" + number;
	}
}

class Ex9_4 {
	public static void main(String[] args) {
		Card c1 = new Card();
		Card c2 = new Card();
		
		System.out.println("c1과 c2가 같나요? "+c1.equals(c2));

		System.out.println("c1의 값은? "+c1.toString());
		System.out.println("c2의 값은? "+c2.toString());
		
		System.out.println("c1의 hashCode는? "+c1.hashCode());
		System.out.println("c2의 hashCode는? "+c2.hashCode());
	}
}

출력값

c1과 c2가 같나요? true
c1의 값은? kind:SPADE, number:1
c2의 값은? kind:SPADE, number:1
c1의 hashCode는? -1842861219
c2의 hashCode는? -1842861219

 

** 참고

  • equals()와 hashCode()의 오버라이딩이 필요한 경우 IDE에서 해당 기능을 자동으로 완성시켜주는 기능이 존재함
  • 이클립스는 오른쪽버튼 -> Source에, 인텔리제이는 컨트롤 + 엔터를 치면 equals() and hashCode()라는 메뉴가 있음

 

** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브 강의