String类
每周学会一个小知识点(第一周第一天)
String StringBuffer StringBuilder 的区别
-
可变性 ,String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
-
线程安全性,String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuffer 和 StringBuilder 的公共父类,提供了好多操作字符串的公共方法(expandCapacity,append,insert,indexOf),StringBuffer 给方法加了同步锁或者对调用的方法加了同步锁,所以线程安全。StringBuilder 没有对方法加同步锁,所以是非线程安全的。
-
性能,每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。StringBuffer 每次都会对StringBuffer 本身进行操作,而不是生成新的对象并改变对象的引用,相同的情况下使用StringBuilder相比使用StringBuffer仅能获得10%到15%左右的性能提升,但却要冒着线程不安全的风险。
什么时候使用 String StringBuffer StringBuilder(推荐)
String 适用少量字符串操作的情况
StringBuffer 适用多线程在字符缓冲区进行大量操作的情况
StringBuilder 使用单线程在字符缓冲区进行大量操作的情况 (使用这个就行啦)
引用一个大佬的回答
我不知道为什么这个这么老的问题会出现在我的时间线上,看了一下回答,大多是2012,2013年的回答,照说那个年代,有些历史故事还很新鲜,却不知道为什么没有一个答案说到点子上。
stringbuffer固然是线程安全的,stringbuffer固然是比stringbuilder更慢,固然,在多线程的情况下,理论上是应该使用线程安全的stringbuffer的。
然而,然而,然而,有谁给我一个实际的案例来显示你需要一个线程安全的string拼接器?对不起,至少在我浅薄的十几年编程生涯中还没有遇到过,也许,仅仅是也许,这个地球上的确是存在这样的编程需求的,然而,它至少跟99.99…99%的程序员是无关的。所以,对于题主的问题,他们的适用场景是什么?最简单的回答是,stringbuffer基本没有适用场景,你应该在所有的情况下选择使用stringbuiler,除非你真的遇到了一个需要线程安全的场景,如果遇到了,请务必在这里留言通知我。
然后,补充一点,关于线程安全,即使你真的遇到了这样的场景,很不幸的是,恐怕你仍然有99.99…99%的情况下没有必要选择stringbuffer,因为stringbuffer的线程安全,仅仅是保证jvm不抛出异常顺利的往下执行而已,它可不保证逻辑正确和调用顺序正确。大多数时候,我们需要的不仅仅是线程安全,而是锁。
最后,为什么会有stringbuffer的存在,如果真的没有价值,为什么jdk会提供这个类?
答案太简单了,因为最早是没有stringbuilder的,sun的人不知处于何种愚蠢的考虑,决定让stringbuffer是线程安全的,然后大约10年之后,人们终于意识到这是一个多么愚蠢的决定,意识到在这10年之中这个愚蠢的决定为java运行速度慢这样的流言贡献了多大的力量,于是,在jdk1.5的时候,终于决定提供一个非线程安全的stringbuffer实现,并命名为stringbuilder。顺便,javac好像大概也是从这个版本开始,把所有用加号连接的string运算都隐式的改写成stringbuilder,也就是说,从jdk1.5开始,用加号拼接字符串已经没有任何性能损失了。
-----------补充一个小小的修改----------如诸多评论所指出的,我上面说,"用加号拼接字符串已经没有任何性能损失了"并不严谨,严格的说,如果没有循环的情况下,单行用加号拼接字符串是没有性能损失的,java编译器会隐式的替换成stringbuilder,但在有循环的情况下,编译器没法做到足够智能的替换,仍然会有不必要的性能损耗,因此,用循环拼接字符串的时候,还是老老实实的用stringbuilder吧。