java常量池概念

原文:http://qurtyy.blog.163.com/blog/static/574436812012212148291/

在class文件中,“常量池”是最複雜也最值得關注的內容。

  Java是一種動態連接的語言,常量池的作用非常重要,常量池中除了包含代碼中所定義的各種基本類型(如int、long等等)和對象型(如String及數組)的常量值還,還包含一些以文本形式出現的符號引用,比如:

  類和接口的全限定名;

  字段的名稱和描述符;

  方法和名稱和描述符。

  在C語言中,如果一個程序要調用其它庫中的函數,在連接時,該函數在庫中的位置(即相對於庫文件開頭的偏移量)會被寫在程序中,在運行時,直接去這個地址調用函數;

  而在Java語言中不是這樣,一切都是動態的。編譯時,如果發現對其它類方法的調用或者對其它類字段的引用的話,記錄進class文件中的,只能是一個文本形式的符號引用,在連接過程中,虛擬機根據這個文本信息去查找對應的方法或字段。

  所以,與Java語言中的所謂“常量”不同,class文件中的“常量”內容很非富,這些常量集中在class中的一個區域存放,一個緊接着一個,這裏就稱爲“常量池”。

java中的常量池技術,是爲了方便快捷地創建某些對象而出現的,當需要一個對象時,就可以從池中取一個出來(如果池中沒有則創建一個),則在需要重複重複創建相等變量時節省了很多時間。常量池其實也就是一個內存空間,不同於使用new關鍵字創建的對象所在的堆空間。本文只從java使用者的角度來探討java常量池技術,並不涉及常量池的原理及實現方法。個人認爲,如果是真的專注java,就必須對這些細節方面有一定的瞭解。但知道它的原理和具體的實現方法則不是必須的。

1,常量池中對象和堆中的對象

public class Test{

Integer i1=new Integer(1);
   Integer i2=new Integer(1);
//i1,i2分別位於堆中不同的內存空間

   System.out.println(i1==i2);//輸出false


   Integer i3=1;
   Integer i4=1;
//i3,i4指向常量池中同一個內存空間

   System.out.println(i3==i4);//輸出true

//很顯然,i1,i3位於不同的內存空間

System.out.println(i1==i3);//輸出false

}

2,8種基本類型的包裝類和對象池

java中基本類型的包裝類的大部分都實現了常量池技術,這些類是Byte,Short,Integer,Long,Character,Boolean,另外兩種浮點數類型的包裝類則沒有實現。另外Byte,Short,Integer,Long,Character這5種整型的包裝類也只是在對應值小於等於127時纔可使用對象池,也即對象不負責創建和管理大於127的這些類的對象。以下是一些對應的測試代碼:

public class Test{

public static void main(String[] args){

   //5種整形的包裝類Byte,Short,Integer,Long,Character的對象,

   //在值小於127時可以使用常量池

   Integer i1=127;

   Integer i2=127;

   System.out.println(i1==i2)//輸出true

   //值大於127時,不會從常量池中取對象

   Integer i3=128;

   Integer i4=128;

   System.out.println(i3==i4)//輸出false

   //Boolean類也實現了常量池技術

   Boolean bool1=true;

   Boolean bool2=true;

   System.out.println(bool1==bool2);//輸出true

   //浮點類型的包裝類沒有實現常量池技術

   Double d1=1.0;

   Double d2=1.0;

   System.out.println(d1==d2)//輸出false

 

}

}

3,String也實現了常量池技術

String類也是java中用得多的類,同樣爲了創建String對象的方便,也實現了常量池的技術,測試代碼如下:

public class Test{

public static void main(String[] args){

//s1,s2分別位於堆中不同空間

String s1=new String("hello");

String s2=new String("hello");

System.out.println(s1==s2)//輸出false

//s3,s4位於池中同一空間

String s3="hello";

String s4="hello";

System.out.println(s3==s4);//輸出true

}

}

最後:

細節決定成敗,寫代碼更是如此。

Integer的封裝:

   public static Integer valueOf(int i) {

         final int offset = 128;

         if (i >= -128 && i <= 127) { // must cache

            return IntegerCache.cache[i + offset];

         }

          return new Integer(i);

       }
當你直接給一個Integer對象一個int值的時候,其實它調用了valueOf方法,然後你賦的這個值很特別,是128,那麼沒有進行cache方法,相當於new了兩個新對象。所以問題中定義a、b的兩句代碼就類似於:

      Integer a = new Integer(128);

      Integer b = new Integer(128);
這個時候再問你,輸出結果是什麼?你就知道是false了。如果把這個數換成127,再執行:

      Integer a = 127;

      Integer b = 127;

      System.out.println(a == b);
結果就是:true

由上可知,我們進行對象比較時最好還是使用equals,便於按照自己的目的進行控制。

--------------------------------------------------補充-----------------------------------------------------------------------

   我們看一下IntegerCache這個類裏面的內容:

   private static class IntegerCache {

      private IntegerCache() {

      }

      static final Integer cache[] = new Integer[-(-128) + 127 + 1];

      static {

         for (int i = 0; i < cache.length; i++)

            cache[i] = new Integer(i - 128);

      }

   }

由於cache[]在IntegerCache類中是靜態數組,也就是只需要初始化一次,即static{......}部分,所以,如果Integer對象初始化時是-128~127的範圍,就不需要再重新定義申請空間,都是同一個對象---在IntegerCache.cache中,這樣可以在一定程度上提高效率。

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