一、从自动装箱开始
如果我们在创建一个Integer对象的时候,使用这样的方式:“Integer a = 3;”
那么Java是如何帮助我们创建一个对象的呢?是使用“Integer a = new Integer(3);”的简写吗?
答案很明显不是的。可以做一个实验简单的证明:1.1创建Integer
如果如上面所猜想的那样,那么对象c和d比较结果应该也是false,但是测试结果却是true。也就是说指向的是同一个对象。
在阅读Integer源码查找原因的时候,发现了其中竟然还有一个内部类:1.2IntegerCache
看这个类的名称就知道,这是一个类似缓存的类,我们很轻易的就可以联想到,对象c和d应该与这个内置的缓存有关。
二、IntegerCache类2.1IntegerCache源码
可以从代码中看出,系统默认帮我们创建了一个数组,其中保存有从-128到127的数字,当然你也可以去修改cache数组的上限:可以设置java.lang.Integer.IntegerCache.high属性并将其保存在sun.misc.VM类中的私有系统属性中,也可以由{@code -XX:AutoBoxCacheMax = }选项控制。
cache数组在Integer类加载的时候就已经准备好了,对象c和d的值刚好在cache数组的范围内,我们可以猜想对象c和d都是取自cache数组,下面可以同过一个实验证明:2.2猜想证明
取一个超过数组保存范围的数,得到的结果果然为false,此时c和d分别指向两个不同的对象。那么自动装箱到底是如何实现的呢?我继续在Integer类中查找使用到了IntegerCache类的方法,最终找到一个方法可以解释以上的现象:2.3自动装箱
在Integer自动装箱的时候,默认是调用的上图这个方法,首先判断i值的范围,如果在cache数组的范围内,则从数组中获取对象,在开始的实验中,由于对象c和d都在这个范围内,所以都是取自cache数组,故而指向的是同一个对象,所以比较的结果true。
在其后的证明实验中,对象c和d的取值超过了范围,所以返回的是一个新建的对象,就相当于是:Integer c = new Integer(200);所以得出的结果为false
三、传递
有上面得到的结论,不由得有了一个想法,如果我们更改c的值,那么d的值会不会也跟着变呢?
于是我做了一个测试3.1改变值(1)
此时发现对象a和b所指向的地址不一致了,其中发生了什么呢?于是我继续在源码中查找答案:发现Integer类中,value变量是用final修饰的3.2finalValue
原来Integer中的值是无法修改的,那么在我们将整数4赋值给变量b的时候发生了什么呢?
请看下面的实验3.3实验
虽然a和b所指向的地址不同了,那么到底是谁改变了地址呢?还是都改变了地址呢?
为搞明白这些问题,我额外增加了一个变量c,从结果可以发现,a的地址没有改变,改变的只有b,所以可以做出这样的结论:在“b=4;”这条语句中,其实相当于“b=valueOf(4);”