String、StringBuffer与StringBuilder的区别,及实现原理

本文深入探讨了Java中String、StringBuilder和StringBuffer的内部实现机制,对比了它们在字符串拼接、修改操作上的效率和线程安全性。分析了String的不可变性、StringBuilder的高效性和StringBuffer的线程安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

源码分析

String使用final关键字修饰可以知道String是不可变的类,String中字符数组的长度你定义多少,就是多少,不存在字符数组扩容一说。内部是final修饰的char[] value,表示String类不可被继承,且value只能被初始化一次。这里的value变量其实就是存储了String字符串中的所有字符。
StringBuffer和StringBuilder二者的源码以及append方法,二者都是AbstractStringBuilder的子类,也都实现了Serializable(可序列化)和CharSequence(char类型的可读序列)接口,AbstractStringBuilder 实现了Appendable(长度可增加)和CharSequence接口。

//String
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
    private final char value[];
    public String substring(int beginIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        int subLen = value.length - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
    }
}
//StringBuilder
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence{
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}
    
//StringBuffer
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence{
    @Override
    private transient char[] toStringCache;
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
}

//AbstractStringBuilder 
abstract class AbstractStringBuilder implements Appendable, CharSequence{
	char[] value;
	int count;
	AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
}
    

String中单的subString方法在最后会使用new关键字创建一个新的对象,如果传入0也是new一个新的和原来相同长度相同内容的字符串返回。其他对字符串的操作方法也是创建一个新的String对象返回
StringBuffer上使用了synchronized 关键字加了同步锁证明它是线程安全的,而StringBuilder没有使用说明是线程不安全的

StringBuilder原理分析

内部调用父类的构造方法,内部是容量为16的字符数组。
StringBuilder的构造方法、append()和toString()方法

	public StringBuilder(int capacity) {
        super(capacity);
    }
        public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }
     public StringBuilder(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }
        @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
      @Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

在参数String类型的构造方法中,在本身字符串长度的基础上再增加16个字符长度,作为StringBuilder实例的初始数组容量,并将str字符串 append到StringBuilder的数组中。
这里的toString方法直接new 一个String对象,将StringBuilder对象的value进行一个拷贝,重新生成一个对象,不直接操作StringBuilder的value。
StringBuilder没有像String一样去重新new 对象,所以在频繁的拼接字符上省去了new 关键字的StringBuilder,不必每次都要重新开辟新的内存空间,其效率远远高于String类。

StringBuffer原理分析

StringBuffer是线程安全的高效字符串操作类,在StringBuilder上加了锁机制
StringBuilder的构造方法、append()和toString()方法

	public StringBuffer() {
        super(16);
    }
    public StringBuffer(int capacity) {
        super(capacity);
    }
     public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }
     public StringBuffer(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }
     @Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
     public synchronized StringBuffer append(StringBuffer sb) {
        toStringCache = null;
        super.append(sb);
        return this;
    }
     @Override
    synchronized StringBuffer append(AbstractStringBuilder asb) {
        toStringCache = null;
        super.append(asb);
        return this;
    }
     @Override
    public synchronized StringBuffer append(CharSequence s) {
        toStringCache = null;
        super.append(s);
        return this;
    }
     @Override
    public synchronized StringBuffer append(CharSequence s, int start, int end)
    {
        toStringCache = null;
        super.append(s, start, end);
        return this;
    }

    @Override
    public synchronized StringBuffer append(char[] str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public synchronized StringBuffer append(char[] str, int offset, int len) {
        toStringCache = null;
        super.append(str, offset, len);
        return this;
    }
	@Override
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }

StringBuffer比StringBuilder多了一个toStringCache域,用以去除toString方法的缓存
作用就是如果StringBuffer对象此时存在toStringCache,在多次调用其toString方法时,其new出来的String对象是会共享同一个char[] 内存的,达到共享的目的。但是StringBuffer只要做了修改,其toStringCache属性值都会置null处理。

总结

