String、StringBuilder和StringBuffer的比较

1.String特性

1.1 不可变

它是Immutable类,被声明为final class,所有属性也是final的, 其拼接、裁剪等操作都会产生新的String对象。不可变的主要作用在于当一个对象需要被多线程共享,并且访问频繁时,可以省略同步和锁等待的时间,从而大幅度提高系统性能。不可变模式是一个可以提高多线程程序的性能,降低多线程程序复杂度的设计模式。所以String是原生线程安全的。

1.2 字符串常量池

String在Java世界中使用过于频繁,队系统性能的影响权重很大,所以JVM有针对性的优化。为了节约内存空间,防止相同字符串反复出现,避免创建重读字符串,引入了缓存字符串常量池,其机制涉及到String的创建方式。

  1. 通过直接量给String对象引用赋值来创建一个字符串时,JVM首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对象引用;如果没有则在池中新建字符串对象,返回对象引用,并且将新创建的对象放入池中。
  2. 采用new关键字新建一个字符串对象时,JVM首先检查池中有没有"xyz"这个字符串对象,如果有,则直接在堆中创建一个"xyz"字符串对象 ,不会把对象放入池中。如果没有,则首先在字符串池中创建一个"xyz"字符串对象,然后再在堆中创建一个"xyz"字符串对象
  3. Intern()方法。String提供了inter()显示方法提示JVM缓存字符串。 调用此方法后,如果池已经包含一个等于此String对象的字符串(equals(oject)),则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用。
String str1 = "123"; //通过直接量赋值方式,放入字符串常量池  
String str2 = new String(“123”);//通过new方式赋值方式,不放入字符串常量池

String s3 = new String("xyz");//通过new方式赋值方式,先在常量池创建xyz,再从堆中创建string对象,并将引用付给s3
String s4 = new String("xyz");//从堆中创建string对象,并将引用付给s4
System.out.println(s3==s4); //s3 != s4

2.StringBuilder和StringBuffer

两者都是实现AbstractStringBuilder抽象类,拥有几乎一致对外提供的调用接口。

2.1 区别

  1. String、StringBuilder和StringBuffer的在内存中的存储方式相同,不同点是StringBufer/StringBuilder对象的值是可以改变的,并且值改变以后,对象引用不会发生改变。
  2. 两者对象在构造过程中,首先按照默认大小申请一个字符数组,由于会不断加入新数据,当超过默认大小后,会创建一个更大的数组,并将原先的数组内容复制过来,再丢弃旧的数组。因此,对于较大对象的扩容会涉及大量的内存复制操作,如果能够预先评估大小,可提升性能。
  3. StringBufer是线程安全的,但是StringBuilder是线程不安全的。StringBufer类中方法定义前面都会有synchronize关键字加锁,性能要远低于StringBuilder。

2.2 应用场景

  1. 在字符串内容不经常发生变化的业务场景优先使用String类。例如:常量声明、少量的字符串拼接操作等。
  2. 如果有大量的字符串内容拼接,避免使用String与String之间的“+”操作,因为这样会产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。
  3. 在频繁进行字符串的运算(如拼接、替换、删除等),并且运行在多线程环境下,建议使用StringBufer,例如XML解析、HTTP参数解析与封装。运行在单线程环境下,建议使用StringBuilder,例如SQL语句拼装、JSON封装等。

参考
《Java核心技术36讲》
《深入理解JVM虚拟机》

posted @ 2020-03-03 15:30  chzhyang  阅读(449)  评论(0编辑  收藏  举报