Java Integer的内存存储在堆和常量池中,及String的内存存储

先看代码:

        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在编译的时候,被翻译成-> 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

测试的结果:

Paste_Image.png

在Java中,对于对象==是比较两个对象的地址。
Integer的存储
1、Integer是int的封装类,当基础变量(int)和Integer进行比较时,Integer会自动拆箱(jdk1.5以上)了后,再去和int进行比较,所以判断i1==i2,i1==i3都为true。
2、对JVM为了节省空间, 当Integer的值落在-128~127之间时,如i4,i5;此时JVM首先检查是否已存在值为127的Integer对象。如果是,则i4,i5直接是引用已存在对象,即i4 = i5。所以判断i4 == i5 为 true
那为什么范围是-128到127呢:java在编译Integer i4 = 127的时候,是被翻译成-> Integer i4 = Integer.valueOf(127)的;这就是判断i4==i6为true的原因了。接下来,关键就是看valueOf()函数了。只要看看valueOf()函数的源码就会明白了。
Paste_Image.png

Paste_Image.png

从代码上看出, 当要赋的值在[-128~127]范围内,则会直接指向该值的引用,不用去new 个对象到堆内存中去了。因为Integer已经缓存了数据。但是,当超出了数组的范围值时,就会去自动装箱在堆内存中建一个新对象。所以,对i8,i9,即使基础变量值一样,封装类对象却指向不同地址。所以判断i8==i9为false
3、对于显式的new Integer(int i),JVM将直接分配新空间。这样两个new Integer(int i)分配的堆内存空间肯定不是同一个,所以判断i10== i11为false。
此外两点, 显式的new Integer(int i)和int自动装箱成的Integer,并不会是同一个对象。他们也是两个不同的存在堆内存中的空间。所以判断i2==i3为false;显式的new Integer(int i)和i范围在[-128~127]内的直接赋值Int类型的值也不指向同一个空间。如判断i4==i7为false,i7指向常量池,而i4指向的是堆内存。

接下来是我的另一个思考: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));

输出结果:


Paste_Image.png

我们可以看出什么呢?常量池里的对应值改变了,所有调用2的值返回的都是129。这是为什么,我画了个图,请看:

Paste_Image.png

当我通过反射修改值时,改变了就是常量池中,IntegerCache.cache[]数组中的值。而我们的下标并没有改变。(这里可能会有人说我常量池与IntegerCache.cache[]数组之间理解有问题,我还没深入研究过,只是想表示是以数组方式存储的)
所以第三步我们所谓的赋值“=”,对应的是常量池中2对应的下标里的值,改成了129。
就好比:int[10] i = {0,1,2,3,9} 变成了int[10] i = {0,1,129,3,9} 。
因此上面的输出结果没问题,所有对应的Integer里,本来是2的值都变成了129。所以!!!没事别像我一样瞎想。

String的存储
对于使用字面量赋值方式。JVM为了节省空间,会首先查找JVM中是否有对应的字符串常量。如果已经存在,则直接返回该引用,而无需重新创建对象。对象new创建方式,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();
        }

对应的输出:

Paste_Image.png

一个例子,来自: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;

还有一点,就是String的拼接,作用于哪要看虚拟机和编译的jdk版本。我没深入研究,你们看着办吧。反正,对于频繁长拼接,用StringBuffer更好。

最后附上整个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在编译的时候,被翻译成-> 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();
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 相关概念 常量池的定义常量池(constant pool):指的是在编译期被确定,并被保存在已编译的.class文...
    snoweek阅读 785评论 0 4
  • 首先学习JVM相关需要需要内存的组成。 基本内容 堆java动态创建对象,即对于new的一个实例对象。但是需要注意...
    CatherYan阅读 543评论 0 4
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,571评论 18 399
  • 这篇文章是我之前翻阅了不少的书籍以及从网络上收集的一些资料的整理,因此不免有一些不准确的地方,同时不同JDK版本的...
    高广超阅读 15,528评论 3 83
  • 一千个人眼中有一千个哈姆雷特,爱情也是如此 人与人之间的缘分大概就是如此吧,从得知那个的名字那时开始,便开始产生好...
    悄悄话秘密阅读 511评论 0 0