深入分析Integer底層原理

本文將介紹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)似的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市腐晾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌篙耗,老刑警劉巖溉跃,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異十减,居然都是意外死亡栈幸,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)帮辟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)速址,“玉大人,你說(shuō)我怎么就攤上這事由驹∩置” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)并炮。 經(jīng)常有香客問(wèn)我默刚,道長(zhǎng),這世上最難降的妖魔是什么逃魄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任荤西,我火速辦了婚禮,結(jié)果婚禮上嗅钻,老公的妹妹穿的比我還像新娘皂冰。我一直安慰自己,他們只是感情好养篓,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布秃流。 她就那樣靜靜地躺著,像睡著了一般柳弄。 火紅的嫁衣襯著肌膚如雪舶胀。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天碧注,我揣著相機(jī)與錄音嚣伐,去河邊找鬼。 笑死萍丐,一個(gè)胖子當(dāng)著我的面吹牛轩端,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逝变,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼基茵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了壳影?” 一聲冷哼從身側(cè)響起拱层,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宴咧,沒(méi)想到半個(gè)月后根灯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掺栅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年烙肺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氧卧。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桃笙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出假抄,到底是詐尸還是另有隱情怎栽,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布宿饱,位于F島的核電站熏瞄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏谬以。R本人自食惡果不足惜强饮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望为黎。 院中可真熱鬧邮丰,春花似錦、人聲如沸铭乾。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炕檩。三九已至斗蒋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間笛质,已是汗流浹背泉沾。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妇押,地道東北人跷究。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像敲霍,于是被迫代替她去往敵國(guó)和親俊马。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348