Java核心技术
String、StringBuffer、StringBuilder有什么区别
- String、StringBuffer、StringBuilder 有什么区别?
典型回答:
1)String是非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。它是典型的Immutable 类,被声明成为final class,所有属性也都是final 的。也由于它的不可变性,类似拼接、裁剪字符串等动作,都会产生新的String 对象。由于字符串操作的普遍性,所以相关操作的效率往往对应用性能有明显影响。
2)StringBuffer就是为了解决String的拼接操作产生太多中间对象的问题,它是Java 1.5中新增的,我们可以用append 或者add 方法,把字符串添加到已有字符串的末尾或者指定位置。StringBuffer本身是线程安全的,所以带来一些额外的线程同步开销,因此除非有线程安全的需要,否则还是推荐使用StringBuilder。 - 字符串设计和实现考量
- String 是Immutable 类,所有线程无法对它内部数据进行修改,原生保证了线程安全。
- StringBuffer 和StringBuilder 底层都是使用“动态扩容”的字符数组(JDK9以后是byte数组),该数组默认大小为16,当数组满了的时候会创建一个更大的数组然后将原数组中的内容复制到新数组,底层数组扩容开销较大,最好可以预估要存储的字符数量。二者都继承了AbstractStringBuilder,里面包含了基本操作,区别仅在于最终的方法是否加了synchronized。
- 在实际编程中,字符串拼接可以直接使用String进行拼接,因为从JDK8开始,String拼接会被编译成创建一个StringBuilder然后去进行拼接,拼接完后调用toString作为结果。
- 字符串缓存
- String在Java6以后提供intern()方法,目的是提示JVM把相应字符串缓存起来,以备重用,如果字符串常量池中存在与字符串对象内容相同的字符串常量那么intern方法返回就是字符串常量的地址。一般来说,JVM会将类似"abc"这样的字符串常量缓存在字符串常量池中。
- 在使用字符串常量的时候,JVM会调用字符串常量对象的intern方法,检测字符串常量池中是否缓存该字符串常量如果没有那么创建一个字符串对象缓存在字符串常量池中。
- 但是在JAVA6中,字符串常量被缓存在永久代中,而永久代的空间很有限的并且很少能被垃圾回收照顾到,如果使用不当很容易出现OOM。
- 在Java8中,永久代被MetaSpace元数据区替代,默认大小也扩大了很大,常量池也迁移到了堆中。
- String的进化
在JDK9中,String底层改为使用byte数组存储字符,带来的好处就是更小的内存占用、更快的操作速度。