天天看點

JVM中的常量池解析

在jvm規範中,每個類型都有自己的常量池。常量池是某類型所用常量的一個有序集合,包括直接常量(基本類型,String)和對其他類型、字段、方法的符号引用。之是以是符号引用而不是像c語言那樣,編譯時直接指定其他類型,是因為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
  }
}      

4=》字元串比較更豐富的一個例子

package testPackage;
public class Test {
 public static void main(String[] args) {
  String hello = "Hello", lo = "lo";
  System.out.print((hello == "Hello") + " ");
  System.out.print((Other.hello == hello) + " ");
  System.out.print((other.Other.hello == hello) + " ");
  System.out.print((hello == ("Hel"+"lo")) + " ");
  System.out.print((hello == ("Hel"+lo)) + " ");
  System.out.println(hello == ("Hel"+lo).intern());
 }
}
class Other { static String hello = "Hello"; }


package other;
public class Other { static String hello = "Hello"; }

SystemOut:true true true true false true      

輸出結果的分别解釋如下:

在同包同類下,引用自同一String對象.

在同包不同類下,引用自同一String對象.

在不同包不同類下,依然引用自同一String對象

在編譯成.class時能夠識别為同一字元串的,自動優化成常量,是以也引用自同一String對象

在運作時建立的字元串具有獨立的記憶體位址,是以不引用自同一String對象

String的intern()方法會查找在常量池中是否存在一份equal相等的字元串,如果有則傳回一個引用,沒有則添加自己的字元串進進入常量池,

注意,隻是字元串部分,