關於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最快。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章