可变性
类都是被final修饰,不可以被继承
String类中,jdk8用 private final **char[]** value
来保存字符串,jdk9用private final **byte**[] value
来保存字符串,都用final修饰,所以String对象是不可变的。
StringBuffer和StringBuilder都继承AbstractStringBuilder,AbstractStringBuilder中用char[] value
保存字符串,没有final修饰,所以这两个对象都是可变的。
线程安全
由于String对象不可变,所以是线程安全的。
StringBuffer对方法加了锁,所以是线程安全的。
StringBuilder没有加锁,所以是非线程安全的。
性能
String类型进行操作时,会产生新的对象
StringBuilder单线程时性能高
StringBuilder比StringBuffer性能大概高10%
字符串拼接的优化
提前预估字符串最终拼接大小,避免多次扩容造成性能损耗
利用不同版本的JDK进行编译
JDK8:
JDK9:
JDK8中字符串拼接操作会被优化成StringBuilder操作,JDK9中里面则是用了StringConcatFactory封装了统一的优化操作。
字符串的缓存
统计数据表明,堆中平均25%的对象是字符创,其中半数是重复的,所以通过建立缓存可以有效降低内存消耗和创建对象的开销。
String在JDK6之前**intern()**方法把字符创缓存,但是会缓存在永久代中,基本不会被垃圾回收,有可能会发生OOM,后续版本中,把这个放到了堆中,避免了永久代被占满的问题,甚至永久代在JDK8 中被MetaSpace替代了。
Intern是一种显式的排重机制,需要开发者写代码时候明确调用,不方便和效率问题,引出了G1 GC下的字符串排重,通过将相同数据的字符串指向同一份数据来做到,是JVM底层的改变。
字符串操作会利用到JVM的Intrinsic机制,运行特殊的本地代码。
String自身的演化
JDK历史用char数组存储,是两个bytes大小,拉丁语系语言的字符,不需要太宽的 char,造成了浪费,最终改为了byte数组加一个编码,通过的性能测试和产品试验,明显的看到紧凑字符串带来的占用内存小,更快操作速度的优势
注:
StringBuffer和StringBuilder的底层都是利用可修改的(char,jdk9之后是byte)数组,二者继承了AbstractStringBuilder,区别在于最终的方法是否加了synchronized