String 클래스에 문자열을 저장하기 위해 문자형 배열 참조변수 char[] value를 인스턴스 변수로정의해 놓고 있다
인스턴스 생성시 생성자의 매개변수로 입력받는 문자열은 value에 저장되게 된다
한번 생성된 String 인스턴스가 가지고 있는 문자열은 읽어 올 수 만 있고
변경할 수 없다
그래서 + 같은 연산자를 이용해서 문자열을 결합하는 경우 인스턴스 안의 문자열이 변경되는 것이 아니라 새로운 문자가 담긴 String 인스턴스가 생성된다

자바에서 객체를 생성할 때 일반적으로 new 키워드를 사용하지만 String 클래스는 값을 바로 할당할 수 있다 이것을 문자열 리터럴이라 한다
String str1 = new String("abc");
String str2 = "abc"
이렇게 문자열을 생성하는 두 방식은 실제로 메모리에 저장되는 방식에 차이가 있다

문자열 연산과 컴파일러 최적화
JDK1.5 이후부터는 String 의 + 연산은 컴파일시에 StringBuilder를 사용하도록 자동변환되어 성능 최적화가 이뤄진다. 그러나 String은 항상 StringBuilder로 변환되지 않는다
///컴파일 전
String str1 = "0" + "1" + "2";
///컴파일 후
String str1 = "012";
String str2 = "";
str2 += 0;
str2 += 1;
str2 += 2;
//컴파일 이 후, 디컴파일 한 소스파일
String str2 = "";
str2 = (new StringBuilder()).append(s1).append("0").toString();
str2 = (new StringBuilder()).append(s1).append("1").toString();
str2 = (new StringBuilder()).append(s1).append("2").toString();
String 을 + 연산시에는 리터럴이면 한줄연산인 경우(첫번째 예시) 컴파일러가 연산과정에서 문자열을 결합하는 최적화 후 한번에 상수풀에올린다
두번째 예시의 경우 새로운 String 객체 생성 없이 상수풀에 있는 값들을 append를 통해 연결해준다.
String 연산시에는 상수풀에 새로운 객체를 생성한다 메모리가 낭비가 된다
StringBuilder는 내부적으로 연산시 새로운 객체를 생성하지 않고 기존 데이터를 변경한다
리터럴 선언은 상수풀에 존재하는 값을 가져와서 쓴다
new 로 만들어진 문자열은 힙 메모리에 새로운 객체를 생성한다
그냥 처음부터 StringBuilder 사용하자