一、问题引入
我们在日常学习和工作中,可能听说过“128陷阱”,那么什么是“128陷阱”呢?这里要先提到java中的一个基本类型int,以及int所对应的包装类Integer。
那么大家可以思考下面这段代码会输出什么值呢?
int n = 128;
int n1 = 128;
int m = 127;
Integer m1 = 127;
int e = 128;
Integer e1 = 128;
Integer d = 127;
Integer d1 = 127;
Integer f = 128;
Integer f1 = 128;
Integer g = 129;
Integer g1 = 129;
System.out.println("n==n1:"+(n==n1));
System.out.println("m==m1:"+(m==m1));
System.out.println("d==d1:"+(d==d1));
System.out.println("e==e1:"+(e==e1));
System.out.println("f==f1:"+(f==f1));
System.out.println("g==g1:"+(g==g1));
下面是输出结果
n==n1:true
m==m1:true
d==d1:true
e==e1:truef==f1:false
g==g1:false
那么输出结果和你期待的是一样的吗?如果不相同那我们接下来需要考虑为什么会出现这样的结果呢?
上面的问题总共分为4组:
-
int n = 128;和int n1 = 128; 这一组数据之间的比较就很简单了,就是基本数据类型间的比较,显而易见是相等的。
- int m = 127;和Integer m1 = 127;
这一组数据之间的比较涉及了自动拆箱,什么是自动拆包呢?
其实非常简单,就是说Integer类型的m1在和m进行比较时,会使用m1.intValue()自动转化成int类型,之后也就是简单的值与值之间的比较了。
- int e = 128;和Integer e1 = 128;
- 剩下三组放一起看
Integer d = 127; Integer d1 = 127; Integer f = 128; Integer f1 = 128; Integer g = 129; Integer g1 = 129;
这些数据均是Integer类型,照理来说,这些数据之间的比较应该相同才是,即:三组结果同为false或同为true。可是结果却不是这样
e==e1:true
f==f1:false
g==g1:false
如果大家验证可以发现Integer类型在值为128时发生,两个同为128的Integer类型对象,之间的比较开始变为false,也就是说128是true和false之间的临界值,这种现象被称为“128陷阱”。
二、探究原因
1.“128陷阱”产生原因
我想现在大家都比较好奇,“128”陷阱是怎么产生的呢?,除了128以外,是否还有其他临界值呢?接下来,让我们进入源码,来探究“128陷阱”是怎么发生的
首先我们需要了解Integer a = 10是怎么创建的,简单来说这件事情就四个字,自动装箱,没错就是和自动拆箱对应的。仔细讲一下的话就是
Integer a = 10
相当于
Integer a = Integer.valueOf(10)
想必聪明的你已经猜到了,“128陷阱”就是发生在,Integer.valueOf(10)中的
2.进入Integer.ValueOf()函数
源码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这里可以看到,当形参i的值处于区间[IntegerCache.low,IntegerCache.high]之间的时候,方法会直接返回一个Integer数组中的元素
(也就是IntegerCache.cache[i + (-IntegerCache.low)]),所以只要i的值在区间内,那么他们返回的对象就是同一个(对象地址也相同),只有在i不在区间内的时候,该方法才会使用构造方法new Integer(i)返回一个新的对象,这个时候即使i值相同其生成的对象地址也就不相同了,所以此时比较返回值将是false;
3.IntegerCache.low,IntegerCache.high和数组IntegerCache.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) {
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 IntegerCache() {}
}
IntegerCache.low,IntegerCache.high和数组IntegerCache.cache的值都是在这代代码中定义的
具体来说就是
static final int low = -128; static final int high; int h = 127; high = h; cache = new Integer[(high - low) + 1];这几段代码定义的
所以如果我们赋值的数i在[-128,127]之间,他就会返回cache[i - (-128)]中已经创建好的对应Integer对象。如果不在之间,就会返回一个新new的Integer对象。
三、总结
我们在学习工作不仅要清楚一件事情或者工具该怎么使用,更要清楚在使用工具的过程中,java是怎么实现的,内部具体发生的事情,是怎么发生的。只有这样才能够越学越清楚,才能真正的融会贯通。