Java 常量池

最近在網上看到一些Android的面試題,關於String和Integer常量池的,就總結了一下,暫時先記錄下來,之後說不定能用到java

 1 public class JavaBase {  2   
 3     public static final String STRING20; // 常量 
 4     public static final String STRING21; // 常量 
 5     static {  6         STRING20 = "hello";  7         STRING21 = "World";  8  }  9   
 10     public static void main(String[] args) {  11   
 12         Integer mInteger1 = new Integer("3");  13         Integer mInteger2 = new Integer("3");  14         System.out.println(mInteger1 == mInteger2);// false 建立兩個對象  15   
 16         // 對象沒法與數值進行比較,因此對象會自動拆箱變成數值在進行比較 
 17         int mInteger3 = new Integer("3");  18         Integer mInteger4 = new Integer("3");  19         System.out.println(mInteger3 == mInteger4);// true  20   
 21         // 首先mInteger6 == (mInteger7+mInteger5),由於+這個操做符不適用於Integer對象,mInteger7  22         // 和mInteger5進行自動拆箱操做,進行數值相加,即mInteger6 ==3。而後Integer對象沒法與數值進行直接比較,  23         // 因此mInteger6自動拆箱轉爲int值3,最終轉爲3 ==3進行數值比較 
 24         Integer mInteger5 = new Integer(0);  25         Integer mInteger6 = new Integer(3);  26         Integer mInteger7 = new Integer(3);  27         System.out.println(mInteger6 == (mInteger7 + mInteger5));// true 在棧中計算 
 28   
 29         Integer mInteger8 = new Integer(3);  30         Integer mInteger9 = 3;  31         System.out.println(mInteger8 == mInteger9);// false 一個在棧中一個在堆中 
 32   
 33         Integer mInteger10 = 3;  34         Integer mInteger11 = 3;  35         System.out.println(mInteger10 == mInteger11);// true 實現了常量池  36   
 37         // 除Float和Double之外, 其它六種都實現了常量池,可是它們只在大於等於-128而且小於等於127時才使用常量池。 
 38         Double mDouble0 = 3d;  39         Double mDouble1 = 3d;  40         System.out.println(mDouble0 == mDouble1);// false 沒有實現常量池,至關於分別new一個 
 41   
 42         Integer mInteger12 = 400;  43         Integer mInteger13 = 400;  44         System.out.println(mInteger12 == mInteger13);// false大於127則在堆中建立,至關於new一個  45   
 46         // Boolean類也實現了常量池技術 
 47         Boolean bool1 = true;  48         Boolean bool2 = true;  49         System.out.println(bool1 == bool2);// 輸出true 
 50   
 51         Boolean bool3 = true;  52         Boolean bool4 = new Boolean(true);  53         System.out.println(bool3 == bool4);// 輸出false 一個在常量池中一個在堆中  54   
 55         // JVM對於字符串常量的"+"鏈接優化爲鏈接後的值,"hello" + "World"經編譯器優化後就已是helloWorld,在編譯期  56         // 字符串常量的值就肯定下來。而對於字符串引用,因爲在字符串的"+"鏈接中,有字符串引用存在,而引用的值在程序編譯期是沒法  57         // 肯定的,因此string0 +"World"沒法被編譯器優化,只有在程序運行期來動態分配並將鏈接後的新地址賦給string1。 
 58         /**  59  * String string2 = "hello" + "World"會查找常量池中時候存在內容爲"helloWorld"字符串對象,如存在則  60  * 直接讓string2引用該對象,  61          */  
 62         String string0 = "hello";  63         String string1 = string0 + "World";  64         String string2 = "hello" + "World";  65         System.out.println(string1 == "helloWorld"); // false 
 66         System.out.println(string2 == "helloWorld"); // true 
 67         System.out.println(string1 == string2); // false 
 68   
 69         /**  70  * String str = "hello"建立對象的過程  71  *1 首先在常量池中查找是否存在內容爲"hello"字符串對象  72  *2 若是不存在則在常量池中建立"hello",並讓str引用該對象  73  *3 若是存在則直接讓str引用該對象  74  *  75  *String str = new String("hello")建立實例的過程  76  *1 首先在堆中(不是常量池)建立一個指定的對象"hello",並讓str引用指向該對象  77  *2 在字符串常量池中查看,是否存在內容爲"hello"字符串對象  78  *3 若存在,則將new出來的字符串對象與字符串常量池中的對象聯繫起來  79  *4 若不存在,則在字符串常量池中建立一個內容爲"hello"的字符串對象,並將堆中的對象與之聯繫起來  80  *intern 方法能夠返回該字符串在常量池中的對象的引用,  81          */  
 82         // string3,string4分別位於堆中不一樣空間 
 83         String string3 = new String("hello");  84         String string4 = new String("hello");  85         System.out.println(string3 == string4);// 輸出false  86   
 87         // string5,string6位於池中同一空間,常量池 
 88         String string5 = "hello";  89         String string6 = "hello";  90         System.out.println(string5 == string6);// 輸出true  91   
 92         // intern首先檢查字符串常量池中是否有該對象的引用,若是存在,則將這個引用返回給變量,不然將引用加入並返回給變量。 
 93         String string7 = new String("hello");  94         String string8 = string7.intern();  95         String string9 = "hello";  96         System.out.println(string8 == string9);// true 
 97   
 98         String string10 = "hello";  99         String string11 = new String("hello"); 100         System.out.println(string10 == string11);// 輸出false 一個在常量池中一個在堆中 
101   
102         /** 103  * 對於final修飾的變量,它在編譯時被解析爲常量值的一個本地拷貝存儲到本身的常量池中或嵌入到它的字節碼流中。 104  * 因此此時的string12 + string13和"hello" + "World"效果是同樣的。 105          */  
106         final String STRING12 = "hello"; 107         final String STRING13 = "World"; 108         String string14 = STRING12 + STRING13; // 將兩個常量用+鏈接進行初始化 
109         String string15 = "helloWorld"; 110         System.out.println(string14 == string15);// ture 
111   
112         String string16 = "hello"; 113         String string17 = "World"; 114         String string18 = string16 + string17; 115         String string19 = "helloWorld"; 116         System.out.println(string18 == string19);// false 
117         System.out.println(string18.intern() == string19);// true 
118   
119         /** 120  * STRING20和STRING21雖然被定義爲常量,可是它們都沒有立刻被賦值。在運算出string22的值以前,他們什麼時候被賦值,以及被賦予什麼樣的值, 121  * 都是個變數。所以STRING20和STRING21在被賦值以前,性質相似於一個變量。那麼string22就不能在編譯期被肯定,而只能在運行時被建立了。 122          */  
123   
124         String string22 = STRING20 + STRING21; 125         String string23 = "helloWorld"; 126         System.out.println(string22 == string23);// false 
127   
128         /** 129  * string25 == string24固然不相等,string24雖然也是拼接出來的,但new String("lo")這部分不是已知字面量, 130  * 是一個不可預料的部分,編譯器不會優化,必須等到運行時才能夠肯定結果,結合字符串不變定理,因此地址確定不一樣。 131          */  
132         String string24 = "Hel" + new String("lo"); 133         String string25 = "Hello"; 134         System.out.println(string25 == string24);// false 
135   
136         String string26 = "Hello"; 137         System.out.println(string26 == "Hello");// true 
138  } 139 }

