1. 程式人生 > >String、StringBuffer、StringBuilder之間的區別

String、StringBuffer、StringBuilder之間的區別

①執行/執行速度:

StringBuilder > StringBuffer > String

  String StringBuffer StringBuilder
執行速度 3 2 1
  字串常量 字串變數 字串變數
執行緒安全 安全 安全 不安全
  不可變字元序列 可變字元序列 可變字元序列

String:適用於少量的字串操作的情況;

StringBuffer:適用於多執行緒下在字串緩衝區進行大量操作的情況;

StringBuilder:支援向其中新增新字元[無需建立新物件,append()方法追加],適用於單執行緒下在字串緩衝區進行大量操作的情況;

 

String不可變原因:

被final修飾。

 

 例:

1 String str="abc";
2 System.out.println(str);
3 str=str+"de";
4 System.out.println(str);

如果執行這段程式碼會發現先輸出“abc”,然後又輸出“abcde”,好像是str這個物件被更改了,其實,這只是一種假象罷了,JVM對於這幾行程式碼是這樣處理的,首先建立一個String物件str,並把“abc”賦值給str,然後在第三行中,其實JVM又建立了一個新的物件也名為str,然後再把原來的str的值和“de”加起來再賦值給新的str,而原來的str就會被JVM的垃圾回收機制(GC)給回收掉了,所以,str實際上並沒有被更改,也就是前面說的String物件一旦建立之後就不可更改了。

所以說,Java中對String物件進行的操作實際上是一個不斷建立新的物件並且將舊的物件回收的一個過程,所以執行速度很慢。

 

1 String str="abc"+"de";
2 StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");
3 System.out.println(str);
4 System.out.println(stringBuilder.toString());

這樣輸出結果也是“abcde”和“abcde”,但是String的速度卻比StringBuilder的反應速度要快很多,這是因為第1行中的操作和

String str="abcde";是完全一樣的,所以會很快,而如果寫成下面這種形式

1 String str1="abc";
2 String str2="de";
3 String str=str1+str2;

那麼JVM就會像上面說的那樣,不斷的建立、回收物件來進行這個操作了。速度就會很慢。

 常見面試題:

String str = new String ("abc") 建立了幾個物件?

答:2個。[比較合理]

原因:

這段程式碼在執行過程中建立了一個String物件;在類載入的過程中,在執行時常量池中建立了一個“abc”物件;

問清面試官是這段程式碼在執行過程中建立了多少個物件?還是涉及到多少個物件?

下面這段程式碼的輸出結果是什麼?

public class test {
	public static void main(String[] args) {
		String a = "hello";
		String b = new String ("hello");
		String c = new String ("hello");
		String d = b.intern();
		System.out.println(a == b);//false
		System.out.println(b == c);//false
		System.out.println(b == d);//false
		System.out.println(a == d);//true
		//===========================================
		String a1 = "hello2";
		String b1 = "hello" + 2;
		System.out.println("第一個結果:" + (a1 == b1));//true
		/* 
		 * 在程式編譯期,JVM就將常量字串的"+"連線優化為連線後的值,拿"hello" + 2來說,
		 * 經編譯器優化後在class中就已經是a1。在編譯期其字串常量的值就確定下來,故上面程式最終的結果都為true。
		 * */
		//===========================================
		String a2 = "hello2";
		String b2 = "hello";
		String c2 = b2 +2;
		System.out.println("第二個結果:" + (a2 == c2));//false
		/*
		 * JVM對於字串引用,由於在字串的"+"連線中,有字串引用b2存在,而引用b2的值在程式編譯器是無法確認的,
		 * 即"hello" + 2無法被編譯器優化,只有在程式執行期來動態分配並將連線後的新地址賦給b2.
		 * 所以結果也就為false。
		 * */
		//===========================================
		String a3 = "hello2";
		final String b3 = "hello";
		String c3 = b3 +2;
		System.out.println("第三個結果:" + (a3 == c3));//true
		/*
		 * 和上面不同的是,b3字串加了final修飾,對於final修飾的變數,
		 * 它在編譯時被解析為常量值的一個本地拷貝儲存到自己的常量池中或嵌入到它的位元組碼流中。
		 * 所以此時的b3 +2和"hello" + 2效果是一樣的。
		 * */
		//===========================================
		String a4 = "hello2";
		final String b4 = getHello();
		String c4 = b4 +2;
		System.out.println("第四個結果:" + (a4 == c4));//false
		/*
		 * JVM對於字串引用b4,它的值在編譯期無法確定,雖然將b4用final修飾,但由於其賦值是通過方法呼叫返回的,
		 * 只有在程式執行期呼叫方法後,才能確認它的值,
		 * 將方法的返回值和2來動態連線並分配地址為s2,故上面 程式的結果為false。
		 * */
	}

	private static String getHello() {
		return "hello";
	}

}

在String類中,intern()方法是一個本地方法,該方法將會在執行時常量池中查詢是否存在內容相同的字串。若存在,則返回指向該字串的引用;若不存在,則會將該字串入池,並返回一個指向該字串的引用。

圖解:

 

延伸:

Java中堆、棧、常量池的關係及應用

棧:存放基本資料型別和物件的引用,但物件本身不存放在棧中,而是存放在堆中;

堆:存放用new產生的物件/資料;

常量池:存放基本型別常量和字串常量;