引言:今天上班时,有个同事在群里问了一句
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查一波
分析:
在上面的代码中
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 也有这个特性,有兴趣的童鞋们可以去试着看看。
(完结撒花,抛砖引玉,如有纰漏,还望指正)