Java에서 변수가 저장하는 값은 기본 타입과 참조 타입에 따라 완전히 다름.
int, char, boolean, double 등int a = 10; // 변수 a에 실제 값 10을 저장
int b = 10; // 변수 b에 실제 값 10을 저장
String, 배열, 클래스 등String name = new String("Hong"); // name에는 "Hong" 객체의 주소가 저장됨
String name2 = new String("Hong"); // name2에는 다른 "Hong" 객체의 주소가 저장됨
== 연산자는 변수에 저장된 값을 비교함. 여기서 핵심은 "값"이 무엇인지 이해하는 것임.
int x = 5;
int y = 5;
System.out.println(x == y); // true - 실제 값 5와 5를 비교
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // false - 서로 다른 메모리 주소를 비교
// 같은 객체를 참조하는 경우
String str3 = str1;
System.out.println(str1 == str3); // true - 같은 메모리 주소를 비교
비유: 두 사람이 같은 내용의 편지를 가지고 있더라도, 각자의 집 주소는 다름. ==는 집 주소를 비교하는 것과 같음.
equals() 메소드는 객체의 내용을 비교하도록 설계됨.
String name = new String("Hong");
String name2 = new String("Hong");
System.out.println(name == name2); // false - 주소 비교
System.out.println(name.equals(name2)); // true - 내용 비교
| 연산자/메소드 | 기본 타입 | 참조 타입 |
|---|---|---|
== | 실제 값 비교 | 메모리 주소 비교 |
equals() | 사용 불가 | 객체 내용 비교 |
String은 참조 타입이지만 리터럴 방식으로 생성할 때 특별한 동작을 함.
// 리터럴 방식 - String Pool에서 같은 객체 재사용
String name3 = "Hong";
String name4 = "Hong";
System.out.println(name3 == name4); // true - 같은 주소 참조
// new 키워드 방식 - 항상 새로운 객체 생성
String name5 = new String("Hong");
String name6 = new String("Hong");
System.out.println(name5 == name6); // false - 다른 주소 참조
String Pool: Java가 메모리 효율성을 위해 동일한 문자열 리터럴을 하나의 객체로 관리하는 특별한 메모리 영역
배열도 참조 타입이므로 == 연산 시 주소를 비교함.
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3}; // 내용은 같지만 다른 객체
int[] arr3 = arr1; // 같은 객체를 참조
System.out.println(arr1 == arr2); // false
System.out.println(arr1.equals(arr2)); // false (배열은 equals가 재정의되지 않음)
System.out.println(arr1 == arr3); // true
// 참조 타입의 특징 - 한 배열 수정 시 참조하는 모든 변수에 영향
arr3[0] = 10;
System.out.println(arr1[0]); // 10 - arr1도 변경됨
import java.util.Arrays;
int[] arr1 = {1, 2, 3};
int[] arr2 = {1, 2, 3};
System.out.println(Arrays.equals(arr1, arr2)); // true - 내용 비교
참조 타입을 == 으로 내용 비교
String str1 = new String("test");
String str2 = new String("test");
if (str1 == str2) { // 잘못된 비교!
// 실행되지 않음
}
null 체크 없이 equals() 사용
String str = null;
// str.equals("test"); // NullPointerException 발생!
// 올바른 방법
if (str != null && str.equals("test")) {
// 안전한 비교
}
// 더 안전한 방법
if ("test".equals(str)) {
// 상수를 앞에 두어 null 예외 방지
}
참조 타입의 == 연산과 equals() 메소드의 차이를 이해하는 것은 Java 프로그래밍의 기본임.
핵심은 == 연산자는 변수에 저장된 값 자체를 비교한다는 점임. 기본 타입에서는 실제 값이, 참조 타입에서는 메모리 주소가 비교됨.
객체의 내용을 비교하려면 반드시 equals() 메소드를 사용해야 하며, 특히 String은 리터럴 생성 방식에 따라 == 연산 결과가 달라질 수 있음을 주의해야 함.
이 개념을 확실히 이해하면 참조 타입 관련 버그를 예방하고 더 안정적인 코드를 작성할 수 있음.