1. 程式人生 > >深入理解String,StringBuilder,StringBuffer

深入理解String,StringBuilder,StringBuffer

首先明確什麼是不可變字元序列? 我們都知道在Java中, String類是不可變的。那麼到底什麼是不可變的物件呢? 可以這樣理解:如果一個物件建立完成之後,不能再改變它的狀態,那麼該物件就是不可變的。不能改變狀態的意思是,不能改變物件內的屬性,包括基本資料型別的值不能改變,引用型別指向的物件的狀態也不能改變。或者這樣理解就是物件狀態不可變,而引用是可再次指向其他物件的。 首先說明一點不是說String類被final修飾就是不可變的,而是成員變數(域)被final修飾 原始碼中是這樣體現的 /** The value is used for character storage. */ private final char value[]; 具體看如下程式碼執行結果(都是針對同一物件進行修改操作)

public class Demo01 {
	  public static void main(String[] args) {
			String s1=new String("abcdef");
			
			System.out.println(Integer.toHexString(s1.hashCode()));
			System.out.println(s1);
			
			s1.substring(3, 5);
			
			System.out.println(Integer.toHexString(s1.hashCode()));
			System.out.println(s1);
		}
}

記憶體地址為ab199863 abcdef 記憶體地址為ab199863 abcdef 對同一物件進行修改操作沒有發生改變即為不可變 為什麼StringBuilder與StringBuffer是可變的? 原始碼中是這樣體現的(摘自父類AbstractStringBuilder) /** * The value is used for character storage. */ char[] value;

public class Demo02 {
>         public static void main(String[] args) {
>     		StringBuilder sb=new StringBuilder("abcdef");
>     		
>     		System.out.println(Integer.toHexString(sb.hashCode()));
>     		System.out.println(sb);
>     		
>     		sb.setCharAt(0, 'A');
>     		
>     		System.out.println(Integer.toHexString(sb.hashCode()));
>     		System.out.println(sb);
>     	}
>     }

記憶體地址為15db9742 abcdef 記憶體地址為15db9742 Abcdef 對同一物件進行修改操作發生改變即為可變

接下來看三者間的效率 我們對5萬個數進行字串拼接效率如下 在這裡插入圖片描述 如圖可得,三者間的效率差距有多大 StringBuilder與StringBuffer的區別 StringBuilder:執行緒不安全,效率高 StringBuffer:執行緒安全:效率低

摘自JDK1.8 StringBuffer的append部分過載方法原始碼

@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;
}

由於append方法加了同步拼接時導致效率低 return this可以進行鏈式呼叫

根據effective java第51條規定: 通過字串連線符進行操作時,時間複雜度為O(n2),這是由於字串不可變而導致的結果,當兩個字串被連線在一起時,他們的內容都要被拷貝,相反,而應該使用StringBuilder的append方法,時間是線性的,另一種方法是使用字元陣列,或者每次只處理一個字串

可變即可擴容

void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    } 

擴容實際上就是陣列的拷貝