先看两个小栗子,整片博文围绕这个例子进行详解。
你可以先自己猜猜运行结果,然后再运行代码哦。这样理解会比较深刻。
先一个个来!!!!
栗子1:
public class IntegerTest {
public static void main(String[] args) {
Integer a = new Integer(11);
Integer b = 11;
int c = 11;
System.out.println(a == c);
System.out.println(a == b);
}
}
===================================================
防止你这么快看到答案!
===================================================
true
false
你的结果是不是也是这个呢,如果不是也没关系,看完我下面所写的,会让你对Integer类的自动拆箱装箱有深刻的理解。
首先我来大概解释下这是怎么回事
1、a==c 为什么会是true呢?
因为c为基本类型,c(int)与a(Integer)比较时,c(Integer)会发生自动拆箱,a会拆箱成int来与c进行==比较。所以这里为true。
你听到这可能还是不理解拆箱,不用担心后面会讲到的,这只是大概告诉你发生了什么事,后面会用代码来证明的。
只有代码才是对的!
2、a==b为什么会是false呢?
因为a是Integer类型,b也是Integer类型,b这里进行了装箱操作,Integer b = 11,其实是等于Integer b = Integer.valueOf(3);
这里为什么会等于这个后面会讲到,你想想,Integer是引用类型,==是比较地址的,ab两个栈中的指向堆中的地址都不一样,所以他们是false。
==================================================
好了 重头戏来了
==================================================
这里你可以看着我来操作,当然也可以一起操作,这都很简单,只需要你有jdk+jre环境即可。
这里讲个题外知识,因为后面会用到,那就是javap
javap定义
javap是 Java class文件分解器,可以反编译(即对javac编译的文件进行反编译),也可以查看java编译器生成的字节码。用于分解class文件。
- 好了先创建一个IntegerTest.java文件
- 将上面的栗子拷贝进去
- 在当前目录 shift+鼠标右键打开powerShell窗口
- 然后输入两个命令 1、javac .\IntegerTest.java 2、javap -c IntegerTest
- 到这里就完事了,后面刷出一大堆javap -c 反汇编后的信息。咱们来阅读一下。
=============================================================
上面你看不懂没关系,我也不大懂,只需要有个javap指令集,将不懂的命令找出来解释就完事了 。
这里有个小福利,javap指令集的帖子
http://bbs.gupaoedu.com/forum.php?mod=viewthread&tid=295&extra=page%3D1
=============================================================
咱们来解析一下 咱们栗子中的每一行代码 对应指令集中的什么地方,根据咱们反汇编的class文件来分析jvm是如何运转的呢。
1、Integer a = new Integer(11);
咱们根据javap指令可以知道,invokespecial 根据编译时类型来调用实例方法,这个方法是什么呢,就是Integer的构造方法。
Integer的构造方法:
所以这行代码没什么好说的了,就是实例化一个Integer对象。设置value属性的值为11。主要是为了让大家熟悉下javap指令。
2、Integer b = 11;
重头戏来了,这里讲了装箱操作。
这里看到没有!!!invokestatic!他这里做了什么,他调用了Integer中的一个方法名叫做valueOf的静态方法。
咱们看看Integer的源码:
找到了,我来解释下这里说了什么,如果入参i的值-128<= i <=127就会从缓存中获取对象,Integer在堆中有很多个常用的对象,所以如果满足就直接引用地址就Ok了,否则将在堆中实例化一个新的对象。
这就是装箱操作,装箱操作调用了valueOf方法,这个是在编译期间就这样做的,所以Integer b = 3;是不是等同于Integer b = Integer.valueOf(3);啊!对吧。
我之前说过只有代码才是对的,才能让别人信服,不是泛泛而谈,说什么就是什么,而且 这样 一步一步分析,可以让你理解更加深刻,以后遇到问题也可以有个思路。
咱们继续讲。
3、int c = 11;
这个没啥好说的
就是将11压入操作数栈,然后将其store到变量c中,这里istore_3就是变量c,我以后还会分享JVM,会提到这方面的知识,希望大家关注我哦!
4、System.out.println(a == c);
这里讲的就是拆箱操作,咱们看看aload_1 从局部变量1中装载引用类型值,invokevirtual调度对象的实例方法,
这里看好!这里调用了这个Integer对象的什么方法啊,是不是intValue方法啊!
咱们看看源码:
这是个啥意思,是不是拆箱的时候将 Integer引用对象转换成了int基本类型啊,实际上a==c 是不是就是a.intValue() == c啊,这里也是jdk编译期间对其进行的操作。你看着是a==c其实class文件中是a.intValue()==c。
这就是拆箱操作。
5、System.out.println(a == b);
这个就是两个引用类型的比较了,==比较地址,他们两个地址不一样,指向堆中的实例化对象也不一样。
所以就是false了。
总结
Integer装箱操作:在编译期将Integer a = 3;这种格式的代码,编译成Integer a = Integer.valueOf(3);,这就是装箱操作。
Integer拆箱操作:在编译期将Integer转换成int类型,会调用int z = a.intValue()方法,拆箱操作在栗子中表现为,int类型与Integer类型进行==比较,此时Integer会进行拆箱操作。
到这里就讲完了,大家听了这么多,应该都听懂了吧。!!!(盲目自信!!!!)
还有一个栗子就留给大家帮我解答一下吧。
栗子2:
public class IntegerTest {
public static void main(String[] args) {
Integer a = 11;
Integer b = 11;
Integer c = 129;
Integer d = 129;
System.out.println(a == b);
System.out.println(c == d);
}
}
最后!!希望大家看完这篇文章后能够给我一点点feedback!!!谢谢大家!!!!!!