java.lang패키지는 자바프로그맹에 가장 기본이 되는 클래스들을 제공한다.
java.lang.Object클래스는 모든 클래스의 최고 조상이기 때문에 Object클래스의 멤버들은 모든 클래스에서 사용 가능하다.
Object클래스는 멤버 변수는 없고 오직 11개의 메서드만 가지고 있다. 이 메서드들은 모든 인스턴스가 가져야 할 기본적인 것들이다.
매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean값으로 알려주는 역할을 한다.
public boolean equals(Object obj) {
return (this==obj);
}
class Example {
public static void main(String[] args) {
Value v1 = new Value(10);
Value v2 = new Value(10);
if (v1.equals(v2))
System.out.println("v1과 v2는 같습니다.");
else
System.out.println("v1과 v2는 다릅니다.");
}
}
class Value {
int value;
Value (int value) {
this.value = value;
}
}
위 예제의 실행결과는 "v1과 v2는 다릅니다."가 출력될 것이다. 그 이유는 똑같은 값이 들어있는 객체를 비교 했었더라도 v1과 v2는 엄연히 독립된 객체이며 그저 멤버int의 값이 같은것 말고는 다른 객체이기에 그렇다. 참조변수 v1와 참조변수v2의 주소값이 달라 equals()에서 false가 나온다.
v1(0x100) == v2(0x200) → 0x100 == 0x200 → false
위 예제의 경우에서는 false가 나왔지만 우리가 Value클래스의 참조변수를 서로 비교하더라도 안의 멤버값이 같다면 true로 반환하게끔 바꿀수도 있다.
class Value {
int value;
Value (int Value) {
this.value = value;
}
public boolean equals(Object obj) {
if(obj instanceof Value)
return value == ((Value)obj).id; // obj가 Object타입이므로 Value값으로 형 변환한다.
} else {
return false; // 타입이 Value가 아니면 값을 비교할 필요도 없다.
}
}
}
public class Example {
public static void main(String[] args) {
Value v1 = new Value(10);
Value v2 = new Value(10);
Value v3 = new Value(20);
System.out.println(v1.equals(v2));
System.out.println(v1.equals(v3));
}
}
위 예제를 실행 하면 각각 true와 false가 나올 것이다.
해싱(hashing)기법에 사용되는 '해시함수(hash function)'를 구현한 것이다. 해싱은 데이터 관리기법 중의 하나인데 다량의 데이터를 저장하고 검색하는 데 유용하다.
해시함수는 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드(hashcode)를 반환한다.
일반적으로 해시코드가 같은 두 객체가 존재하는 것이 가능하지만, Object클래스에 정의된 hashCode메서드는 객체의 주소값을 이용해서 해시코드를 만들어 반환하기 때문에 서로 다른 두 객체는 결코 같은 해시코드를 가질 수 없다. 단, 64 bit JVM에서는 주소가 64 bit이므로 주소를 해시코드(32 bit)로 변환하면 중복된 값이 나올 수도 있다.
앞서 살펴본 것과 같이 클래스의 인스턴스변수 값으로 객체의 같고 다름을 판단해야하는 경우라면 equals메서드 뿐 만아니라 hashCode메서드도 적적히 오버라이딩해야 한다. 같은 객체라면 hashCode메서드를 호출했을 때의 결과값인 해시코드도 같아야 하기 때문이다. 만일 hashCode메서드를 오버라이딩 하지 않는다면 Object클래스에 정의된 대로 모든 객체가 서로 다른 해시코드값을 가질 것이다.
class Example {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1.equals(str2));
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
System.out.println(System.identityHashCode(str1));
System.out.println(System.identityHashCode(str2));
}
}
결과
true
96354
96354
27134973
12846932
String클래스는 이미 문자열의 내용이 같다면 같은 해시코드를 반환하도록 hashCode()가 오버라이딩 되어 있다. 반면에 System.identityHashCode(Obeject obj)는 Object.hashCode()메서드처럼 주소값으로 해시코드를 생성하기에 모든 객체에 대해 항상 다른 해시코드값을 반환한다.
이 메서드는 인스턴스의 정보를 문자열로 제공할 목적으로 정의한 메서드이다. 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 뜻이다.
public String toString() {
return getClass().getName()+"@"+Integer.toHexString(hashCode());
}
클래스를 작성할 때 toString()을 오버라이딩 하지 않으면 클래스 이름과 16진수의 해시코드를 얻게된다.
String클래스의 toString()은 해당 인스턴스가 가지고 있는 문자열을 반환하도록 오버라이딩 돼 있고, Date클래스의 경우에는 해당 인스턴스가 가지고 있는 날짜,시간을 문자열로 반환하도록 오버라이딩 돼 있다.
class Card {
String kind;
int number;
Card() {
this("SPADE", 1);
}
Card(String kind, int number) {
this.kind = kind;
this.number = number;
}
public String toString() {
return "kind : " + kind + " , number : " + number;
}
}
public class Example {
public static void main(String[] args) {
Card c1 = new Card();
Card c2 = new Card("CLOVER", 7);
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
결과
kind : SPADE, number : 1
kind : CLOVER, number : 7
원래 였으면 Card@42e9d0등과 같은 클래스 + 16진수 해시코드화 주소값이 나왔겠지만 toString()을 오버라이딩 하여 Card클래스의 인스턴스 멤버변수의 값들을 불러올 수 있게 되었다.