學(xué)習(xí)和使用java已經(jīng)有4年多了,反省一下,太多關(guān)注了實(shí)際的應(yīng)用層面,對某些基礎(chǔ)的東西沒有特別理解透徹。好記性不如爛筆頭,記錄下一些基礎(chǔ)的知識點(diǎn),把基礎(chǔ)夯實(shí),才能更進(jìn)一步。
先引用一下java常量池的說明:
常量池在java用于保存在編譯期已確定的,已編譯的class文件中的一份數(shù)據(jù)。它包括了關(guān)于類,方法,接口等中的常量,也包括字符串常量,如String s = “java”這種申明方式;當(dāng)然也可擴(kuò)充,執(zhí)行器產(chǎn)生的常量也會放入常量池,故認(rèn)為常量池是JVM的一塊特殊的內(nèi)存空間。
java是一種動態(tài)鏈接的語言,常量池的作用非常重要,常量池中除了包含代碼中所定義的各種基本類型(如int、long等等)和對象型(如String及數(shù)組)的常量值外,還包含一些以文本形式出現(xiàn)的符號引用,比如:
類和接口的全限定名;字段的名稱和描述符;方法的名稱和描述符。
對于程序員來說,這樣的說明略顯枯燥和抽象,我們還是用代碼來說明一切:
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
System.out.println(i1 == i2); // false
Integer i3 = 1;
Integer i4 = 1;
System.out.println(i3 == i4); // true
對于上面的i1和i2指向的對象,都是用new關(guān)鍵字來創(chuàng)建的,那么2個(gè)實(shí)際的對象都創(chuàng)建在堆內(nèi)存上,就是說,i1和i2分別指向了堆內(nèi)存上的2個(gè)地址,那么用==來比較的時(shí)候,肯定返回false了。對于i3和i4,是直接指定了值,這時(shí)候,java虛擬機(jī)會先到常量池里面去找,如果找到了,就返回對應(yīng)的引用;如果沒找到,那么就重新在堆內(nèi)存上創(chuàng)建,類似于new。
那么,對于上面的Integer,多大的范圍內(nèi)數(shù)會在常量池中存在呢?我們打開Integer類的源碼,發(fā)現(xiàn)它其實(shí)是使用了一個(gè)內(nèi)部類IntegerCache來實(shí)現(xiàn)這個(gè)功能的:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
不難看出來,當(dāng)數(shù)值在-128—127的時(shí)候,會從常量池中取得。當(dāng)然,我們自己寫代碼驗(yàn)證一下,會更好:
Integer i5 = 127;
Integer i6 = 127;
System.out.println(i5 == i6); // true
Integer i7 = 128;
Integer i8 = 128;
System.out.println(i7 == i8); // false
類似的,其他幾種基本數(shù)據(jù)類型和對應(yīng)的封裝類類都有使用常量池,不過,float和double沒有,其實(shí)也比較容易理解,如果這2中在常量池中要有,那要放哪些值呢?如果是Integer,那么-128—127明顯比其他的值更加常用,把這些放在常量池中比較合理,但是對于浮點(diǎn)數(shù),就沒有明顯證據(jù)表明哪幾個(gè)更加常用了。
其實(shí)除了基本數(shù)據(jù)類型之外,還有一種對象在java中很常用,也會用到常量池,就是String。提到String類和常量池,必須提到String的一個(gè)native方法:intern()。intern方法會返回String對象的在常量池中的引用,如果在常量池中已經(jīng)存在這個(gè)字符串了,那么直接返回這個(gè)引用;如果沒有,就先把這個(gè)字符串復(fù)制到常量池,然后返回引用。依然通過代碼來說明:
String s1 = new String("java");
String s2 = "java";
System.out.println(s1 == s2); // false
System.out.println(s1.intern() == s2); // true
備注:
1、以上的源碼都來自JDK1.7,不同版本的JDK可能會有不同。
2、運(yùn)行環(huán)境為java自帶的hotspot虛擬機(jī),如果執(zhí)行其他虛擬機(jī),由于實(shí)現(xiàn)機(jī)理可能不同,運(yùn)行結(jié)果也可能不同。