Java中的String詳解

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() 去掉起始和結尾的空格

發佈了22 篇原創文章 · 獲贊 23 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章