아래 테스트 코드는 통과된다.
String a = "HI";
String b = "HI";
assertThat(a == b).isTrue(); // true
assertThat(a).isEqualTo(b); // true
그런데 아래 테스트 코드는 통과되지 않는다.
String a = "HI";
String b = new String("HI");
assertThat(a == b).isTrue(); // FALSE
assertThat(a).isEqualTo(b); // true
하아ㅏㅏㅏㅏ,,,,, 왤까?
Java에서 String은 두 가지 생성 방식을 가진다.
String a = new String("HI");
String b = "HI";
두 방식의 결정적인 차이는, 전자인 new 방식의 경우 String을 생성하면 Heap 영역에, 후자인 literal 방식의 경우 String constant pool이라는 곳에 저장되기 때문이다. 결론부터 말하자면 논리적으로 같은 값일지라도 저장된 주소가 다르니 identity가 다르게 나오는 건 당연하다.
그럼 “HI” == “HI”가 참인 것은 어떻게 설명할 수 있단 말인가.
그건 문자열을 literal 방식으로 선언하면 내부적으로 intern() 메서드가 호출되는데, 이 메서드는 String constant pool에 요청된 객체와 같은 값이 있는지 먼저 체크하고, 없으면 하나를 만들고 있으면 원래 있던 문자열의 레퍼런스를 가리키게 된다. 그렇기 때문에 자동으로 equality가 보장된다는 것,,!
그리고 이건 새로 알게 된 건데, Java 6버전 까지 String constant pool의 위치는 perm 영역이었다가 Java7에서 Out Of Memory 문제가 발생하여 오라클 엔지니어들이 Heap 영역으로 이 자료 구조의 위치를 옮겼다고 한다. 따라서 GC의 대상이 되므로,, 얌전히 equals를 쓰는 게 좋겠다.