String類代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作爲此類的實例實現。
首先來看一個關於String示例:
String s1 = "HelloWorld";
String s2 = "HelloWorld";
String s3 = new String("HelloWorld");
String s4 = "Hello";
String s5 = "World";
String s6 = "Hello" + "World";
String s7 = s3 + s4;
System.out.println(s1 == s2);//1:輸出爲true
System.out.println(s1 == s3);//2:輸出爲false
System.out.println(s1 == s6);//3:輸出爲true
System.out.println(s1 == s7);//4:輸出爲false
System.out.println(s1 == s7.intern());//5:輸出爲true
System.out.println(s3 == s3.intern());//6:輸出爲false
輸出結果:
看了輸出結果,你可能會有很多疑惑,沒關係,我們對輸出結果分別進行分析:
一、爲什麼輸出語句1輸出爲true,而輸出語句2卻輸出爲false?
在解析這個問題之前,我們先來了解一下JVM內存中的棧(stack)、堆(heap)和方法區:
► 棧中保存的是:基本數據類型的變量、對象的引用、函數調用的現場。
► 堆中保存的是:通過new關鍵字和構造器創建的對象。堆是垃圾收集器管理的主要區域。
► 它們之間的區別是:棧空間操作起來最快但是棧很小,通常大量的對象都是放在堆空間,棧和堆的大小都可以通過JVM的啓動參數來進行調整,棧空間用光了會引發StackOverflowError,而堆和常量池空間不足則會引發OutOfMemoryError。
► 舉例說明:String str = new String(“hello”)這個語句中變量str放在棧上,用new創建出來的字符串對象放在堆上,而”hello”這個字面量是放在方法區的。
JVM中內存分佈:
瞭解完內存後我們來解決這個問題,String實例的創建方式主要是以下兩種:
- 直接賦值:
String s1 = "HelloWorld"
- 通過new關鍵字來創建:
String s3 = new String("HelloWorld")
這兩種方式的內存示意圖:
這兩種創建方式的區別是它們產生實例的過程,以上面示例進行演示:1.直接賦值會先檢查字符串常量池中是否含有HelloWorld字符串,如果HelloWorld字符串已經存在,那麼就直接指向; 如果沒有,則將HelloWorld添加進常量池並且指向。
2.通過new關鍵字來創建,會向堆申請內存,來存儲HelloWord字符串對象,指向的是堆中的字符串對象。通過new操作符創建的字符串對象不指向字符串池中的任何對象,採用new 創建的字符串對象也不會進入字符串池。
輸出語句1比較的是s1和s2,在執行String s1="HelloWorld"
時會將“HelloWorld”添加進常量池,s2賦值時“HelloWorld”已經存在於常量池中,因此s1和s2指向常量池中同一個字符串,所以輸出爲true。而輸出語句比較的是s1和s3,s3是通過new關鍵字創建的字符串,通過new操作符創建的字符串對象不指向字符串池中的任何對象,因此輸出爲false。
二、爲什麼輸出語句3輸出爲true,而輸出語句4卻輸出爲false?
解析:字符串相加的時候,如果相加的都是靜態字符串,那麼跟直接賦值相同,會先檢查字符串常量池中字符串是否存在,如果經存在,就直接指向,如果不存在,就將字符串添加進常量池並且指向【String s6="Hello"+"World"
跟String s1="HelloWorld"
實質上是相同的,String s6="Hello"+"World"
在編譯階段就會被處理成String s6="HelloWorld"
】,所以輸出語句3輸出爲true;如果相加時包含了變量,如String s7=s3 + s4
中包含了s3,s4兩個變量,那麼就相當於通過new關鍵字來創建,因此輸出語句4輸出爲false。
三、String中intern方法詳解
當調用 intern 方法時,如果字符串常量池已經包含一個等於此 String 對象的字符串(用 equals(Object) 方法確定),則返回池中的字符串。否則,將此 String 對象添加到池中,並返回此 String 對象的引用。
這就可以解釋輸出語句5和6的輸出結果:
輸出語句5比較的是s1跟s7.intern(),s1指向的是字符串常量中的“HelloWorld”,s7是由s3+s4拼接起來,字面量爲“HelloWorld”,調用s7的intern()時,由於字符串常量池中已經包含了HelloWorld,因此返回的是已經存在的HelloWorld,跟s1所指向的是同一個對象,所以輸出語句5輸出的爲true;
輸出語句6中比較的是s3和s3.intern();s3是通過new關鍵字創建的對象,跟字符串常量池沒有絲毫關係,因此輸出爲false。
四、String中常用的方法(瞭解更多方法請參考API)
(1)length() 字符串的長度
例:char chars[]={‘a’,’b’.’c’};
String s=new String(chars);
int len=s.length();
(2)charAt() 截取一個字符
例:char ch;
ch=”abc”.charAt(1); 返回’b’
(3)indexOf()和lastIndexOf()
indexOf() 查找字符或者子串第一次出現的地方。
lastIndexOf() 查找字符或者子串是後一次出現的地方。
(4)substring()
此方法有兩個重載:String substring(int startIndex) 和String substring(int startIndex,int endIndex)
(5)替換:replace()
(6)trim() 去掉起始和結尾的空格