String 是不可变的,而 StringBuffer 和 StringBuilder 是可变类。
StringBuffer多线程安全,但是加了synchronized,其效率低,故适用于多线程下,并发量不是很高的场景。
StringBuilder没有加任何锁,其效率高,适用单线程场景。
可变与不可变的区别
String是final类不能被继承且为字符串常量,而StringBuilder和StringBuffer均为字符串变量。
java中String的使用可以说是最多的,而且很多是作为常量反复使用,所以有String的常量池的实现。
像基本类型Integer、Long这些,也都设置了各自的常量池(通常是-128~127),覆盖一些常用的数字范围,目的就是避免创建大量无意义的对象。
而String的常量池由于不能预判用户经常会使用哪些字符串,所以不能像Integer一样初始化一个范围。
所以String的常量池在声明一个String时,它会进入常量池中找这个字符串,如果没有,就直接new一个String对象,同时将这个对象投入到常量池。
那么如果后面又有其他地方用到了这个字符串,就会直接使用第一次new出来的对象。
如果String是可变长的,那就实现不了常量池了,所以String是不可变的。
所以String类不适合于频繁修改的字符串操作上,所以在这种情况下,往往可以使用StringBuffer类,即StringBuffer类方便用户进行内容修改。
线程安全性的区别
先说结论,String:线程安全,StringBuffer:线程安全,而StringBuilder:是线程不安全的。
为什么?String、StringBuffer线程安全?StringBuilder线程不安全?
1)首先,我们先看String
对于String来说,是把数据存放在了常量池中,因为所有的String,默认都是以常量形式保存,且由final修饰,因此在线程池中它是线程安全的。
因为每一个String当被创建好了以后,他就不再发生任何变化,所以是线程安全的。
2)其次,我们再看StringBuffer和StringBuilder
StringBuffer 的所有公开方法都是 synchronized 修饰的,具体示例如下:
@Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; }
StringBuilder是没有加所示的:
@Override public StringBuilder append(String str) { super.append(str); return this; }
所以,在多线程的情况下,StringBuffer 是线程安全的,而 StringBuilder 并没有 Synchronized修饰,是线程不安全的。
缓存区的区别
StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串,如下代码示例:
@Override public synchronized String toString() { if (toStringCache == null) { toStringCache = Arrays.copyOfRange(value, 0, count); } return new String(toStringCache, true); }
而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串,如下代码示例:
@Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
所以,缓存冲这也是对 StringBuffer 的一个优化吧,不过 StringBuffer 的这个toString 方法仍然是同步的。
性能的区别
性能:运行速度快慢为:StringBuilder > StringBuffer > String。
为什么String最慢?
因为String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。
看一段代码示例:
String str="abc"; System.out.println(str); str=str+"de"; System.out.println(str);
JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,然后在第三行中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了。
str实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。
所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。
为什么StringBuilder > StringBuffer?
原因很简单:既然 StringBuffer 是线程安全的,它的所有公开方法都是同步的,方法上都加有Synchronized,所以每次获取方法都需要加锁然后再释放锁,所以性能会降低。
同样道理StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder 的性能要远大于 StringBuffer。
陈睿mikechen
10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!
后台回复【面试】即可获取《史上最全阿里Java面试题总结》,后台回复【架构】,即可获取《阿里架构师进阶专题全部合集》