一、String緩沖池
?
? ? ? ?首先我們要明確,String并不是基本數據類型,而是一個對象,并且是不可變的對象。查看源碼就會發現String類為final型的(當然也不可被繼承),而且通過查看JDK文檔會發現幾乎每一個修改String對象的操作,實際上都是創建了一個全新的String對象。
? ? ? ?字符串為對象,那么在初始化之前,它的值為null,到這里就有必要提下””、null、new String()三者的區別。null 表示string還沒有new ,也就是說對象的引用還沒有創建,也沒有分配內存空間給他,而””、new String()則說明了已經new了,只不過內部為空,但是它創建了對象的引用,是需要分配內存空間的。
? ? ? ?java的虛擬機在內存中開辟出一塊單獨的區域,用來存儲字符串對象,這塊內存區域被稱為字符串緩沖池。那個java的字符串緩沖池是如何工作的呢?
String a = "abc";
String b = "abc";
String c = new String("xyz");
例如上邊的代碼:?
String a = "abc";
? ? ? ?創建字符串的時候先查找字符串緩沖池中有沒有相同的對象,如果有相同的對象就直接返回該對象的引用,如果沒有相同的對象就在字符串緩沖池中創建該對象,然后將該對象的應用返回。對于這一步而言,緩沖池中沒有abc這個字符串對象,所以首先創建一個字符串對象,然后將對象引用返回給a。
String b = "abc";
? ? ? ?這一句也是想要創建一個對象引用變量b使其指向abc這一對象。這時,首先查找字符串緩沖池,發現abc這個對象已經有了,這是就直接將這個對象的引用返回給b,此時a和b就共用了一個對象abc,不過不用擔心,a改變了字符串而影響了b,因為字符串都是常量,一旦創建就沒辦法修改了,除非創建一個新的對象。
String c = new String("xyz");
? ? ? ?查找字符串緩沖池發現沒有xyz這個字符串對象,于是就在字符串緩沖池中創建了一個xyz對象然后再將引用返回。
? ? ? ?從上邊的分析可以看出,當new一個字符串時并不一定是創建了一個新的對象,有可能是與別的引用變量共同使用了同一個對象。下面看幾個常見的有關字符串緩沖池的問題。
?
二、創建了幾個對象
?
String a = "abc";
String b = "abc";
String c = new String("xyz");
String d = new String("xyz");
String e = "ab" + "cd";
? ? ? ?這個程序與上邊的程序比較相似,我們分比來看一下:
? ? ? ?1、String a = “abc”;這一句由于緩沖池中沒有abc這個字符串對象,所以會創建一個對象;
? ? ? ?2、String b = “abc”;由于緩沖池中已經有了abc這個對象,所以不會再創建新的對象;
? ? ? ?3、String c = new String(“xyz”);由于沒有xyz這個字符串對象,所以會首先創建一個xyz的對象,然后這個字符串對象由作為String的構造方法,在內存中(不是緩沖池中)又創建了一個新的字符串對象,所以一共創建了兩個對象;
? ? ? ?4、String d = new String(“xyz”);緩沖池中已有該字符串對象,則緩沖池中不再創建該對象,然后會在內存中創建一個新的字符串對象,所以只創建了一個對象;
? ? ? ?5、String e = ”ab” + ”cd”;由于常量的值在編譯的時候就被確定了。所以這一句等價于String e = ”abcd”;所以創建了一個對象;
? ? ? ?所以創建的對象的個數分別是:1,0,2,1,1。
?
三、到底相等不相等
?
? ? ? ?我們在學習java時就知道兩個字符串對象相等的判斷要用equal而不能使用==,但是學習了字符串緩沖池以后,應該知道為什么不能用==,什么情況下==和equal是等價的,首先,必須知道的是,equal比較的是兩個字符串的值是否相等,而==比較的是兩個對象的內存地址是否相等,下面我們就通過幾個程序來看一下。
?
實例一:
public static void main(String[] args) { String s1 = "Monday"; String s2 = "Monday"; if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2");
}
輸出結果:
s1 == s2
? ? ? ?分析:通過上邊的介紹字符串緩沖池,我們知道s1和s2都是指向字符串緩沖池中的同一個對象,所以內存地址是一樣的,所以用==可以判斷兩個字符串是否相等。
?
實例二:
public static void main(String[] args) { String s1 = "Monday"; String s2 = new String("Monday"); if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); if (s1.equals(s2)) System.out.println("s1 equals s2"); else System.out.println("s1 not equals s2");
}
輸出結果:
s1 != s2
s1 equals s2
? ? ? ?分析:由上邊的分析我們知道,String s2 = new String(“Monday”);這一句話沒有在字符串緩沖池中創建新的對象,但是會在內存的其他位置創建一個新的對象,所以s1是指向字符串緩沖池的,s2是指向內存的其他位置,兩者的內存地址不同的。
?
實例三:
public static void main(String[] args) { String s1 = "Monday"; String s2 = new String("Monday"); s2 = s2.intern(); if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); if (s1.equals(s2)) System.out.println("s1 equals s2"); else System.out.println("s1 not equals s2");
}
輸出結果:
s1 == s2
s1 equals s2
? ? ? ?分析:先來說說intern()這個方法的作用吧,這個方法的作用是返回在字符串緩沖池中的對象的引用,所以s2指向的也是字符串緩沖池中的地址,和s1是相等的。
?
實例四:
public static void main(String[] args) { String Monday = "Monday"; String Mon = "Mon"; String day = "day"; System.out.println(Monday == "Mon" + "day"); System.out.println(Monday == "Mon" + day); }
輸出結果:
true
false
? ? ? ?分析:第一個為什么等于true我們已經說過了,因為兩者都是常量所以在編譯階段就已經能確定了,在第二個中,day是一個變量,所以不能提前確定他的值,所以兩者不相等,從這個例子我們可以看出,只有+連接的兩邊都是字符串常量時,引用才會指向字符串緩沖池,都則都是指向內存中的其他地址。
?
實例五:
public static void main(String[] args) { String Monday = "Monday"; String Mon = "Mon"; final String day = "day"; System.out.println(Monday == "Mon" + "day"); System.out.println(Monday == "Mon" + day); }
輸出結果:
true
true
? ? ? ?分析:加上final后day也變成了常量,所以第二句的引用也是指向的字符串緩沖池。
?
?