欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

String、StringBuffer、StringBuilder的区别及使用

程序员文章站 2024-01-20 22:14:40
...

不可变类 - String

一如既往,在进行学习之前我们先看看相关的定义吧~下面引自 JavaDocs:

Strings are constant; their values cannot be changed after they are created

String 类是不可变的,String 对象的值在创建后不会发生改变。换句话说,我们平常对 String 对象的操作,实际上都是创建了一个新的 String 对象,将该对象的引用赋给我们的对象,而内存之中仍然存有原来的两个字符串。可能有点绕,大家可以看看下面的代码:

public class Demo {
    public static void main(String[] args) {
        String str;
        String changeStr;

        str = "chaos";
        changeStr = "";

        System.out.println("初始");
        System.out.println(str);
        System.out.println(changeStr);

        changeStr = change(str);
        System.out.println("改变后");
        System.out.println(changeStr);
        System.out.println(str);
    }

    private static String change(String str){
        str = str  + "change";
        return str;
    }
}

输出:

初始 
chaos

改变后 
chaoschange 
chaos

我们可以看到,change() 方法并没有改变 test 的值。为什么呢?因为实际上,传给 change() 方法的是引用的一个拷贝,原对象并没有作为参数被方法修改。

所以 String 具有一个大坑:如果我们没有注意到 String 类的这个特性,将多个频繁发生改变的字符串的对象类型选为 String的话,内存一定会受此影响,从而影响系统性能;此外,当引用过多,还会出发垃圾回收机制,影响性能。

线程安全的 StringBuffer

A thread-safe, mutable sequence of characters. A string buffer is like a String, but can be modified.

StringBuffer 和 String 的最大区别在于,StringBuffer 是一个可变类,换句话说,StringBuffer 中的值不但可以改变,还不会产生 String 类的副作用(不断创建对象)。此外,StringBuffer 还是线程安全的。例如:

public class Demo {
    public static void main(String[] args) {
        String str;

        StringBuffer sb = new StringBuffer();
        StringBuffer changeSb = new StringBuffer();

        str = "chaos";
        sb.append(str);

        System.out.println("初始");
        System.out.println(sb.toString());

        changeSb = sb;
        changeSb.append("change");
        System.out.println("改变后");
        System.out.println(sb.toString());
        System.out.println(changeSb.toString());
    }
}

输出:

初始 
chaos 
改变后 
chaoschange 
chaoschange

所以如果我们的程序中如果存在需要频繁修改值的字符串,最好是使用 StringBuffer,因为除了刚刚提到的“线程安全”和“可变”两个特性以外,每个 StringBuffer 还具有一定的缓冲区容量,只有当字符串大小超过容量上限才会增加容量大小。

那么这样看下来,很多人会觉得 StringBuffer 很完美,以后就用它了!对于这样的同学我想说一句:且慢!!!事实上,StringBuffer 虽然看起来很完美,但它的线程安全实现机制是它最大的弱点,我们先来看看它是怎么实现线程安全的:

@Override
    public synchronized StringBuffer append(boolean b) {
        toStringCache = null;
        super.append(b);
        return this;
    }

我们可以看到,StringBuffer 通过 synchronized 关键字为方法加上同步锁,从而实现线程安全。但如果我们 append 1w 次,那么程序就要负担 1w 次加锁解锁带来的时间消耗,影响了效率。此外,大部分情况下,我们都是在单线程情况下使用 String,不需要考虑到线程安全问题,这就使得 StringBuffer 的加锁/解锁过程为程序带来了不必要的开支。所以在 Effective Java 中就有这么一句话:

Java 1.5发行版本中增加了非同步 StringBuilder 类,代替了现在已经过时的 StringBuffer 类

非线程安全的 StringBuilder

事实上,StringBuilder 的内部实现和 StringBuffer 几乎一样,唯一的区别在于:StringBuilder 中去掉了 synchronized 关键字,失去了线程安全的特性。

我们刚刚也提到了,StringBuffer 的线程安全机制在大多数情况下都不必要,为系统带来的开销完全是不必要的,所以 Java 1.5 发行版本推出了 StringBuilder。

其他方面倒没什么区别,所以最佳方案应该是使用 StringBuilder,如果需要考虑线程安全问题,再考虑 StringBuffer,或者重写 StringBuilder 的相应方法实现线程安全。


转自http://blog.csdn.net/u012403246/article/details/45787585