Java Integer的內(nèi)存存儲在堆和常量池中,及String的內(nèi)存存儲

先看代碼:

        int i1 = 128;
        Integer i2 = 128;
        Integer i3 = new Integer(128);
        //Integer會自動拆箱為int,所以為true
        System.out.println(i1 == i2);
        System.out.println(i1 == i3);
        System.out.println(i2 == i3);
        System.out.println("**************");
        Integer i4 = 127;//java在編譯的時(shí)候,被翻譯成-> Integer i5 = Integer.valueOf(127);
        Integer i5 = 127;
        Integer i6 = Integer.valueOf(127);
        System.out.println(i4 == i5);//true
        System.out.println(i4 == i6);//true
        Integer i7 = new Integer(127);
        System.out.println(i4 == i7); //false
        Integer i8 = 128;
        Integer i9 = 128;
        System.out.println(i8 == i9);//false
        Integer i10 = new Integer(128);
        Integer i11 = new Integer(128);
        System.out.println(i10 == i11);  //false

測試的結(jié)果:

Paste_Image.png

在Java中,對于對象==是比較兩個(gè)對象的地址。
Integer的存儲
1、Integer是int的封裝類,當(dāng)基礎(chǔ)變量(int)和Integer進(jìn)行比較時(shí),Integer會自動拆箱(jdk1.5以上)了后,再去和int進(jìn)行比較,所以判斷i1==i2,i1==i3都為true。
2、對JVM為了節(jié)省空間, 當(dāng)Integer的值落在-128~127之間時(shí),如i4,i5;此時(shí)JVM首先檢查是否已存在值為127的Integer對象。如果是,則i4,i5直接是引用已存在對象,即i4 = i5。所以判斷i4 == i5 為 true
那為什么范圍是-128到127呢:java在編譯Integer i4 = 127的時(shí)候,是被翻譯成-> Integer i4 = Integer.valueOf(127)的;這就是判斷i4==i6為true的原因了。接下來,關(guān)鍵就是看valueOf()函數(shù)了。只要看看valueOf()函數(shù)的源碼就會明白了。
Paste_Image.png

Paste_Image.png

從代碼上看出, 當(dāng)要賦的值在[-128~127]范圍內(nèi),則會直接指向該值的引用,不用去new 個(gè)對象到堆內(nèi)存中去了。因?yàn)镮nteger已經(jīng)緩存了數(shù)據(jù)。但是,當(dāng)超出了數(shù)組的范圍值時(shí),就會去自動裝箱在堆內(nèi)存中建一個(gè)新對象。所以,對i8,i9,即使基礎(chǔ)變量值一樣,封裝類對象卻指向不同地址。所以判斷i8==i9為false
3、對于顯式的new Integer(int i),JVM將直接分配新空間。這樣兩個(gè)new Integer(int i)分配的堆內(nèi)存空間肯定不是同一個(gè),所以判斷i10== i11為false。
此外兩點(diǎn), 顯式的new Integer(int i)和int自動裝箱成的Integer,并不會是同一個(gè)對象。他們也是兩個(gè)不同的存在堆內(nèi)存中的空間。所以判斷i2==i3為false;顯式的new Integer(int i)和i范圍在[-128~127]內(nèi)的直接賦值Int類型的值也不指向同一個(gè)空間。如判斷i4==i7為false,i7指向常量池,而i4指向的是堆內(nèi)存。

接下來是我的另一個(gè)思考:Integer作為對象包裝器類,是沒有set()方法的。他的值是final的。


Paste_Image.png

那我就是想給他改值怎么辦。可以!用反射。

          Integer j1 = 2;
        Integer j2 = 2;
        System.out.println("j1 = j2? " + (j1 == j2));  //true
        try {
            Field field = Integer.class.getDeclaredField("value");
            field.setAccessible(true);
            field.set(j2,129);
            System.out.println( "j1+"+j1+"+j2+" + j2
                    +"\nSystem.identityHashCode(j1)"+System.identityHashCode(j1)
                    +"\nSystem.identityHashCode(j2)"+System.identityHashCode(j2)
                    +"\nj1 == j2" + (j1 == j2));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        Integer j3 = 2;
        Integer j4 = 2;
 System.out.println("j3+"+j3+"\nSystem.identityHashCode(j3)"+System.identityHashCode(j3)+"\nj3 = j4? " + (j3 == j4));

輸出結(jié)果:


Paste_Image.png

我們可以看出什么呢?常量池里的對應(yīng)值改變了,所有調(diào)用2的值返回的都是129。這是為什么,我畫了個(gè)圖,請看:

Paste_Image.png

當(dāng)我通過反射修改值時(shí),改變了就是常量池中,IntegerCache.cache[]數(shù)組中的值。而我們的下標(biāo)并沒有改變。(這里可能會有人說我常量池與IntegerCache.cache[]數(shù)組之間理解有問題,我還沒深入研究過,只是想表示是以數(shù)組方式存儲的)
所以第三步我們所謂的賦值“=”,對應(yīng)的是常量池中2對應(yīng)的下標(biāo)里的值,改成了129。
就好比:int[10] i = {0,1,2,3,9} 變成了int[10] i = {0,1,129,3,9} 。
因此上面的輸出結(jié)果沒問題,所有對應(yīng)的Integer里,本來是2的值都變成了129。所以?。?!沒事別像我一樣瞎想。

String的存儲
對于使用字面量賦值方式。JVM為了節(jié)省空間,會首先查找JVM中是否有對應(yīng)的字符串常量。如果已經(jīng)存在,則直接返回該引用,而無需重新創(chuàng)建對象。對象new創(chuàng)建方式,JVM將分配新空間。

String a="1";
        String b="1";
        int aHashCode = System.identityHashCode(a);
        int bHashCode = System.identityHashCode(b);
        System.out.print("\na:"+a+"\nb:"+b);
        System.out.print("\naHashCode:"+aHashCode+"\nbHashCode:"+bHashCode);
        try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] valueChar = (char[]) value.get(b);
        valueChar[0] = '2';
        String c="1";
        String d="2";
        int cHashCode = System.identityHashCode(c);
        int dHashCode = System.identityHashCode(d);
        System.out.print("\na:"+a+"\nb:"+b+"\nc:"+c+"\nd:"+d);
        System.out.print("\naHashCode:"+aHashCode+"\nbHashCode:"+bHashCode+"\ncHashCode:"+cHashCode+"\ndHashCode:"+dHashCode);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