在上面咱們看到Integer在-128~127之間是使用常量池的,若是不在這個區間就不會使用,實際上是從新new了一個Integer,咱們看一下源碼面試

public static Integer valueOf(int i) { assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } 

咱們看到若是i >= IntegerCache.low && i <= IntegerCache.high就會調用IntegerCache的cache方法,而不會從新new一個integer,繼續,咱們找到IntegerCache這個類優化

 1 private static class IntegerCache {  2     static final int low = -128;  3     static final int high;  4     static final Integer cache[];  5   
 6     static {  7         // high value may be configured by property 
 8         int h = 127;  9         String integerCacheHighPropValue =  
10             sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); 11         if (integerCacheHighPropValue != null) { 12             int i = parseInt(integerCacheHighPropValue); 13             i = Math.max(i, 127); 14             // Maximum array size is Integer.MAX_VALUE 
15             h = Math.min(i, Integer.MAX_VALUE - (-low) -1); 16  } 17         high = h; 18   
19         cache = new Integer[(high - low) + 1]; 20         int j = low; 21         for(int k = 0; k < cache.length; k++) 22             cache[k] = new Integer(j++); 23  } 24   
25     private IntegerCache() {} 26 }

有一個static的代碼塊,裏面初始化了一些Integer,若是範圍在-128~127之間就會從這裏面取,若是不在這個範圍內就會new一個Integer。spa

final類型若是不賦值是要報錯的,若是這樣賦值沒有報錯3d

static { asd="asd"; } public static final String asd ; { qwe="qwe"; } public final String qwe;

再看一下下面的狀況調試

{ qwe=2; } public int qwe;

若是打應qwe的值是爲2,由於斷點調試的時候public int 這行沒有執行。再看一種狀況code

{ qwe=2; } public int qwe=1;

若是打印qwe的值爲1,由於斷點調試的時候public int這行執行了。同理若是兩個都加上static都同樣對象

static { qwe=2; } public static int qwe;

這個結果也是爲2,由於斷點的時候public那行沒有執行,blog

static { qwe=2; } public static int qwe=1;

這種狀況就爲1了,由於是按照順序執行的。若是一個是static一個不是,又會是上面結果字符串

 { qwe=2; } public static int qwe=1;

這種狀況下結果爲2,由於static先執行

 { qwe=2; } public static int qwe;

同理這種狀況下也爲2,儘管調試的時候public那行沒有執行,由於是static先執行的。

 { qwe=2; } public static final int qwe;

若是上面這樣寫會報錯的,提示qwe沒有初始化

static { qwe=2; } public static final int qwe;

同理上面這個結果也爲2,

static { qwe=2; } public static final int qwe=1;

那麼這種就要報錯了。變量若是沒有賦初值,在調試的時候就不會執行。

相關文章
相關標籤/搜索