String 类不可变,内部维护的char[] 数组长度不可变,为final修饰,String类也是final修饰,不存在扩容。字符串拼接,截取,都会生成一个新的对象。频繁操作字符串效率低下,因为每次都会生成新的对象。
StringBuilder 类内部维护可变长度char[] , 初始化数组容量为16,存在扩容, 其append拼接字符串方法内部调用System的native方法,进行数组的拷贝,不会重新生成新的StringBuilder对象。
它是非线程安全的字符串操作类, 其每次调用 toString方法而重新生成的String对象,不会共享StringBuilder对象内部的char[],会进行一次char[]的copy操作。
StringBuffer 类内部维护可变长度char[], 基本上与StringBuilder一致,但其为线程安全的字符串操作类,大部分方法都采用了Synchronized关键字修改,以此来实现在多线程下的操作字符串的安全性。
其toString方法而重新生成的String对象,会共享StringBuffer对象中的toStringCache属性(char[]),但是每次的StringBuffer对象修改,都会置null该属性值。

部分摘自:https://www.jianshu.com/p/64519f1b1137

在 Java 中,`String`, `StringBuilder` 和 `StringBuffer` 是处理字符串的三个常用类。它们各有特点,适用于不同的应用场景。下面是关于这三个类的主要区别的详细介绍: ### 1. **不可变性 vs 可变性** - **String** - 特点:`String` 类的对象是**不可变**的(immutable),即每次对 `String` 对象的操作都会创建新的对象。 - 示例: ```java String str = "Hello"; str += " World"; // 实际上生成了一个新对象:"Hello World" ``` - **StringBuilder & StringBuffer** - 特点:这两个类都是**可变**的(mutable),可以在原对象基础上直接修改内容而不必每次都新建对象。 - 这使得频繁操作字符串时效率更高。 ### 2. **线程安全** - **String** - 线程安全否并不适用,因为它本身就是不可变的,所以不用担心多线程环境下的同步问题。 - **StringBuffer** - 提供了内置的线程安全保障机制,所有的方法都被 synchronized 关键字修饰,因此它是线程安全的。 - 因此,在多线程环境中使用更为合适,但由于锁机制的存在,性能相对较低。 - **StringBuilder** - 没有提供任何形式的线程安全性措施,所以在单线程环境下运行速度更快。 - 单线程或不需要考虑并发的情况下推荐优先选择 `StringBuilder`。 ### 3. **性能对比** 由于 `StringBuffer` 的所有方法都带有同步控制,所以在高频率更新字符串的情景下,它的开销比 `StringBuilder` 要大得多。相比之下,`StringBuilder` 更加高效,但在需要保证线程安全的地方就不如前者。 #### 性能测试示例: ```java public class PerformanceTest { public static void main(String[] args) throws InterruptedException { long start; StringBuilder sb = new StringBuilder(); start = System.currentTimeMillis(); for (int i = 0; i < 50000; ++i) sb.append("a"); System.out.println("Time taken by StringBuilder: " + (System.currentTimeMillis() - start)); StringBuffer sf = new StringBuffer(); start = System.currentTimeMillis(); for (int i = 0; i < 50000; ++i) sf.append("a"); System.out.println("Time taken by StringBuffer: " + (System.currentTimeMillis() - start)); // 测试 String (这里仅做展示目的) String s = ""; start = System.currentTimeMillis(); for (int i = 0; i < 50000; ++i) s += "a"; System.out.println("Time taken by String concatenation: " + (System.currentTimeMillis() - start)); } } ``` 从上述代码可以看出,在循环中进行大量拼接操作时,`StringBuilder` 明显快于 `StringBuffer`,而 `String` 的表现最差。 综上所述, - 如果你需要一个简单的字符串并且不打算更改它,那就用 `String`. - 若是在单一任务内构建大量的动态文本,请选用 `StringBuilder`. - 当涉及到多线程并行访问同一个缓冲区的情况,则应采用 `StringBuffer`.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值