String常量池詳解

  String使用private final char value[]來實現字符串的存儲,也就是說String對象建立以後,就不能再修改此對象中存儲的字符串內容,就是由於如此,才說String類型是不 可變的(immutable)。String類有一個特殊的建立方法,就是使用""雙引號來建立.例如new String("i am")實際建立了2個
  String對象,一個是"i am"經過""雙引號建立的,另外一個是經過new建立的.只不過他們建立的時期不一樣,
  一個是編譯期,一個是運行期!java對String類型重載了+操做符,能夠直接使用+對兩個字符串進行鏈接。運行期調用String類的intern()方法能夠向String Pool中動態添加對象。

  例1
  String s1 = "sss111";
  //此語句同上
  String s2 = "sss111";
  System.out.println(s1 == s2); //結果爲true
  例2
  String s1 = new String("sss111");
  String s2 = "sss111";
  System.out.println(s1 == s2); //結果爲false
  例3
  String s1 = new String("sss111");
  s1 = s1.intern();
  String s2 = "sss111";
  System.out.println(s1 == s2);//結果爲true
  例4
  String s1 = new String("111");
  String s2 = "sss111";
  String s3 = "sss" + "111";
  String s4 = "sss" + s1;
  System.out.println(s2 == s3); //true
  System.out.println(s2 == s4); //false
  System.out.println(s2 == s4.intern()); //true


