关于String、StringBuffer、StringBuilder的区别

最近看到了一道题目:
在这里插入图片描述
正确答案:C

下面就这题的其他选项给出解析:

String

String不可变!先来看看String的源码:

public final class String implements Serializable, Comparable<String>, CharSequence {
    private final byte[] value;
    private final byte coder;
    private int hash;
    private static final long serialVersionUID = -6849794470754667710L;
    .......}

1、String由final修饰,说明final不能被继承,不能被修改
2、String用来存储数据的是字节数组 byte[] ,同样也是由final修饰的。
3、虽然字节数组value是由final修饰,但是我们要清楚一个原则就是:String是引用型变量,要清楚在String的数组是放在堆中的,然后将栈中放的是数组在堆中的引用地址,而我们通常所用的就是一个指向堆中真实数组数据的一个引用地址,所以也称String为引用型对象。简而言之:引用地址不可变,但是地址指向的堆中的数组数据是可以改变的。一旦创建了一个String对象,就在内存中申请一片固定的地址空间存放数据,不管怎么变,地址是不会变的,但是地址所指向的空间真实存放的数据是可以改变的。再通俗点就是:你家的门牌号不会变,但是你家要住几口人是你说了算。
下面通过一个Demo来说明:

public class Main {
    public static String strAppend(String str){
        str += "String";
        return str;
    }

    public static StringBuilder sbAppend(StringBuilder str){
        return str.append("StringBuilder");
    }

    public static void testBuilder() {
        String s1 = new String("ysy ");
        String s2 = Main.strAppend(s1);
        System.out.println("String不可改变");
        System.out.println(s2.toString());
        System.out.println(s1.toString());

        System.out.println("StringBuilder可以改变:");
        StringBuilder sb1 = new StringBuilder("string");
        StringBuilder sb2 = Main.sbAppend(sb1);

        System.out.println(sb2.toString());
        System.out.println(sb1.toString());
    }
    
    public static void main(String[] args) {
        testBuilder();
    }
}

在这里能看出来String追加字符串后能新的字符串后,原来的字符串并没有发生改变。
但是StringBuilder修改后就改变了自身的值。

Sting不可变的意义何在:

  1. 字符串常量池的需要
  2. 允许String对象缓存HashCode
  3. 安全性
    在这里可以参考一位大佬的博客为什么String要设计成不可变的? 说的很详细。

StringBuffer是线程安全

先来看下StringBuffer的源码:

   public synchronized StringBuffer append(String str) {
        this.toStringCache = null;
        super.append(str);
        return this;
    }

注意下里面的关键字synchronize,线程安全。也可以通过下面一个Demo来例证:
先创建多个线程,然后在每一个线程中对一个变量进行自增多次,最后结果一定的话就说明这个是加了线程锁的,也就是线程安全的。

public class Main {
    public static void main(String[] args) {
//        testBuilder();
        testBuffer();
    }
    public static void testBuffer(){
        StringBuffer buffer = new StringBuffer();
        for(int i = 0; i < 100; i++ ){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j <1000; j++){
                        buffer.append("j");
                    }
                }
            }).start();
        }
        try {
            Thread.sleep(100);
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        System.out.println("线程安全"+buffer.length());
    }
}

在这里创建了100个线程,每个线程中对StringBuffer的对象append的一个字符,最后整个字符串的长度一定为100000,所以可以看出来StringBuffer的线程安全的。

StringBuilder线程不安全

同样将这个方法跟StringBuffer进行对比,结果是不一定的,能够看出StringBuilder是线程不安全的。

    public static void testBuilder() {
        StringBuilder builder = new StringBuilder();
        for(int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int j = 0; j < 1000; j++) {
                        builder.append("a");
                    }
                }
            }).start();
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.print("线程不安全:"+builder.length());
    }

String、StringBuffer、StringBuilder三者的速度

速度:String最慢,StringBuffer次之,StringBuilder最快。


版权声明:本文为qq_41377858原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。