String是一个final类,不允许继承,实现了Serializable、Comparable和CharSequence接口
内部维护一个final的char类型的数组value[],对String参数的操作本质上是在操作这个数组

String的不可变
String类用final修饰,不可以被继承,所以方法不可以被修改;String维护的value数组用final修饰,初始化后不可变,final只能保证引用不可变,但是数组仍然可以修改,所以构造方法通过拷贝数组内容的方法保证原数组的修改不会影响新String对象,保证了value不被外部访问;任何需要修改String内容的方法都会创建新的String对象
不可变可以通过反射破坏,反射获取到value参数,修改其访问权限,然后修改数组内容即可。

String使用了字符串常量池,存于JVM的堆中。像Integer一样作为缓存减少对象的创建,Integer的缓存使用数组存储,String的使用HashTable存储,结构为数组+链表,这个HashTable不可以扩容。
当代码中使用双引号表示一个String时,JVM到常量池查找它是否已经存在,存在的话返回此对象的引用,不存在的话新创建一个对象放入字符串常量池再返回引用。
当使用new创建对象时,在堆中一定会创建一个对象,栈中存放着引用;如果常量池中没有此字符串的话,在常量池中也会创建一个对象,堆中的对象的引用是常量池的对象地址
String.intern()可以强制让一个String对象存储到常量池,不过应该小心使用,容易内存溢出,HashTable的大小虽然不可以扩容,但是可以设置-XX:StringTableSize,默认为60013(质数更好散列)

编译时优化,java编译的时候会将字符串进行优化
String str4 = “abc”+“efg”;
String str5 = “abcefg”;
System.out.println(str4 == str5); //返回TRUE
应该是创建一个对象"abcefg",编译期会进行常量折叠

StringBuilder和StringBuffer
都继承自抽象类AbstractStringBuilder,抽象类中定义了大部分操作方法,StringBuilder和StringBuffer重写并做自己的处理
抽象类中维护一个char类型的数组,对StringBuilder和StringBuffer的操作都是对数组的操作,数组初始大小为16,如果不够用的话会扩容,扩容为2倍原大小+2。可以与ArrayList做比较,ArrayList维护的是Object类型的数组。
StringBuffer中有一个char类型的数组toStringCache作为缓存,toString的时候会用到缓存(如果toStringCache是null,就将它置为当前数组),append等方法中会将toStringCache置为null
StringBuffer是线程安全的,方法基本都使用synchronized修饰