結果上面分析,總結以下:

     1.單獨使用""引號建立的字符串都是常量,編譯期就已經肯定存儲到String Pool中;

  2,使用new String("")建立的對象會存儲到heap中,是運行期新建立的;

  3,使用只包含常量的字符串鏈接符如"aa" + "aa"建立的也是常量,編譯期就能肯定,已經肯定存儲到String Pool中;

  4,使用包含變量的字符串鏈接符如"aa" + s1建立的對象是運行期才建立的,存儲在heap中;

  還有幾個常常考的面試題:

  String s1 = new String("s1") ;
  String s2 = new String("s1") ;
  上面建立了幾個String對象?
  答案:3個 ,編譯期Constant Pool中建立1個,運行期heap中建立2個.(用new建立的每new一次就在堆上建立一個對象,用引號建立的若是在常量池中已有就直接指向,不用建立

  String s1 = "s1";
  String s2 = s1;
  s2 = "s2";
  s1指向的對象中的字符串是什麼?
  答案: "s1"。(永遠不要忘了String不可變的,s2 = "s2";實際上s2的指向就變了,由於你不能夠去改變一個String,)

--------------------------------------------------------------------------------------------------------------------------------------------------

String是一個特殊的包裝類數據。能夠用:
String str = new String("abc");
String str = "abc";
兩種的形式來建立,第一種是用new()來新建對象的,它會在存放於堆中。每調用一次就會建立一個新的對象
而第二種是先在棧中建立一個對String類的對象引用變量str,而後經過符號引用去字符串常量池裏找有沒有"abc",若是沒有,則將"abc"存放進字符串常量池,並令str指向」abc」,若是已經有」abc」 則直接令str指向「abc」。

比較類裏面的數值是否相等時,用equals()方法;當測試兩個包裝類的引用是否指向同一個對象時,用==,下面用例子說明上面的理論。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
能夠看出str1和str2是指向同一個對象的。

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不一樣的對象。每一次生成一個。

因 此用第二種方式建立多個」abc」字符串,在內存中其實只存在一個對象而已. 這種寫法有利與節省內存空間. 同時它能夠在必定程度上提升程序的運行速度,由於JVM會自動根據棧中數據的實際狀況來決定是否有必要建立新對象。而對於String str = new String("abc");的代碼,則一律在堆中建立新對象,而無論其字符串值是否相等,是否有必要建立新對象,從而加劇了程序的負擔。

另 一方面, 要注意: 咱們在使用諸如String str = "abc";的格式定義類時,老是想固然地認爲,建立了String類的對象str。擔憂陷阱!對象可能並無被建立!而可能只是指向一個先前已經建立的 對象。只有經過new()方法才能保證每次都建立一個新的對象。
因爲String類的immutable性質,當String變量須要常常變換其值時,應該考慮使用StringBuffer類,以提升程序效率。
1. 首先String不屬於8種基本數據類型,String是一個對象。
由於對象的默認值是null,因此String的默認值也是null;但它又是一種特殊的對象,有其它對象沒有的一些特性。

2. new String()和new String(」")都是申明一個新的空字符串,是空串不是null;

3. String str=」kvill」;String str=new String (」kvill」)的區別

看例1:

String s0="kvill";
String s1="kvill";
String s2="kv" + "ill";
System.out.println( s0==s1 );
System.out.println( s0==s2 );
結果爲:
true
true

首先,咱們要知結果爲道Java會確保一個字符串常量只有一個拷貝。
因 爲例子中的s0和s1中的」kvill」都是字符串常量,它們在編譯期就被肯定了,因此s0==s1爲true;而」kv」和」ill」也都是字符串常 量,當一個字符串由多個字符串常量鏈接而成時,它本身確定也是字符串常量,因此s2也一樣在編譯期就被解析爲一個字符串常量,因此s2也是常量池中」 kvill」的一個引用。因此咱們得出s0==s1==s2;用new String() 建立的字符串不是常量,不能在編譯期就肯定,因此new String() 建立的字符串不放入常量池中,它們有本身的地址空間。

看例2:
String s0="kvill";
String s1=new String("kvill");
String s2="kv" + new String("ill");
System.out.println( s0==s1 );
System.out.println( s0==s2 );
System.out.println( s1==s2 );
結果爲:
false
false
false

例 2中s0仍是常量池中"kvill」的應用,s1由於沒法在編譯期肯定,因此是運行時建立的新對象」kvill」的引用,s2由於有後半部分 new String(」ill」)因此也沒法在編譯期肯定,因此也是一個新建立對象」kvill」的應用;明白了這些也就知道爲什麼得出此結果了。

4. String.intern():
再補充介紹一點:存在於.class文件中的常量池,在運行期被JVM裝載,而且能夠擴充。String的intern()方法就是擴充常量池的 一個方法;當一個String實例str調用intern()方法時,Java查找常量池中是否有相同Unicode的字符串常量,若是有,則返回其的引用,若是沒有,則在常量池中增長一個Unicode等於str的字符串並返回它的引用;看例3就清楚了

例3:
String s0= "kvill";
String s1=new String("kvill");
String s2=new String("kvill");
System.out.println( s0==s1 );
System.out.println( "**********" );
s1.intern();
s2=s2.intern(); //把常量池中"kvill"的引用賦給s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 );
結果爲:
false
**********
false //雖然執行了s1.intern(),但它的返回值沒有賦給s1
true //說明s1.intern()返回的是常量池中"kvill"的引用
true

最 後我再破除一個錯誤的理解:有人說,「使用 String.intern() 方法則能夠將一個 String 類的保存到一個全局 String 表中 ,若是具備相同值的 Unicode 字符串已經在這個表中,那麼該方法返回表中已有字符串的地址,若是在表中沒有相同值的字符串,則將本身的地址註冊到表中」若是我把他說的這個全局的 String 表理解爲常量池的話,他的最後一句話,」若是在表中沒有相同值的字符串,則將本身的地址註冊到表中」是錯的:

看例4:
String s1=new String("kvill");
String s2=s1.intern();
System.out.println( s1==s1.intern() );
System.out.println( s1+" "+s2 );
System.out.println( s2==s1.intern() );
結果:
false
kvill kvill
true

在這個類中咱們沒有聲名一個」kvill」常量,因此常量池中一開始是沒有」kvill」的,當咱們調用s1.intern()後就在常量池中新添加了一個」kvill」常量,原來的不在常量池中的」kvill」仍然存在,也就不是「將本身的地址註冊到常量池中」了。
s1==s1.intern()爲false說明原來的」kvill」仍然存在;s2如今爲常量池中」kvill」的地址,因此有s2==s1.intern()爲true。 java

相關文章
相關標籤/搜索