프로그래밍에서 두 객체를 비교할 때 어떤 기준으로 비교할지 명확해야 한다. "같다"라는 것이 무엇을 의미하는지 정의해야 하는데, 이때 동일성과 동등성은 객체를 비교할 때 중요한 개념이다.
자바에서는 이 두 개념을 `equals()`와 `==`연산자를 통해 구분할 수 있다.
이 개념을 설명하기 전에 `==`연산자는
기본 타입을 비교할 때는 값 자체를,
참조 타입을 비교할 때는 객체의 메모리 주소를 비교한다는 사실을 알고 가자.
동일성이란?
동일성은 두 객체가 메모리 상에서 동일한 주소를 참조하고 있는지를 비교하는 개념이다. 자바에서는 `==`연산자를 사용하여 객체의 동일성을 비교한다. 두 객체를 `==`연산자로 비교하면, 힙 메모리에서 같은 객체인지를 확인한다.
Object obj1 = new Object();
Object obj2 = new Object();
Object obj3 = obj1;
System.out.println(obj1 == obj2); // false (서로 다른 객체)
System.out.println(obj1 == obj3); // true (같은 객체를 참조)
동등성이란?
동등성은 두 객체가 내용적으로 같은지(같은 상태나 값을 가지는지) 비교하는 개념이다. `Object` 클래스의 기본 `equals()` 메서드는 `==`와 동일하게 참조를 비교한다.
public boolean equals(Object var1) {
return this == var1;
}
다음은 `Object` 클래스의 기본 `equals()`메서드이다. 하지만, 많은 클래스에서 객체의 내용을 비교하기 위해 `@Override`해서 사용한다.
// == 연산자 사용
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // false (서로 다른 객체)
// equals() 메소드 사용
System.out.println(str1.equals(str2)); // true (내용이 같음)
💡왜 `equals()`를 오버라이딩 해야 할까?
위 코드를 확인하면, Object 클래스의 `equals()`메서드는 `==`연산자를 사용해서 동일성을 비교한다. 동등성을 비교하려면 객체의 필드 값들을 비교할 수 있도록 `equals()`메서드를 오버라이딩해야 한다. 객체의 상태나 값을 비교하기 위해서는 클래스에 맞는 동등성 기준을 정의해야 하기 때문이다.
String은 객체인데 ==비교해도 값이 같은 경우가 있. 왜 그럴까?
자바에선 문자열 생성 방식에 두 가지가 있다.
리터럴 방식
String str1 = "Hello";
String str2 = "Hello";
- 생성된 문자열은 문자열 상수 풀에 저장된다.
- JVM은 먼저 풀에서 "Hello"라는 문자열이 있는지 확인한다.
- 있다면 해당 참조를 반환하고, 없다면 새로 생성하여 풀에 추가한다.
- 따라서 `str1`과 `str2`는 같은 문자열 객체를 참조하게 된다.
- 이 경우 `str1 == str2`는 `true`를 반환한다.
new 연산자 방식
String str1 = new String("Hello");
String str2 = new String("Hello");
- `new` 연산자는 항상 힙 메모리에 새로운 객체를 생성한다.
- 내용은 같더라도 `str1`와 `str2`는 서로 다른 객체를 참조한다.
- 이 경우는 `str1 == str2`는 `false를 반환한다.
- `str1.equals(str2)`는 `true`를 반환하다.
문자열 풀과 intern() 메서드
String str1 = new String("Hello").intern();
String str2 = "Hello";
System.out.println(str1 == str2); // true
- `intern()` 메서드는 해당 문자열이 상수 풀에 있는지 확인한다.
- 있다면 풀에 있는 문자열의 참조를 반환하고, 없다면 풀에 추가한 후 그 참조를 반한한다.
- 따라서 `str1`과 `str2`는 같은 객체를 참조한다.
동일한 문자열 리터럴을 `==`연산자로 비교하면 문자열 상수 풀에서 동일한 참조를 사용하기 때문에 `true`를 반환할 수 있습니다. 하지만 `new`키워드를 사용하여 문자열을 생성하면 새로운 객체가 생성되므로 `==`연산자는 `false`를 반환한다. 따라서 문자열 비교 시 항상 `equals()`메서드를 사용한 동등성 비교를 하는 것이 좋다.
String 클래스의 equals() 구현
// String 클래스의 equals()
public boolean equals(Object var1) {
if (this == var1) {
return true;
} else {
boolean var10000;
if (var1 instanceof String) {
String var2 = (String)var1;
if ((!COMPACT_STRINGS || this.coder == var2.coder) && StringLatin1.equals(this.value, var2.value)) {
var10000 = true;
return var10000;
}
}
var10000 = false;
return var10000;
}
}
String 클래스에서는 `equals()`메서드를 위와 같이 오버라이드하여 문자열의 내용(문자 배열)을 비교하도록 구현했다.먼저 참조 비교를 해보고, 다른 객체라면 실제 문자열 내용을 비교하는 방식을 사용한다.
'Java' 카테고리의 다른 글
[Java] equals()와 hashCode()에 관하여 (0) | 2025.03.14 |
---|---|
[Java] 얕은 복사와 깊은 복사 (3) | 2025.03.11 |
[Java] 일급 컬렉션이란? (1) | 2025.03.08 |
[Java] Checked Exception과 Unchecked Exception (1) | 2025.03.07 |