曾经遇见过一道关于Integer的面试题:
Integer a = 180, b = 180;
Integer c=1,d=1;
System.out.println(a==b);
System.out.println(c==d);
输出为:
false
true
一开始也不明白,后来看了下Integer的源码,才知道Integer默认将-128~127之间的整数存储到了一个数组中,我们使用的时候,直接调用数组中数据,而超过这个范围就会重新创建Integer对象,看下源代码就明白了
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 =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
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);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
然后又出现了一个新问题,下面这个方法交换的是什么呢?
private static void swap(Integer a, Integer b) {
Integer temp = a;
a = b;
b = temp;
}
一开始,我认为应该是传递的引用,调用后,应该会发生数据值的改变,但事实上数据却并没有发生交换。那就继续看代码吧
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
看到这里我们也就明白了,我们的数据值保存在value这个私有常量值中,当两个值交换时,因为Integer的自动拆装箱导致传递的是数据值,也就是值传递,因此不会修改原值。想要交换两个Integer对象的值应该通过修改这个value值来实现,因此我们可以通过反射获取这个私有的value属性然后进行修改
private static void swap1(Integer a, Integer b) {
try {
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true);
int temp = a.intValue();
field.set(a, b.intValue());
field.set(b,temp);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
测试下,发现还是不对,原来field.set()这个方法传入的是对象, field.set(a, b.intValue())这个方法中,b.intValue()相当于Integer.valueOf(a.intValue()).intValue(),而temp只是int,因此并没有修改b中的value值,好,继续改
private static void swap1(Integer a, Integer b) {
try {
Field field = Integer.class.getDeclaredField("value");
field.setAccessible(true);
Integer temp = Integer.valueOf(a.intValue());
field.set(a, b.intValue());
field.set(b,temp);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
我们通过反射机制获取Integer属性值value,然后用a的值重新创建一个对象,这样我们传递的就是一个地址值,从而实现真正的交换
注意
以前创建Integer对象的时候,我们都是直接new出来,但是先java已经废弃了这个方法,查看文档发现改为使用Integer.valueOf()方法,查看valueOf方法我们知道,由于-128~127缓存存在,当我们用第一种方式时每次都会产生一个新的实例,而当你使用静态工厂方法时,不一定会产生一个新的实例,所以这样是现在推荐使用valueOf()这个方法的原因吧
总结
通过这个问题的分析,我们主要涉及到Integer的拆装箱、通过反射机制去修改privat final value、Integer的-128~127的缓存,其他的问题我们以后再研究吧