String 연산 (+)
String은 객체로 생성하든 리터럴로 생성하든 수정을 하게 되면 그 자체가 수정이 되지 않고 수정된 새로운 문자열이 생기고 변수가 그 주소를 담게 됩니다. 따라서 원래 문자열은 힙영역에 garbage상태로 남았다가 garbage collect때 없어집니다.
String파보기1 에서 알아보았듯이 ==연산은 변수에 저장된 값을 비교하기 때문에 String형 변수가 참조하고있는 주소를 비교하게 됩니다. 따라서 같은 주소를 보게 하고 각각 string을 +연산으로 수정해줬을때 담고 있는 주소가 달라졌음을 알 수 있는 코드입니다
// 객체
String a = new String("hello");
String c = a;
System.out.println(a==c); // true
a = a + " world";
System.out.println(a==c); // false
// 리터럴
String b = "hello";
c = b;
System.out.println(b==c); // true
b = b + " rabbit";
System.out.println(b==c); // false
이런식이라면 String연산은 하면 할 수록 메모리 낭비가 심해집니다. 따라서 수정이 자주 필요한 경우 String연산으로 처리하는 것은 권장되지 않습니다!
StringBuilder
이때 써먹는 것이 StringBuilder입니다!! StringBuilder는 다영한 내장함수로 새로 생성없이 해당 String을 수정할 수 있습니다.
여기서 예제에서는 append에 int를 넣었는데 어떻게 string으로 나왔는지 궁금해서 찾아보니 내부적으로 string이 아닌 파라미터가 들어오면 String.valueof()함수를 통해 String으로 바꾼다고 합니다~
StringBuffer sb = new StringBuffer();
StringBuffer tmp = sb;
System.out.println(tmp == sb); // true
for(int i=0; i<5; i++){
sb.append(i);
}
System.out.println(sb); // 01234 출력
System.out.println(tmp==sb); //true
이렇게 좋아보이는 StringBuilder의 치명적 단점은 동기화기능이 없다는 것입니다. 따라서 멀티쓰레드 환경이라면 race condition이 발생할 수 있습니다! 물론 단일쓰레드 환경에서는 걱정없이 사용가능합니다 🤗
StringBuffer
StringBuilder + 동기화기능이 StringBuffer입니다. 사용법은 StringBuilder와 같습니다. 따라서 멀티쓰레드환경에서는 StringBuffer를 사용하면 됩니다. 참고로 그냥 String 또한 수정을 허용하지 않기 때문에 당연히 race condition이 발생하지 않습니다