文章目录
String、StringBuilder、StringBuffer的区别和联系
1、String的优点和缺点
说起String的缺点,可能大家一开始就会想到它的不可变O(∩_∩)O,但是说到String的优点,可能就有点想不起来了😆。其实它的优点也在于它的不可变。所以我们本节主要了解
String
为什么不可变,以及不可变的好处。注意:一般我们说的String不可变,都是使用双引号创建的String对象不可变
String的不可变主要有以下两个点:
final
修饰。String类的底层是使用数组实现的,而该数组是使用final
修饰的,导致我们无法进行重新赋值private
修饰。String类的底层实现该数组是使用private
修饰,同时String类没有向外部提供操作它的方法
-
不可变的优点:
-
能够使用常量池。由于String的不可变性,我们无法修改String对象的值,这就能够让我们安心使用常量池了。
String使用双引号创建的对象被存放在常量池中,常量池中的的对象会被很多其它对象引用,假如是可变的,当我们改变String对象的值时,就会影响其它引用了,从而导致大量数据错误
-
能够复用哈希码。
哈希码是根据对象地址进行创建的,地址不变,哈希码就不会变。当我们计算了对象的哈希码时,会将其缓存,由于地址不会变,所以再次使用就能够直接使用而无需计算
-
线程安全。不可变就意味着在多线程下使用,不会影响到其它线程的正常运行。
-
-
不可变的缺点:
- 占用内存。当我们在使用String对象是,每次进行字符串的修改,都会重新创建一个新的String对象,很废内存。
- 不够灵活。因为String对象比较原始,操作字符串的API相对而言不够强大,有时想要操作字符串需要写很长的代码。
2、StringBuilder和StringBuffer的区别
相同点:
-
所属家族相同,都属于
AbstractStringBuilder
类的子类 ,且都在Java.lang
包下 -
主要功能相同,都是用来优化对字符串的操作的。
传统的使用
+
进行字符串拼接会大大增加常量池的压力,比如:String st = "a"; String st += "b";
上面两行代码就在常量池中创建了三个字符:a、b、ab;而使用StringBuffer或StringBuilder就能避免这种情况的发生,同时它们还提供了很多好用的API用于简化操作字符串。
-
底层原理相同,都是调用父类的方法创建一个初始容量为16的
byte[]
数组。StringBuilder
和StringBuffer
底层原理都是是创建一个初始容量为16的byte[]数组,该数组没有被final修饰,当容量超标时就会重新创建一个新的数组进行扩容,先将原来数组的内容拷贝到新数组中,然后让String Buffer的指针(准确来说是对象的引用)从指向开始的数组变为指向扩容后的数组,开始的数组就会被垃圾回收器给回收。
不同点:
-
适用场景不同,
Stringbuffer
适用于多线程,StringBuilder
适用于单线程。Stringbuffer
对象中所属有的方法都有synchronized
关键字修饰,都是同步方法,能够避免多线程下同时使用StringBuffer
对象产生的线程安全问题;而StringBuilder
对象中的方法都是普通方法,存在线程安全。 -
性能不同,
StringBuilder
的性能高于StringBuffer
。由于
StringBuffer
的所有方法都是同步方法,每次调用,都需要进行上锁、解锁, 大大降低了性能。
拓展:
-
如何优化StringBuffer的性能?
在使用StringBuffer时,可以预估一下字符串的容量,然后在初始化时指定一个稍微大一点容量的StringBuffer对象。
总结
类型 | 特点 | 适用场景 |
---|---|---|
String | 不可变,线程安全 | 操作少量数据或者不需要操作数据1 |
StringBuilder | 可变,线程不安全 | 需要频繁操作数据且不用考虑线程安全 |
StringBuffer | 可变,线程安全,性能较低 | 需要频繁操作数据且需要考虑线程安全 |
总的来讲String、StringBuilder、StringBuffer虽然是一步一步优化过来的,但是鱼与熊掌不可兼得,优化了这个问题又随之出现另一个问题,所以没有一个是完美的,也没有一个是能够取代其它任意一个的位置的,它们各司其职。我们要做的就是能够利用它们的特点解决某一类型的问题😄
推荐阅读:JavaSE知识【回顾+总结】
不需要操作数据的含义,就是简单的将String对象当作一个容器 ↩︎