Object클래스
equals(Object obj)
public boolean equals(Object obj) {
return (this==obj);
}
Object클래스에 정의되어 있는 equals메서드의 실제 내용이다.
위의 코드에서 알 수 있듯이 두 객체의 같고 다름을 참조 변수의 값으로 판단한다.
public class Test {
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");
v2 = v1;
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
euqals메서드는 주소 값으로 비교하기 때문에, 두 멤버 변수 value의 값이 10으로 서로 같을지라도 equals메서드로 비교한 결과는 false일 수밖에 없다.
하지만, v2 = v1; 을 수행한 후에는 참조 변수 v2는 v1이 참조하고 있는 인스턴스의 주소 값이 저장되므로 v2도 v1과 같은 주소 값이 저장된다.
Ojbect클래스로부터 상속받은 equals메서드로 인스턴스의 값을 비교하려면
오버 라이딩하여 주소가 아닌 객체에 저장된 내용을 비교하도록 변경하면 된다.
import java.util.Objects;
class Person {
long id;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id;
}
public Person(long id) {
this.id = id;
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person(123L);
Person p2 = new Person(123L);
if(p1 == p2)
System.out.println("p1 == p2");
else
System.out.println("p1 != p2");
if(p1.equals(p2))
System.out.println("p1 == p2");
else
System.out.println("p1 != p2");
}
}
equals메서드가 Person인스턴스의 주소 값이 아닌 멤버 변수 id의 값을 비교하도록 오버 라이딩했다.
hashCode()
이 메서드는 해싱(hashing) 기법에 사용되는 해시함수를 구현한 것이다. 해싱은 데이터 관리기법 중의 하나인데 다량의 데이터를 저장하고 검색하는 데 유용하다.
해시함수는 찾고 하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시 코드(hash code)를 반환한다.
일반적으로 해시 코드가 같은 두 객체가 존재하는 것이 가능하지만, Object클래스에 정의된 hashCode메서드는 객체의 주소 값으로 해시 코드를 만들어 반환하기 때문에 32 bit JVM에서는 서로 다른 두 객체는 결과 같은 해시 코드를 가질 수 없었지만, 64 bit JVM에서는 8 byte 주소 값으로 해시 코드(4 byte)를 만들기 때문에 해시 코드가 중복될 수 있다.
클래스의 인스턴스 변수 값으로 객체의 같고 다름을 판단해야 하는 경우라면 equals메서드뿐만 아니라 hashCode메서드도 적절히 오버 라이딩해야 한다.
public class Test {
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
2003749087
1324119927
String클래스는 문자열의 내용이 같으면, 동일한 해시 코드를 반환하도록 hashCode메서드가 오버라이딩되어 있기 때문에 내용이 같은 인스턴스에 대해 항상 동일한 해시 코드 값을 얻는다.
반면에 System.identityHashCode(Object o)는 hashCode메서드처럼 객체의 주소 값으로 해시 코드를 생성하기 때문에 모든 객체에 대해 항상 다른 해시 코드값을 반환할 것을 보장한다. 그래서 srt1과 str2가 해시 코드는 같지만 서로 다른 객체라는 것을 알 수 있다.
toString()
toString() 메서드는 인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것이다. 인스턴스의 정보를 제공한다는 것은 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 뜻이다.
Object클래스에 정의된 toString()은 아래와 같다.
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
오버 라이딩하지 않고 toString()을 호출하면 16진수의 해시 코드를 얻게 된다.
public String toString() {
return "objName + ~~ ";
}
이처럼 toString을 오버 라이딩하여 사용할 수 있다.
String 클래스
변경 불가능한 클래스
String클래스에는 문자열을 저장하기 위해서 문자형 배열 참조 변수(char [])value를 인스턴스 변수로 정의해놓고 있다. 인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열이 인스턴스 변수(value)에 문자형 배열(char [])로 저장되는 것이다.
덧셈 연산자를 사용해서 문자열을 결합하는 것은 매 연산 시마다 새로운 문자열을 가진 String인스턴스가 생성되어 메모리 공간을 차지하게 되므로 가능한 결합 횟수를 줄이는 것이 좋다.
문자 열간의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우 StringBuffer클래스를 사용하는 것이 좋다.
StringBuffer 클래스와 StringBuilder 클래스
StringBuffer 클래스는 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며, 인스턴스를 생성할 때 그 크기를 지정할 수 있다.
편집할 문자열의 길이를 고려하여 버퍼의 길이를 충분히 잡아주는 것이 좋다.
StringBuffer의 생성자
StringBuffer클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편지하기 위한 공간(buffer)으로 사용된다.