本文將介紹Java中Integer的相關(guān)知識(shí)中贝,分析Integer緩存的原理和作用梢莽。
Integer緩存
在Integer類(lèi)內(nèi)部定義了一個(gè)私有的靜態(tài)類(lèi)IntegerCache萧豆,用來(lái)實(shí)現(xiàn)Integer的緩存常量池。
直接深入源碼進(jìn)行分析:
// Integer類(lèi)中私有的靜態(tài)類(lèi) 承載cache的實(shí)現(xiàn)
private static class IntegerCache {
static final int low = -128;// 最小支持為-128
static final int high;//最大支持
static final Integer cache[];// 用來(lái)裝載緩存 常量池
static {
// -128~127 這個(gè)范圍的整數(shù)值是使用最廣泛的
int h = 127;
// Java6中可以通過(guò)調(diào)整JVM啟動(dòng)參數(shù)來(lái)設(shè)置最大值
// 根據(jù)應(yīng)用程序的實(shí)際情況 靈活的調(diào)整來(lái)提高性能
// JVM 的啟動(dòng)參數(shù) -XX:AutoBoxCacheMax=size 修改最大值
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
// 獲取較大者
i = Math.max(i, 127);
// 設(shè)置最大值不能超過(guò)Inter.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// 如果該值配置錯(cuò)誤則忽略該參數(shù)配置的值昏名,使用默認(rèn)范圍-128~127
}
}
high = h;
// 初始化數(shù)組容量為127 + 128 + 1(以默認(rèn)區(qū)間為參考)
cache = new Integer[(high - low) + 1];
int j = low;
// 緩存通過(guò)for循環(huán)來(lái)實(shí)現(xiàn)涮雷,創(chuàng)建范圍內(nèi)的整數(shù)對(duì)象并存儲(chǔ)到cache數(shù)組中
// 程序第一次使用Integer的時(shí)候需要一定的額外時(shí)間來(lái)初始化該緩存
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
該緩存的作用是為了節(jié)省內(nèi)存,提高性能轻局。
在給一個(gè)Integer對(duì)象直接賦一個(gè)int類(lèi)型的值得時(shí)候:
Integer num = 100;
Java會(huì)通過(guò)自動(dòng)裝箱機(jī)制將int類(lèi)型值轉(zhuǎn)為Integer類(lèi)型洪鸭。自動(dòng)裝箱是指在編譯時(shí)期編譯器自動(dòng)調(diào)用包裝類(lèi)型中的valueOf()方法样刷,進(jìn)行自動(dòng)的轉(zhuǎn)換。
Integer#valueOf方法的實(shí)現(xiàn)
public static Integer valueOf(int i) {
// 如果命中緩沖區(qū)的范圍則直接返回已有對(duì)應(yīng)對(duì)象的引用
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
// 不在緩沖范圍內(nèi)的直接new新的對(duì)象
return new Integer(i);
}
針對(duì)[-128,127]這個(gè)范圍內(nèi)的值览爵,Integer會(huì)通過(guò)復(fù)用對(duì)象的方式來(lái)提升內(nèi)存的使用率置鼻,減少新對(duì)象的生成。
Intege類(lèi)重寫(xiě)了equals方法和hashCode方法
hashCode()
@Override
public int hashCode() {
return Integer.hashCode(value);
}
public static int hashCode(int value) {
return value;
}
即Integer的hashCode值返回對(duì)象本身的value值
equals()
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
public int intValue() {
return value;
}
equals方法比較的是兩個(gè)對(duì)象的value值,即兩個(gè)Integer對(duì)象只要邏輯上數(shù)值一致蜓竹,則equals方法返回true箕母。
常見(jiàn)的使用場(chǎng)景
通過(guò)分析六種不同類(lèi)型的比較,進(jìn)而真正弄懂Integer與int的區(qū)別俱济,這也是面試中經(jīng)常會(huì)問(wèn)到的地方嘶是。
1.兩個(gè)Integer對(duì)象 通過(guò) == 比較
Integer a = new Integer(100);
Integer b = new Integer(100);
System.out.println(a == b); // 輸出false
通過(guò)new生成的兩個(gè)對(duì)象是永遠(yuǎn)不相等的,調(diào)用==時(shí)比較的是兩個(gè)引用指向的內(nèi)存地址姨蝴。
2.兩個(gè)對(duì)象 通過(guò)equals方法比較
Integer a = new Integer(100);
Integer b = new Integer(100);
System.out.println(a.equals(b)); // 輸出true
當(dāng)調(diào)用equals方法進(jìn)行比較時(shí)俊啼,只要兩個(gè)對(duì)象的值相同,則返回true左医。Integer重寫(xiě)了equals方法授帕,核心邏輯是判斷對(duì)象表示的value值是否相同。
3.基本類(lèi)型和Integer類(lèi)型 通過(guò) == 比較
Integer a = new Integer(100);
int b = 100;
System.out.println(a == b); // 輸出true
當(dāng)包裝類(lèi)型和基本類(lèi)型進(jìn)行比較時(shí)浮梢,Java會(huì)進(jìn)行自動(dòng)拆箱跛十,將包裝類(lèi)型拆為對(duì)應(yīng)的基本類(lèi)型,然后進(jìn)行比較秕硝,實(shí)際上比較的兩個(gè)int值是否相同芥映。
4.在緩存范圍內(nèi)的比較
Integer a = 100;
Integer b = 100;
System.out.println(a == b); // 輸出true
System.out.println(a.equals(b)); // 輸出true
對(duì)應(yīng)非new生成的Integer對(duì)象,Java會(huì)通過(guò)自動(dòng)裝箱機(jī)制远豺,調(diào)用Integer的valueOf方法將int類(lèi)型轉(zhuǎn)為Integer類(lèi)型奈偏。如果該int值在[-128,127]區(qū)間范圍內(nèi),則生成的對(duì)象引用都會(huì)指向Integer內(nèi)部的常量池中對(duì)應(yīng)的值的對(duì)象躯护,因此兩個(gè)引用指向的內(nèi)存地址也相同即==時(shí)返回true惊来,調(diào)用equals時(shí)比較的是value值,因此也返回true棺滞。
5.在緩存范圍外的比較
Integer a = 128;
Integer b = 128;
System.out.println(a == b); // 輸出false
System.out.println(a.equals(b));//輸出true
非new生成的Integer對(duì)象裁蚁,如果int值超出了[-128,127]這個(gè)區(qū)間,則不會(huì)使用常量池中的對(duì)象继准,由valueOf方法可知會(huì)重新new一個(gè)Integer對(duì)象枉证。因此兩個(gè)引用a和b,調(diào)用==比較時(shí)是比較的對(duì)象內(nèi)存地址移必,所以返回false室谚;但是調(diào)用equals方法比較的是value值,因此返回true。
6.new生成的Integer對(duì)象與直接賦值的Integer對(duì)象的比較
Integer a = new Integer(100);
Integer b = 100;
System.out.println(a == b); //輸出false
System.out.println(a.equals(b));// 輸出true
當(dāng)非new生成的對(duì)象和new生成的對(duì)象進(jìn)行比較時(shí)舞萄,非new生成的對(duì)象根據(jù)其value值是否在默認(rèn)緩存區(qū)間內(nèi)而選擇是否復(fù)用對(duì)象眨补,通過(guò)new生成的對(duì)象是在堆中重新開(kāi)辟的內(nèi)存空間,因此兩者指向的內(nèi)存地址肯定不一樣倒脓,所以調(diào)用==時(shí)返回false撑螺;equals方法比較對(duì)象的邏輯值value,因此返回true崎弃。
開(kāi)發(fā)中常見(jiàn)的建議
- int是基本數(shù)據(jù)類(lèi)型甘晤,只占用4個(gè)字節(jié),Integer是一個(gè)對(duì)象饲做,當(dāng)表示一個(gè)值時(shí)Integer占用的內(nèi)存空間要高于int類(lèi)型线婚,從節(jié)省內(nèi)存空間考慮,建議使用int類(lèi)型盆均。
- Integer類(lèi)型必須進(jìn)行初始化后才能使用塞弊,否則會(huì)引起NullPointerException異常。
- Integer對(duì)象默認(rèn)值為null泪姨,int默認(rèn)值為0游沿。
- 針對(duì)一些特殊的場(chǎng)景比如考試成績(jī)分為沒(méi)有參加考試和成績(jī)?yōu)?,int的默認(rèn)值為0肮砾,顯然不合適這種場(chǎng)景诀黍;Integer可以區(qū)分出未賦值和值為0的區(qū)別。
- 使用Integer類(lèi)型時(shí)仗处,建議采用直接賦值的形式而不是通過(guò)new產(chǎn)生新對(duì)象眯勾,提高對(duì)內(nèi)存的利用率。
Integer a = 100;
替代
Integer a = new Integer(100);
- 當(dāng)程序中大量使用數(shù)值時(shí)婆誓,可以根據(jù)實(shí)際情況適當(dāng)擴(kuò)展常量池緩沖區(qū)的區(qū)間上限吃环,
修改JVM的啟動(dòng)參數(shù): -XX:AutoBoxCacheMax=size ,進(jìn)而節(jié)省內(nèi)存洋幻,提升性能郁轻。 - 當(dāng)使用Integer類(lèi)型時(shí),在進(jìn)行兩個(gè)對(duì)象比較的時(shí)候鞋屈,推薦使用equals方法,而不是直接調(diào)用"=="故觅。
阿里巴巴開(kāi)發(fā)手冊(cè)中提到:
包裝類(lèi)型間的相等判斷應(yīng)該用equals厂庇,而不是'=='
所有的包裝類(lèi)對(duì)象之間值的比較,全部使用equals方法比較输吏。
說(shuō)明:對(duì)于Integer var=?在-128至127之間的賦值权旷,Integer對(duì)象是在IntegerCache.cache產(chǎn)生,會(huì)復(fù)用已有對(duì)象,這個(gè)區(qū)間內(nèi)的Integer值可以直接使用==進(jìn)行判斷拄氯,但是這個(gè)區(qū)間之外的所有數(shù)據(jù)躲查,都會(huì)在堆上產(chǎn)生,并不會(huì)復(fù)用已有對(duì)象译柏,這是一個(gè)大坑镣煮,推薦使用equals方法進(jìn)行判斷。
其他緩存的對(duì)象
這種緩存行為不僅適用于Integer對(duì)象鄙麦。我們針對(duì)所有整數(shù)類(lèi)型的類(lèi)都有類(lèi)似的緩存機(jī)制典唇。
Byte,Short胯府,Long 有固定范圍: -128 到 127介衔。對(duì)于 Character, 范圍是 0 到 127。除了 Integer 可以通過(guò)參數(shù)改變范圍外骂因,其它的都不行炎咖。
Double類(lèi)的valueOf方法會(huì)采用與Integer類(lèi)的valueOf方法不同的實(shí)現(xiàn)。很簡(jiǎn)單:在某個(gè)范圍內(nèi)的整型數(shù)值的個(gè)數(shù)是有限的寒波,而浮點(diǎn)數(shù)卻不是乘盼。
注意,Integer影所、Short蹦肴、Byte、Character猴娩、Long這幾個(gè)類(lèi)的valueOf方法的實(shí)現(xiàn)是類(lèi)似的款青。Double逢勾、Float的valueOf方法的實(shí)現(xiàn)是類(lèi)似的。