아래와 같이 Literal 하게 String 변수인 strA, strB를 선언한 뒤, 이 두개의 변수를 "+"연산으로 합치면 어떻게 될까? 물론 strC가 "test1test2"로 될 것이다.
String strA = "test1";
String strB = "test2";
String strC = strA + strB;
System.out.println("strA : " + strA );
System.out.println("strB : " + strB );
System.out.println("strC : " + strC );
실행 결과
strA : test1
strB : test2
strC : test1test2
하지만 이렇게 될 때, literal하게 선언되었으니, "test1", "test2"는 String Constant Pool에 등록되었고, "+"연산자를 이용해서 "test1", "test2"를 합칠 때 마다 새로운 String 객체가 생성된다. 이는 당연히 가비지 컬렉터가 회수해야할 대상이 된다. 이로 인해서 메모리 사용량이 증가하게 되고, 가비지 컬렉션 실행 빈도가 늘어나기 되기때문에 성능 저하를 야기할 가능성이 있다.
이는 String 이 불변(immutable)하기 때문인데, 불변 객체는 한 번 생성된 후 그 상태를 변경할 수 없으며, 모든 변경 사항이 적용된 새로운 객체를 생성해야만 하기 때문이다. 이러한 특성은 문자열 연산이 많은 경우 성능 저하를 초래할 수 있다는 단점이 있다.
그래서 대안으로 나온 것이 가변적인 StringBuffer와 StringBuilder 클래스를 사용하는 것이다.
이러한 클래스들을 사용하면 문자열을 직접 변경할 수 있고, 매번 새로운 객체를 생성하지 않아도 된다. 이는 특히 문자열 조작이 많은 경우 성능 저하를 초래한다는 String 클래스의 단점을 보완할 수 있다.
StringBuffer sbf = new StringBuffer();
sbf.append("Hello");
sbf.append(" ");
sbf.append("World!");
String result2 = sbf.toString();
System.out.println(result2);
출력 결과
Hello World!
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" ");
sb.append("World!");
String result = sb.toString();
System.out.println(result);
출력 결과
Hello World!
StringBuffer와 StringBuilder의 차이점은 멀티스레드 상황일 때 동기화를 하는지 안하는지의 여부이다.
따라서 선택기준은 다음과 같다.