對應(yīng)的輸出:

Paste_Image.png

一個(gè)例子,來自:http://blog.csdn.net/hejingyuan6/article/details/50489171

        String s1 = "china";
        String s2 = "china";
        String ss1 = new String("china");
        String ss2 = new String("china");
        int i = 1;
        int j = 1;
        public static final int i1 = 1;
        public static final int j1 = 1;
        Integer it1 = 127;
        Integer it2 = 127;
        Integer it11 = 128;
        Integer it12 = 128;

還有一點(diǎn),就是String的拼接,作用于哪要看虛擬機(jī)和編譯的jdk版本。我沒深入研究,你們看著辦吧。反正,對于頻繁長拼接,用StringBuffer更好。

最后附上整個(gè)class的測試代碼:

package com.yy007.zxing;


import java.lang.reflect.Field;

/**
 * Created by 仁昌居士 on 2017/6/16.
 * Description:
 */

public class TestAcitivity {
    /**
     * @param args
     */
    public static void main(String[] args) {
        int i1 = 128;
        Integer i2 = 128;
        Integer i3 = new Integer(128);
        //Integer會自動拆箱為int,所以為true
        System.out.println(i1 == i2);
        System.out.println(i1 == i3);
        System.out.println(i2 == i3);
        System.out.println("**************");
        Integer i4 = 127;//java在編譯的時(shí)候,被翻譯成-> Integer i5 = Integer.valueOf(127);
        Integer i5 = 127;
        Integer i6 = Integer.valueOf(127);
        System.out.println(i4 == i5);//true
        System.out.println(i4 == i6);//true
        Integer i7 = new Integer(127);
        System.out.println(i4 == i7); //false
        Integer i8 = 128;
        Integer i9 = 128;
        System.out.println(i8 == i9);//false
        Integer i10 = new Integer(128);
        Integer i11 = new Integer(128);
        System.out.println(i10 == i11);  //false


        System.out.println("**************");
        Integer j1 = 2;
        Integer j2 = 2;
        System.out.println("j1 = j2? " + (j1 == j2));  //true
        try {
            Field field = Integer.class.getDeclaredField("value");
            field.setAccessible(true);
            field.set(j2,129);
            System.out.println( "j1+"+j1+"+j2+" + j2
                    +"\nSystem.identityHashCode(j1)"+System.identityHashCode(j1)
                    +"\nSystem.identityHashCode(j2)"+System.identityHashCode(j2)
                    +"\nj1 == j2" + (j1 == j2));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        Integer j3 = 2;
        Integer j4 = 2;
        System.out.println("j3+"+j3+"\nSystem.identityHashCode(j3)"+System.identityHashCode(j3)+"\nj3 = j4? " + (j3 == j4));


        System.out.println("**************");
        String a="1";
        String b="1";
        int aHashCode = System.identityHashCode(a);
        int bHashCode = System.identityHashCode(b);
        System.out.print("\na:"+a+"\nb:"+b);
        System.out.print("\naHashCode:"+aHashCode+"\nbHashCode:"+bHashCode);
        try {
        Field value = String.class.getDeclaredField("value");
        value.setAccessible(true);
        char[] valueChar = (char[]) value.get(b);
        valueChar[0] = '2';
        String c="1";
        String d="2";
        int cHashCode = System.identityHashCode(c);
        int dHashCode = System.identityHashCode(d);
        System.out.print("\na:"+a+"\nb:"+b+"\nc:"+c+"\nd:"+d);
        System.out.print("\naHashCode:"+aHashCode+"\nbHashCode:"+bHashCode+"\ncHashCode:"+cHashCode+"\ndHashCode:"+dHashCode);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 相關(guān)概念 常量池的定義常量池(constant pool):指的是在編譯期被確定,并被保存在已編譯的.class文...
    snoweek閱讀 852評論 0 4
  • 首先學(xué)習(xí)JVM相關(guān)需要需要內(nèi)存的組成。 基本內(nèi)容 堆java動態(tài)創(chuàng)建對象,即對于new的一個(gè)實(shí)例對象。但是需要注意...
    CatherYan閱讀 622評論 0 4
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,728評論 18 399
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準(zhǔn)確的地方,同時(shí)不同JDK版本的...
    高廣超閱讀 16,053評論 3 83
  • 一千個(gè)人眼中有一千個(gè)哈姆雷特,愛情也是如此 人與人之間的緣分大概就是如此吧,從得知那個(gè)的名字那時(shí)開始,便開始產(chǎn)生好...
    悄悄話秘密閱讀 562評論 0 0

友情鏈接更多精彩內(nèi)容