今天就來談談字串吧。
相信以下這段程式是大家學Java第一天就會寫的:
class HelloWorld{
public static void main(String[] args){
System.out.println("Hello World!");
}
}
我們用Java寫出了讓電腦和世界打招呼的程式呢。裡頭的"Hello World!"就是一個字串,前幾天我們有提到過,字串類別沒有基本型別(primitive type)可以用,只能使用類別型別(class type)來宣告出變數裝進去,可是字串本身卻又是一種常量(literal)。接下來運用==的運算來說明一些觀念:
int num01 = 1;
int num02 = 2;
System.out.println(num01 == num02);
以上程式肯定是印出true,沒啥問題。我們接著看:
String str01 = "ithome";
String str02 = "ithome";
System.out.println(str01 == str02);
以上這段呢?應該也會是true沒錯吧?是的,確實是true。我們再繼續來看:
String str01 = "ithome"
String strs = "ithome jworld";
System.out.println(str01 == strs.split(" ")[0]);
這個勒? strs.split(" ")[0]的結果也是"ithome",那答案應該也會是true吧?很遺憾,console會印出false給你看。
(這邊很適合上個成龍問號或黑人問號)
要解釋這個結果,需要先講兩個概念。第一個是stack memory & heap memory的概念。
(字串部分的heap可能和實際有差別,這邊先以這樣的概念來解釋)
左邊stack部分的左半部可以看到我們取的變數名稱,右半部就是代表變數所裝進去的值。可以看到primitive type就是在stack中直接儲存值本身;但如果是class type像String型別的變數,儲存的是一串位址值,而位址值的功用就是可以去對照出相對應的heap memory物件,再從這個物件中取用我們需要的值。
而"=="的功能,是去比較stack中存進去的值,所以str01 == str02會是true,不是因為他們都是"ithome",而是因為他們儲存的位址值一樣;所以如果到了下面的例子,雖然我們知道位址值對應的字串常量是一樣的,但是我們比較的是位址值,所以為false。
不過我們常常講,Java中的字串是一個特別的存在,這就要帶出第二個要說的概念,字串池(string pool)。
所以為什麼上面例子中的str01和str02是指向同一個String位址值呢?就是因為Java字串池的機制,Java會自動判斷我們所輸入的字串常量有沒有在字串池中已經存在了,如果已存在就會直接取用相同的字串位址值給我們要指派的字串變數中;而strs.split(" ")[0]因為是陣列的關係,所以我們比較的其實是String[]第0索引的位址值,而這個位址值中存放的是"ithome"字串在字串池中的位址值。
雖說原本主題是字串,不過講到後來比較重要的概念是stack memory和heap memory,基本型別之所以和一般類別不一樣就是因為基本型別沒有heap memory,我們所輸入的值本身就是值;一般類別的變數儲存的都會位址值,再透過這個位址值對應出實際物件儲存的heap記憶體空間。