첫 번째 System.out.println(s1 == s2)는 s1과 s2가 모두 "Hello World" 문자열을 참조하고 있으므로 true를 출력합니다.
두 번째 System.out.println(s1 == s2)는 s2에 "!"를 추가한 후, s1과 비교하므로 false를 출력합니다. 이는 s2 + "!" 연산이 새로운 String 객체를 생성하고, 그 객체의 참조값을 s2가 가리키게 되기 때문입니다.
세 번째 System.out.println(s1 == s2)는 다시 s2를 "Hello World"로 초기화한 후, s1과 s2가 모두 같은 문자열을 참조하고 있으므로 true를 출력합니다.
+ 더 자세한 해설 Java의 String 클래스는 불변(immutable) 객체입니다.
이는 생성된 String 객체가 변경 불가능하다는 것을 의미합니다.
즉, 한 번 생성된 String 객체는 그 값을 변경할 수 없습니다.
이러한 불변성은 Java의 메모리 모델에서 매우 중요합니다.
Java는 객체를 참조하는 변수가 있을 때, 해당 객체에 대한 참조 값을 변수에 저장합니다.
불변 객체는 생성된 이후에 값이 변경될 수 없기 때문에, 이들의 값은 항상 일정합니다.
따라서, 불변 객체에 대한 참조를 갖는 변수는 해당 객체가 가리키는 값을 변경할 수 없습니다.
이러한 특성은 Java의 스레드 안정성(thread-safety)을 보장하는 데에도 도움을 줍니다.
하지만 이러한 불변성 때문에, String 객체를 수정하려고 하면 기존 객체를 변경하는 대신 새로운 객체를 생성합니다.
예를 들어, String 객체에 대해 문자열 결합 연산자(+)를 사용하면, 기존 객체를 변경하는 대신, 두 개의 String 객체를 연결한 새로운 String 객체를 생성합니다.
이 때문에 String을 연결하거나 수정하는 작업이 빈번하게 일어날 때, 메모리 소비가 높아질 수 있습니다.
그리고 불변성으로 인해 String 객체가 많은 수로 생성될 수 있다는 단점도 있습니다. 예를 들어, 문자열을 많이 연결하는 작업을 수행할 경우 새로운 String 객체가 생성될 수 있기 때문에 메모리 사용량이 늘어날 수 있습니다. 이러한 경우에는 StringBuilder나 StringBuffer 클래스를 사용하는 것이 더 효율적입니다. 이 클래스들은 가변(mutable) 객체로, 문자열을 연결할 때 기존 객체를 변경하는 대신에 내부적으로 저장공간을 조정하여 문자열을 추가할 수 있도록 해줍니다.
6. 총정리
그래서 언제 사용하는게 좋을까?!
String : 값이 자주 변하지 않을 때 (자주 변하면, 메모리 소비가 심함)
StringBuilder : 값이 변하고, 싱글스레드일때 (대신 빠름)
StringBuffer : 값이 변하고, 멀티스레드일때 (대신 느림)
이상으로, Java에서 문자열을 다루는 String, StringBuffer, StringBuilder 클래스에 대해 알아보았습니다.
적절한 상황에서 각 클래스를 적절히 사용하면서, 보다 효율적으로 문자열을 다룰 수 있을 것입니다.