面试官:“小伙子,在日常的写代码过程中,使用过String,StringBuilder和StringBuffer没?”
我:“用过的呀!”
面试官:“那你就来聊一聊,他们之间有什么区别,不同场景下如何选择吧”
我:“好嘞!”
在Java的开发过程中,使用频率最高的就是String字符串,但由于在字符串存储和拼接的过程中,涉及到很多场景需要因地制宜的选用StringBuider与StringBuffer。
我们先来聊一聊String,由源码引入话题:
Java8中的String源码- public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
- private final char value[];
- //...
- }
复制代码 我们从源码中可以总结出如下几点内容:
- String类被final关键字修饰,意味着它不可被继承;
- String的底层采用了final修饰的char数组,意味着它的不可变性;
- 实现了Serializable接口意味着它支持序列化;
- 实现了Comparable接口,意味着字符串的比较可以采用compareTo()方法,而不是==号,并且Sring类内部也重写了Object的equals()方法用来比较字符串相等。
Java9以后String的底层数据由char类型改为了byte类型,看源码:- public final class String
- implements java.io.Serializable, Comparable<String>, CharSequence {
- @Stable
- private final byte[] value;
- private final byte coder;
- private int hash;
- }
复制代码 这样做的目的是为了节省字符串占用的内存空间,在大字符串处理中提升效率,同时也降低了垃圾回收的次数,提升性能。
面试官:“String类设置为不可变对象的话,岂不是每次创建或者字符串拼接的时候都会创建对象,增加开销?那为什么还要用final修饰呢?”
我:“嗯~这个问题非常好呀,我理解的是两个原因”- 1、String 类是最常用的类之一,为了效率,禁止被继承和重写
- 2、为了安全。String 类中有很多调用底层的本地方法,调用了操作系统的API,
- 如果方法可以重写,可能被植入恶意代码,破坏程序。其实Java 的安全性在这里就有一定的体现啦。
复制代码 基于以上String的特点,Java官方自然也知道它的弊端,所以为了全场景的最优方案适配,便产生了StringBuilder和StringBuffer这两个String的近亲字符串处理类,他们都实现了CharSequence 接口。
StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串,不过没有使用 final 和 private 关键字修饰,所以可以解决在字符串拼接时的性能问题,最关键的是这个 AbstractStringBuilder 类还提供了很多修改字符串的方法,比如 append 方法。
源码:- abstract class AbstractStringBuilder implements Appendable, CharSequence {
- char[] value;
- public AbstractStringBuilder append(String str) {
- if (str == null)
- return appendNull();
- int len = str.length();
- ensureCapacityInternal(count + len);
- str.getChars(0, len, value, count);
- count += len;
- return this;
- }
- //...
- }
复制代码 StringBuilder源码- public final class StringBuilder extends AbstractStringBuilder
- implements java.io.Serializable, CharSequence
- {
- // ...
- public StringBuilder append(String str) {
- super.append(str);
- return this;
- }
- public String toString() {
- // Create a copy, don't share the array
- return new String(value, 0, count);
- }
- // ...
- }
复制代码 StringBuilder替代String的字符串拼接案例
Java 是一门解释型的编程语言,所以当编译器遇到 + 号这个操作符的时候,会将- new String("Hello") + new String("World")
复制代码 解释为- new StringBuilder().append("Hello").append("World").toString();
复制代码 并且在循环字符串拼接的时候,String的“+”拼接方式,会在循环内部多次创建StringBuilder对象,白白浪费了资源,如:- String[] arr = {"he", "llo", "world"};
- String s = "";
- for (int i = 0; i < arr.length; i++) {
- s += arr[i];
- }
- System.out.println(s);
复制代码 这一段代码完全就可以直接使用StringBuilder调用append实现方式来替代:- String[] arr = {"he", "llo", "world"};
- StringBuilder s = new StringBuilder();
- for (String value : arr) {
- s.append(value);
- }
- System.out.println(s);
复制代码 StringBuffer源码- public final class StringBuffer extends AbstractStringBuilder implements Serializable, CharSequence {
- public StringBuffer() {
- super(16);
- }
-
- public synchronized StringBuffer append(String str) {
- super.append(str);
- return this;
- }
- public synchronized String toString() {
- return new String(value, 0, count);
- }
- // 其他方法
- }
复制代码 从它的源码我们可以看出,内部的实现与StringBuilder相差无机,无非是在方法的前面加了synchronized 关键字进行了同步,以确保在多线程场景下的线程安全,但在大部分的非多线程场景下,由于同步锁的存在会导致执行效率过低,并且即便在多线程情况下,依旧可以通过ThreadLocal搭配StringBuilder的方式来安全的修改字符串,所以,实际开发中,StringBuilder 的使用频率也是远高于 StringBuffer,甚至可以这么说,StringBuilder 完全取代了 StringBuffer。
三者异同点总结- 相同点:
- 1、都可以储存和操作字符串
- 2、都使用 final 修饰,不能被继承
- 3、提供的 API 相似
- 异同点:
- 1、String 是只读字符串,String 对象内容是不能被改变的
- 2、StringBuffer 和 StringBuilder 的字符串对象可以对字符串内容进行修改,在修改后的内存地址不会发生改变
- 3、StringBuilder 线程不安全;StringBuffer 线程安全
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |