当你运行Integer i =1时,发生了什么?

时间:2023-02-12 17:05:09

引言:今天上班时,有个同事在群里问了一句
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = a;
        b = 2;
        System.out.println(a);
    }

这个输出什么。以我对于我们猿的尿性的熟知程度,当机立断告诉他 “1”,然后他问了一句为什么是1。我想了一会儿居然不能很好的解释,于是就去探究了一番。当Integer=1时,我们的JVM做了什么。


使用工具 :javac,javap,文本文档,显示器,键盘,鼠标,脑子  (脑子:我没有你这个逗逼主人)

首先,我先去查看了一下Integer的源码,发现了两段关键代码

  public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
  public Integer(int value) {
        this.value = value;
    }
 我们可以看到  Integer类中只有一个属性  -->”value”,并且没有getter/setter。哦..没有getter/setter????excuse me? 那我怎么赋值啊摔!难道JVM做了什么见不得人的勾当吗!看来仅仅是阅读源码是不够了。于是本人暗搓搓的从小黑屋里拖出一个小公举(工具)↓

javap
找到.java文件  噼里啪啦在CMD里面输入了
javac main.java  
javap -c main

两句话之后

CMD给我吐了一个这玩意儿↓

Compiled from "main.java"
public class main {
  public main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       4: astore_1
       5: aload_1
       6: astore_2
       7: iconst_2
       8: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      11: astore_2
      12: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
      15: aload_1
      16: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      19: return
}
里面一些指令码,助记符就不详细说了,有兴趣可以去找某度或者某g查一波

分析:

在上面的代码中

字节码表示 他先将1从常量池中取出 用的是iconst  具体的代码是
iconst_1
取出后调用了

  public static Integer valueOf(int i) 
这个函数(往上翻一个滚轮或者一个半个屏幕就找到了)进行了赋值

因为位于-128到127之间,所以使用了Integer内部的cache进行赋值,然而其实并没有什么不同

   private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }
可以看到取得的还是cache数组里面的一个元素 当然也是一个Integer的对象。如下

 cache[k] = new Integer(j++);
取得对象后,执行下面的astore对应的指令码将其入栈

 astore_1

到此为止

最上的上述代码中

 Integer a = 1;
这一句话已经执行完毕了。

但是我们还没有就这样轻易的狗带!(脑子:你还是狗带吧。。),我们马上又要执行下面一句话了。

  Integer b = a;
先把a的值取出来,使用的是
aload_1
然后把它和b联系上,就用下面这一行,简单粗暴!

astore_2
然后

  Integer b = a;
这一句话也执行完了。

接下来是b=2

 b = 2;
讲道理b=2和上面的a=1并没有太大的不同,而且我们的编译工具也是讲道理的(编译工具:快夸我!)所以

7:iconst_2
8: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
11: astore_2
三句话一执行,搞定。

剩下的指令就是print的了 我们可以忽略他们。

至此

  Integer a = 1;
        Integer b = a;
        b = 2;
这三句话都执行完毕了。总结一下:

1:a=1的时候从常量池取了1然后调用了valueof方法把一个值为1的Integer对象关联给它了

2:b=a的时候,把b指向了a,其实也就是把b指向了上述cache中的那个值为1的Integer对象

3:b=2的时候,重新调用了valueof方法,b重新指向了值为2的Integer对象,a表示毫无影响

所以这就是为什么 执行结果是1而不是2了。


至于超过了cache,其实也是一样的,只不过数值对象由cache数组中的值变成了直接new的值。



并且!

String 也有这个特性,有兴趣的童鞋们可以去试着看看。


(完结撒花,抛砖引玉,如有纰漏,还望指正)当你运行Integer i =1时,发生了什么?