String 객체는 생성될 때 private final char[] 의 형태로 생성되기 때문에 내부의 문자열이나 길이를 변경할 수 없으며 할당된 메모리 공간 또한 변하지 않는다.
따라서 + 연산자 또는 concat 메서드를 통해 기존의 문자열에 새로운 문자열을 붙이는 경우 기존 문자열에 새로 붙이는 것이 아니라, 새로운 String 객체를 만든 후 연결된 문자열을 저장하고 그 객체를 참조하도록 한다.
String data = "ABC";
data += "DEF";
위의 경우 "ABC"에 "DEF"가 추가되어 data 변수의 값이 "ABCDEF"가 된다고 생각할 수 있지만, String 객체는 내부 문자열을 수정할 수 없으므로 "ABCDEF"라는 새로운 String 객체를 생성한다.
data 변수는 새로 생성된 String 객체를 참조하게 되고, 기존에 있던 "ABC"는 참조되지 않아 가비지 컬렉션의 메모리 해제를 기다리게 된다.
이렇듯 문자열 연산이 많을 수록 String 객체의 수가 늘어나 오버헤드가 발생하며 성능이 떨어진다.
하지만, 객체가 불변하므로 멀티 스레드 환경에서 동기화를 신경 쓸 필요가 없고 조회 연산에서는 StringBuffer나 StringBuilder 보다 빠르다.
StringBuffer와 StringBuilder 클래스는 String 클래스와 달리 문자열 연산 도중 기존 객체의 공간이 부족할 경우 버퍼의 크기를 늘리며 유연하게 동작한다.
또한 StringBuffer와 StringBuilder 클래스가 제공하는 메서드는 서로 동일하다.
이 두 클래스의 차이점은 동기화 여부이다.
StringBuffer는 각 메서드별로 Synchronized 키워드가 존재하여 멀티 스레드 환경에서도 동기화를 지원한다.
반면, StringBuilder는 Synchronized 키워드가 없으며 동기화를 보장하지 않는다.
따라서 멀티 스레드 환경이라면 StringBuffer를 사용하는 것이 좋고, 단일 스레드 환경이라면 StringBuilder를 사용하는 것이 좋다.
단일 스레드 환경에서 StringBuffer를 사용한다고 문제가 되는 것은 아니지만, 동기화 관련 처리로 인해 StringBuilder에 비해 성능이 좋지 않다.
String: 문자열 연산이 적고, 조회가 많은 멀티 스레드 환경
StringBuffer: 문자열 연산이 많은 멀티 스레드 환경 또는 스레드에 안전한 프로그램이나 개발 중인 시스템이 스레드에 안전한지 모를 경우
StringBuilder: 문자열 연산이 많은 단일 스레드 또는 스레드 안전 여부에 관계 없는 프로그램
[참고]