美文网首页
Java Integer的内存存储在堆和常量池中,及String

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

作者: 仁昌居士 | 来源:发表于2017-06-15 17:16 被阅读0次

    先看代码:

            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();
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Java Integer的内存存储在堆和常量池中,及String

          本文链接:https://www.haomeiwen.com/subject/xncuqxtx.html