public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    
    @Stable
    private final byte[] value;
    
    private final byte coder;
    
    private int hash;
    
    // ...
}
String() {}
String(byte[] bytes) {}
String(byte[] bytes, Charset charset) {}
String(byte[] bytes, int offset, int length) {}
String(byte[] bytes, int offset, int length, Charset charset) {}
String(byte[] bytes, int offset, int length, String charsetName) {}
String(byte[] bytes, String charsetName) {}
String(char[] value) {}
String(char[] value, int offset, int count) {}
String(int[] codePoints, int offset, int count) {}
String(String original) {}
String(StringBuffer buffer) {}
String(StringBuilder builder) {}
String s1 = new String("luca");
String s2 = new String("luca");

힙역영
모든 쓰레드가 공유하며, new 키워드로 생성된 객체와 배열이 생성되는 영역입니다. 또한, 메소드 영역에 로드된 클래스만 생성이 가능하고 Garbage Collector가 참조되지 않는 메모리를 확인하고 제거하는 영역입니다
값울 표현한 것String s3 = "hello";
String s4 = "hello";

String Constant Pool에 올라간다.ConcurrentHashMap로 구현String s1 = new String("luca");
String s2 = new String("luca");
String s3 = "luca";
String s4 = "luca";
System.out.println(s1 == s2); //false
System.out.println(s2 == s3); //false
System.out.println(s3 == s4); //true
System.out.println(s4 == s1); // false
String s1 = new String("luca");
String s3 = "luca";
s1 = s1.intern();
System.out.println(s1 == s3); //true
String s1 = new String("luca");
s1 = s1 + "luca";

매번 + 연산을 진행할 때마다 새로운 객체를 생성하기 때문에 비효율적이다.
String은 기본적으로 불변이기 때문에 concat(), toUpperCase(), trim()같은 메서드를 사용할 때도 내부의 필드가 바뀌는 것이 아니라 새로운 String 객체를 생성해서 반환한다.
public final class StringBuilder extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuilder>, CharSequence {
  public StringBuilder() {
    super(16);
  }
  
  public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
  }
  public StringBuilder append(String str) {
    super.append(str);
    return this;
  }
}
abstract class AbstractStringBuilder implements Appendable, CharSequence {
  byte[] value;
  byte coder;
  int count;
  private static final byte[] EMPTYVALUE = new byte[0];
  AbstractStringBuilder() {
    value = EMPTYVALUE;
  }
  AbstractStringBuilder(int capacity) {
    if (COMPACT_STRINGS) {
      value = new byte[capacity];
      coder = LATIN1;
    } else {
      value = StringUTF16.newBytesFor(capacity);
      coder = UTF16;
    }
  }
  public AbstractStringBuilder append(String str) {
    if (str == null) {
      return appendNull();
    }
    int len = str.length();
    ensureCapacityInternal(count + len);
    putStringAt(count, str);
    count += len;
    return this;
  }
  
}
StringBuilder sb = new StringBuilder("luca");
sb.append("luca");

진짜로 + 연산자가 느릴까?
String s = "hello";
for(int i = 0; i < 3; i++) {
    s += "luca";    
}


public final class StringConcatFactory {
  private enum Strategy {
    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder}.
     */
    BC_SB,
    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but trying to estimate the required storage.
     */
    BC_SB_SIZED,
    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but computing the required storage exactly.
     */
    BC_SB_SIZED_EXACT,
    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also tries to estimate the required storage.
     */
    MH_SB_SIZED,
    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also estimate the required storage exactly.
     */
    MH_SB_SIZED_EXACT,
    /**
     * MethodHandle-based generator, that constructs its own byte[] array from
     * the arguments. It computes the required storage exactly.
     */
    MH_INLINE_SIZED_EXACT
  }
  public static CallSite makeConcatWithConstants(MethodHandles.Lookup lookup,
                                                 String name,
                                                 MethodType concatType,
                                                 String recipe,
                                                 Object... constants) throws StringConcatException {
    if (DEBUG) {
      System.out.println("StringConcatFactory " + STRATEGY + " is here for " + concatType + ", {" + recipe + "}, " + Arrays.toString(constants));
    }
    return doStringConcat(lookup, name, concatType, false, recipe, constants);
  }
}
기본전략은 MH_INLINE_SIZE_EXACT 이 사용되기 되는데, 
이 전략은 문자열 연결 작업이 특정 크기 이하일 경우,
문자열 연결 작업의 크기를 예측할 경우에 적합하다.
나머지 전략에서 대부분 StingBuilder를 사용한다.
+ 연산자를 사용하면 버전에 따라서 최적화가 진행된다.StringBuilder 객체를 생성하는 방식으로 진행되어 성능의 저하가 심하다.StringConcatFactory를 사용하여 개선되었다.StringBuilder sb = newStringBuilder("hello");
for(int i = 0; i < 3; i++) {
    sb.append("luca");    
}
String s = sb.toString();