经典Integer对象赋值128问题
Integer经典面试问题:两个Integer对象都赋值为128,这两个对象比较是否相同?为什么?
回答这个问题,首先我们要知道,在Java中,当你写Integer a = 1;
实际上是调用了Java的自动装箱功能。这会将整数 1
自动装箱为Integer对象,然后将这个对象赋值给变量a。
自动装箱功能是由编译器自动插入的,实际上它相当于执行了如下的代码:
1 | Integer a = Integer.valueOf(1); |
这里的valueOf方法是Integer类的一个静态方法,它的作用就是将传入的参数(通常是基本数据类型)自动转换为对应的包装类对象。对于Integer类来说,就是将整数值转换为Integer对象。
知道这一点之后,我们看看Integer.valueOf方法的源码:
1 | /** |
注解上说:如果不需要新的Integer实例,则通常应该优先使用此方法,而不是构造函数Integer(int),因为通过缓存频繁请求的值,该方法可能会产生更好的空间和时间性能。此方法将始终缓存-128到127(包括-128到127)范围内的值,并可能缓存此范围之外的其他值。
接下来看看IntegerCache缓存的实现:
1 | /** |
缓存在第一次使用时初始化。缓存的大小可以通过-XX:AutoBoxCacheMax=
选项来控制。在虚拟机初始化过程中,可以设置java.lang.Integer.IntegerCache.high属性,并保存在sun.misc.VM类的私有系统属性中。
IntegerCache缓存范围的最小值固定为-128,最大值默认为127.但是最大值是可以被重新定义的,那么可定义的范围是多少呢?
1 | i = Math.max(i, 127); |
上面的两行已经定义了IntegerCache缓存最大值的范围:
定义最大值范围大于等于127;
定义最大值范围小于等于
Integer.MAX_VALUE - (-low) -1;
Integer.MAX_VALUE的定义:(2的23次方 - 1)
1 | public static final int MAX_VALUE = 0x7fffffff; |
IntegerCache缓存的范围定义好后,为区间范围赋值。
1 | for(int k = 0; k < cache.length; k++) |
最后通过断言检查 IntegerCache.high 是否大于等于 127。
1 | // range [-128, 127] must be interned (JLS7 5.1.7) |
IntegerCache.high 是 Java 中 Integer 缓存的一部分,这个缓存用于存储 -128 到 127 之间的整数,以提高这个范围内的整数在多次使用时的性能。
assert 是一个用于调试的语句,当 IntegerCache.high <
127 时,该语句将抛出 AssertionError。
JLS7 5.1.7 是 Java Language Specification 的一个部分,这部分规定了对于字符串字面值,编译器必须将它们在编译期放入字符串池中,以避免内存浪费。
这段代码的目的是确保 IntegerCache 的范围在 [-128, 127] 内,因为这是 String 缓存这个优化策略可以应用的范围。如果 IntegerCache 的范围超出了这个范围,那么可能会导致无法进行这种优化,从而可能降低性能。
需要注意的是,assert
语句在默认情况下是关闭的,如果你想要开启它,需要在启动 JVM 时添加
-ea(或 -enableassertions)参数。
因此,当写 Integer i1 = 128; 和
Integer i2 = 128;
,实际上在内存中创建了两个不同的对象,即使它们包含相同的值。
可以通过调用 equals()
方法来比较这两个对象的内容是否相同,而不是使用 ==
运算符。这是因为
==在比较对象时实际上是检查它们是否指向内存中的同一个对象,而不是比较它们的内容。而
equals() 方法则是比较对象的内容。
如果使用 equals()
方法来比较这两个对象,它们会被视为相等,因为它们包含相同的内容。但是,如果你使用
==运算符来比较,它们会被视为不相等,因为它们是不同的对象。