一、什么是128陷阱?
下面用一段代码展示了什么是128陷阱:
public static void main(String[] args) {
Integer a = 10;
Integer b = 10;
Integer aa = 127;
Integer bb = 127;
Integer aaa=128;
Integer bbb=128;
Integer aaaa=130;
Integer bbbb=130;
System.out.println(a==b); //true
System.out.println(aa==bb); //true
System.out.println(aaa==bbb); //false
System.out.println(aaaa==bbbb); //false
}
}
上述代码运行结果如下:
true
true
false
false
其中在判断(aaa == bbb)和(aaaa==bbbb)时输出了false,很明显输出结果并不符合预期;
而(a==b)和(aa=bb)输出true在逻辑上也并不正确,因为Integer是引用类型,变量用“==”比较的是两个变量的地址;
综上便总结出128陷阱问题是:为什么当两个相同值的Integer对象进行“==”比较时,当值小于128时输出的结果为TRUE;当值大于128时输出为FALSE?
在理解为什么会产生128陷阱之前,我们提前要理解两个知识点:
1.在java中,“==”与equals()方法的区别与特点:
①基本类型一般用“==”来进行比较、效果是直接比较两值是否相等;
②引用类型要用equals()方法进行比较、不同引用类型的equals()方法具有不同的比较方式;
③如果用“==”来比较两个引用类型的变量,实际上比较的是两者的地址是否相等;
注意:在Object类中,equals()方法与“==”功能是相同的;Object下不同的类会按需重写equals()方法。从而不同引用类型的equals()方法具有不同的比较方式。
2.包装类的自动拆箱与封箱:
在Java中我们在创建一个Integer对象时的常用语法为:Integer a = 10;
而不是像其他类一样通过new关键字类创建对象,这是因为在Java SE5以后,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能。
- 自动装箱:就是将基本数据类型自动转换成对应的包装类。
- 自动拆箱:就是将包装类自动转换成对应的基本数据类型。
Integer i =10; //自动装箱
int b= i; //自动拆箱
Integer i=10 可以替代 Integer i = new Integer(10); 这就是因为Java帮我们提供了自动装箱的功能,不需要开发者手动去new一个Integer对象。
通过观察Integer类的源码我们可以发现;Java是通过类静态方法Integer.valueOf()实现的自动装箱;通过类静态方法intValue()实现的自动拆箱:
//下面两种写法等效(自动装箱)
Integer a =10;
Integer a = Integer.valueOf(10);
//下面两种写法等效(自动拆箱)
int b = a;
int b = a.intValue();
二、源码分析
1.Integer.valueOf()的源码
上面我们一直已知,java的自动装箱和拆箱是通过自动调用Integer.valueOf()和Integer.intValue()实现的;我们下面来看看他们的源码;
@IntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
通过valueOf()不难看出,想要理解该方法做了什么,首先要了解IntegerCache是什么
2、IntegerCache是Integer的一个静态内部类,源码如下:
private static class IntegerCache {
static final int low = -128; //下限是-128
static final int high;
static final Integer[] cache;
static Integer[] archivedCache;
static {
// high value may be configured by property(上限high的值可以通过属性进行配置)
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
// Load IntegerCache.archivedCache from archive, if possible
// 如果可能,从归档中加载 IntegerCache.archivedCache
CDS.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// Use the archived cache if it exists and is large enough
// 如果归档缓存存在且足够大,则使用它
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
结合上述来个段代码代码;我们可以大致了解;java设计师在设计时,为了减少创建Integer对象的时间开销,提升代码效率,预先创建好了一个静态类IntegerCache;
其中静态成员变量有:
static final Integer[] cache; 静态Integer数组用于存储数值为-128~127的Integer对象共256个
static final int low = -128; 用于表示数组最小下标,-128
static fina int high; 用于表示数组最大下标,
因此;我们便知道在Integer对象创建过程中:
Integer a = 10; ——> Integer a = Integer.valueOf(10) ; ——> 而方法valueOf()所做的事有:
1、如果传入的int值在cache缓存-128~127的范围中,则直接返回预先创建好的对象,如果不在范围中,则创建一个新的Integer对象返回。
2、如果传入value数值在-128~127范围内,那么所有在这个范围内创建的数值相同的Integer变量,实际都指向同一个对象,因此地址也相同,所以用“==”比较的结果为true;
3、如果传入value数值不在cache范围内,那么每次被创建的对象都是一个新的对象,即通过new关键字由JVM分配新地址。因此地址不相同,所以用“==”比较的结果为false;
#本篇是参考以下文章后所做的心得体会,当做一篇总结笔记,供大家参考;
Java Integer128陷阱详解_integer 128-CSDN博客
java 1.8 archive,jar 意思不是Java Archive-CSDN博客
#
标签:拆箱,Java,进阶,int,自动,static,128,Integer,IntegerCache From: https://blog.csdn.net/Future_yzx/article/details/140286776