在一次采访中,我的一个朋友被问到如果我们有两个Integer对象, Integer a = 127; Integer b = 127;
Integer a = 127; Integer b = 127;
为什么当a == b
都持有两个单独的对象时,其值为true
? 在本文中,我将尝试回答这个问题,并尝试解释答案。
简短答案
这个问题的简短答案是,将int
常量直接分配给Integer
引用是自动装箱概念的一个示例,在该示例中,由编译器处理到对象转换代码的常量值,因此在编译阶段,编译器将Integer a = 127;
转换为Integer a = 127;
Integer a = Integer.valueOf(127);
。
Integer
类为内部整数维护一个内部IntegerCache,这些整数默认范围为-128 to 127
并且Integer.valueOf()
方法从该缓存中返回上述范围的对象。 因此a == b
返回true,因为a
和b
都指向同一个对象。
长答案
为了理解简短的答案,让我们首先了解Java类型,Java中的所有类型都分为两类
- 基本类型: Java中有8种基本类型(字节,短型,整型,长型,浮点型,双精度型,字符型和布尔型),它们直接以二进制位的形式保存其值。
例如int a = 5; int b = 5;
int a = 5; int b = 5;
这里a
和b
直接持有的5二进制值,如果我们试图比较a
和b
使用a == b
我们实际上是在比较5 == 5
返回true。 - 引用类型:除基本类型外,所有其他类型都位于引用类型的类别下,例如类,接口,枚举,数组等,引用类型保存对象的地址,而不是对象iteslf。
例如,Integer a = new Integer(5); Integer b = new Integer(5)
Integer a = new Integer(5); Integer b = new Integer(5)
,此处a和b不保存二进制值5
而是a
和b
保存两个单独对象的内存地址,其中两个对象都包含值5
。 因此,如果尝试使用a == b,
比较a
和b
a == b,
则实际上是在比较这两个单独的内存地址,因此我们得到false
,要对a
和b
执行实际相等,需要执行a.euqals(b)
。 引用类型又分为4类: 强引用,软引用,弱引用和幻像引用 。
而且我们知道Java为所有原始类型提供包装器类,并支持自动装箱和自动拆箱。
// Example of auto-boxing, here c is a reference type
Integer c = 128; // Compiler converts this line to Integer c = Integer.valueOf(128); // Example of auto-unboxing, here e is a primitive type
int e = c; // Compiler converts this line to int e = c.intValue();
现在,如果我们创建两个整数对象a
和b,
并尝试使用相等运算符==
进行比较,则将得到false
因为两个引用都持有不同的对象
Integer a = 128; // Compiler converts this line to Integer a = Integer.valueOf(128);
Integer b = 128; // Compiler converts this line to Integer b = Integer.valueOf(128);System.out.println(a == b); // Output -- false
但是,如果我们为a
和b
都分配值127
并尝试使用等于运算符==
进行比较,那么为什么会true
?
Integer a = 127; // Compiler converts this line to Integer a = Integer.valueOf(127);
Integer b = 127; // Compiler converts this line to Integer b = Integer.valueOf(127);System.out.println(a == b); // Output -- true
正如我们在代码中看到的那样,我们为a
和b
分配了不同的对象,但是只有当a
和b
都指向同一个对象时, a == b
才能返回true。
那么比较如何返回true? 这里到底发生了什么? 是a
和b
指向相同的对象?
到目前为止,我们知道代码Integer a = 127;
是自动装箱的示例,编译器自动将此行转换为Integer a = Integer.valueOf(127);
。
因此,正是Integer.valueOf()
方法返回这些整数对象,这意味着该方法必须在幕后进行某些操作。
并且,如果我们看一下Integer.valueOf()
方法的源代码,我们可以清楚地看到,如果传递的int文字i
大于IntegerCache.low
且小于IntegerCache.high
则该方法从IntegerCache
返回Integer对象。 IntegerCache.low
和IntegerCache.high
默认值分别是-128
和127
。
换句话说而不是创建和retruning新的整数对象, Integer.valueOf()
方法返回整数从内部对象IntegerCache
如果传递的INT字面大于
-128
且小于127
。
/*** 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*/public static Integer valueOf(int i) {if (i >= IntegerCache.low && i <= IntegerCache.high)return IntegerCache.cache[i + (-IntegerCache.low)];return new Integer(i);}
Java缓存落入-128到127范围内的整数对象,因为该整数范围在日常编程中被大量使用,从而间接节省了一些内存。
如您在下图中所看到的, Integer
类维护一个内部静态IntegerCache
类,该类充当缓存并保存从-128到127的整数对象,这就是为什么当我们尝试获取127
整数对象时总是得到相同的对象。
当类由于static block
而被加载到内存时,首次使用时将初始化缓存。 高速缓存的最大范围可以由-XX:AutoBoxCacheMax
JVM选项控制。
此缓存行为仅适用于Integer
对象,类似于Integer.IntegerCache
我们还有ByteCache
, ShortCache
, LongCache
, Byte
CharacterCache
, Short
,
Long
, Character
。
Byte,Short和Long具有固定的缓存范围,介于–127到127(含)之间,而Character的范围是0到127(含)之间。 只能通过参数对Integer修改范围,而不能对其他参数进行修改。
您可以在此Github存储库中找到本文的完整源代码,请随时提供宝贵的反馈。
翻译自: https://www.javacodegeeks.com/2018/11/integer-cache-integer-valueof127-true.html