實例化方式
既然是一個類,我們知道,一般的類在實例化的時候都是使用傳統實例化方式,new一個對象,而String類中提供兩種實例化方式:
- 傳統方式
String str = new String("Hello Bit") ;
- 直接賦值
String str = "Hello Bit" ; // str是一個對象,那麼"Hello Bit" 就應該保存在堆內存中
字符串比較
我們在比較基本數據類型數值時,大多使用’= =’,那麼比較字符串是否也可以用’= ='進行比較呢?我們不妨測試一下,如下代碼:
String str1 = "Hello" ;
String str = new String("Hello") ;
System.out.println(str1==str); // false
結果表明:兩個字符串內容是相同的課時用‘==’比較出來的結果卻是false,這是什麼原因呢?我們可以來看一下它們在內存中的存放情況,如下圖:
我們發現,這兩個字符串並不在同一個內存塊中,那麼比較的結果也就不同了。因此,我們總結出‘= =’比較的情況:
== 本身是
進行數值比較
的,如果用於對象比較,那麼所比較的就應該是兩個對象所保存的內存地址數值比較
,而並沒有比較對象的內容。
那麼我們要向比較兩個字符串的內容而不是數值呢?應該如何比較呢,這時候String類就提供了一個equals方法,我們可以使用equals比較兩個字符串的內容,如下:
String str1 = "Hello" ;
String str = new String("Hello") ;
System.out.println(str1.equals(str));//true
通過以上比較,我們可以得出以下結論:
在String類中==和equals的區別如下:
- ”==”:進行的數值比較,比較的是兩個字符串對象的內存地址數值。
- “equals()”:可以進行字符串內容的比較
String類的匿名對象
在java中,本身是沒有提供字符串常量的概念的,所有用雙引號引起來的字符串的內容本質上來講其實都是String類的匿名對象。
String str1 = "Hello" ;
String str = new String("Hello") ;
System.out.println(str1.equals(str));//true
System.out.println("Hello".equals(str));//true
從結果中可以看出:”String str = ”hello““,本質上就是將一個匿名的String類對象設置有名字,而且匿名對象一定保存在堆內存中。
在這裏需要注意的是在日後的開發過程之中,如果要判斷用戶輸入的字符串是否等同於特定字符串,一定要將特定字符串寫在前面。
我們觀察以下兩個結果:
String str = new String("Hello") ;
System.out.println("Hello".equals(str));//true
System.out.println(str.equals("hello"));//false
爲什麼一定要將特定字符串寫在前面呢?這是因爲,如果將變量寫在前面,倘若變量爲null的話,就會造成null引用equals方法導致空指針異常,所以爲了安全起見,在比較的時候一定要將特定字符串寫在前面。
實例化區別
那麼這兩種實例化方式有什麼區別呢?
我們看一段代碼:
String str1 = "hello" ;
String str2 = "hello" ;
String str3 = "hello" ;
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str2 == str3); // true
看到結果你是否吃了一驚呢?怎麼又是true了?其實仔細觀察,你就會發現這個和上面的代碼的區別,這三個比較都是直接賦值的,而之前的false是傳統的賦值。那麼這兩種賦值方式有什麼區別呢?我們看一看在內存中情況:
我們發現,這三個字符串在內存佔同一塊內存。
在JVM底層實際上會自動維護一個對象池(字符串對象池),如果現在採用了直接賦值的模式進行String類的對象
實例化操作,那麼該實例化對象(字符串內容)將自動保存到這個對象池之中。如果下次繼續使用直接賦值的模式
聲明String類對象,此時對象池之中如若有指定內容,將直接進行引用;如若沒有,則開闢新的字符串對象而後將
其保存在對象池之中以供下次使用
所謂的對象池就是一個對象數組(目的就是減少開銷)
而傳統的實例化方式,在內存中總是要開闢新的空間,例如:
我們觀察到,先是匿名對象“hello”在對象池中,然後新開闢的空間,並賦值,原來的匿名對象那塊內存會有垃圾空間GC回收,至於什麼時候回收,怎樣回收,我們都是不知道的。
// 該字符串常量並沒有保存在對象池之中
String str1 = new String("hello") ;
String str2 = "hello" ;
System.out.println(str1 == str2); // false
那麼在String類中提供有方法入池操作 public String intern(),例如:
String str1 = new String("hello").intern() ;
String str2 = "hello" ;
System.out.println(str1 == str2); // true
面試題:請解釋String類中兩種對象實例化的區別:
- 直接賦值:只會開闢一塊堆內存空間,並且該字符串對象可以自動保存在對象池中以供下次使用。
- 構造方法:會開闢兩塊堆內存空間,其中一塊成爲垃圾空間,不會自動保存在對象池中,可以使用intern()方法手工入池。
字符串不可變更
在定義字符串常量時,它的內容是不可改變的:
String str = "hello" ;
str = str + " world" ;
str += "!!!" ;
System.out.println(str); // hello world!!!
這裏的變更不是字符串常量的變更,而是字符串對象的變更。
可以發現字符串上沒有發生任何變化,但是字符串對象的引用一直在改變,而且會形成大量的垃圾空間。
通過以上所有,我們有以下三個原則:
- 字符串使用就採用直接賦值。
- 字符串比較就使用equals()實現。
- 字符串別改變太多。
字符與字符串
在String類中提供了很多方法可以實現字符與字符串之間的相互轉化,如下:
字節與字符串
字節常用於數據傳輸以及編碼轉換的處理之中,在String中提供有對字節的支持,如下:
除此之外String中海油很多常用的而方法,這裏不一一列舉。可以查看相關API文檔。