偶然看到一篇博客,题为《为什么1000 == 1000返回为False,而100 == 100会返回为True?》,代码是如下的一段:
会得到结果是
确实很奇怪,因为以前一直都以为int和Integer是一样的,只不过Integer 是包装类。
然后把Integer改成了int
意外发现了结果是
我们知道,如果两个引用指向同一个对象,那么==就成立;反之,如果两个引用指向的不是同一个对象,那么==就不成立,即便两个引用的内容是一样的。因此,结果就会出现false。
如果你查看Integer.java类,你会找到IntegerCache.java这个内部私有类,它为-128到127之间的所有整数对象提供缓存。
privatestaticclassIntegerCache{
staticfinalintlow=-128;
staticfinalinthigh;
staticfinalIntegercache[];
static{
// high value may be configured by property
inth=127;
StringintegerCacheHighPropValue=sun.misc.VM
.getSavedProperty("java.lang.Integer.IntegerCache.high");
if(integerCacheHighPropValue!=null){
try{
inti=parseInt(integerCacheHighPropValue);
i=Math.max(i,127);
// Maximum array size is Integer.MAX_VALUE
h=Math.min(i,Integer.MAX_VALUE-(-low)-1);
}catch(NumberFormatExceptionnfe){
// If the property cannot be parsed into an int, ignore it.
}
}
high=h;
cache=newInteger[(high-low)+1];
intj=low;
for(intk=0;k<cache.length;k++)
cache[k]=newInteger(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assertIntegerCache.high>=127;
}
privateIntegerCache(){
}
}
这个东西为那些数值比较小的整数提供内部缓存,当进行如此声明时:
它的内部就是这样的:
如果我们观察valueOf()类函数,我们可以看到
如果值在-128到127之间,它就会返回该缓存的实例。
因此
,
两者指向同样的对象。
这就是为什么这段代码的结果为true了:
现在你可能会问,为什么会为-128到127之间的所有整数设置缓存?
这是因为在这个范围内的小数值整数在日常生活中的使用频率要比其它的大得多,多次使用相同的底层对象这一特性可以通过该设置进行有效的内存优化。你可以使用reflection API任意使用这个功能。
接下来试着使用反射来做一些改变
static{
try{
Class<?>cache=Integer.class.getDeclaredClasses()[0];
FieldmyCache=cache.getDeclaredField("cache");
Fieldhigh=cache.getDeclaredField("high");
Fieldlow=cache.getDeclaredField("low");
myCache.setAccessible(true);
high.setAccessible(true);
low.setAccessible(true);
/*去除final修饰符的影响,将字段设为可修改的*/
FieldmodifiersField=Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(high,high.getModifiers()&~Modifier.FINAL);
modifiersField.setInt(low,low.getModifiers()&~Modifier.FINAL);
modifiersField.set(myCache,myCache.getModifiers()&~Modifier.FINAL);
MethodmodifiersMethod=Method.class.getDeclaredMethod("getModifiers");
modifiersMethod.setAccessible(true);
high.setInt(high,high.getModifiers()&~Modifier.FINAL);
high.set("high",1000);
low.setInt(low,low.getModifiers()&~Modifier.FINAL);
low.set("low",-1001);
/*修改cache数组的信息,将数组的大小和内容都修改*/
Integer[]ca=newInteger[2002];
intj=-1001;
for(intk=0;k<ca.length;k++)ca[k]=newInteger(j++);
myCache.set(null,ca);
}catch(Exceptione){
e.printStackTrace();
}
}
这里我们改变了IntegerCache的cache数组大小,同时也修改了上下界,这样之后会发现,这个输出的结果会变成
这边发生变化的原因就是因为修改了上下界的判断范围,导致缓存的范围是从-1001~1000之间,那么1000这个数就会以相同对象存在缓存中。这样之后判断a == b就会是true
但是!这边出现了一个很有趣的现象:
如果执行下面的语句:
得到的结果是:
就是说这时候e的值会变成-871,这是怎么回事呢?
经过一系列的研究、推测、搜索等过程,终于发现了原因:
对于基本类型的静态常量,JAVA在编译的时候就会把代码中对此常量中引用的地方替换成相应常量值。
就是说,关于valueOf方法:
这段代码在编译的时候已经被java自动优化成这样的:
所以这个时候,无论传入的数是否在修改后的上下界之间,这个数只要是在-128~127之外,就还是会执行return IntegerCache.cache[i + 128]; 这就导致结果出现上面那种现象(由于cache数组已经改变,那么如果是传入的是2,那么return的是cache[130]=-871)。这时候这个方法已经没有办法绕过了,就是说不论怎么修改IntegerCache中的low和high常量的值,这个函数都还是判断-128~127之间的数据进行缓存。这样就会出现明明存的是2,但是输出的结果却是-871的现象。
——这本身是JVM的优化代码提高运行效率的一个行为,但是就会导致我们在用反射改变此常量值时出现类似不生效的错觉。
可以通过反射来达到修改静态内部类、静态变量、静态方法乃至常量。这是反射的比较强大的功能体现。