大家好,欢迎来到IT知识分享网。
今天有人问我一道简单的面试题,如下的示例代码输出结果是什么?并解释为什么是这个结果?
public static void main(String[] args) { Integer i1 = 127; Integer i2 = 127; System.out.println(i1 == i2); Integer i3 = new Integer(127); Integer i4 = new Integer(127); System.out.println(i3 == i4); Integer i5 = 128; Integer i6 = 128; System.out.println(i5 == i6); }
这是个很有意思的题,先公布答案,输出结果如下:
true false false
如果你对这个结果有疑惑,说明你对Integer的IntegerCache并不了解,请耐心阅读完本文。
下面我结合Integer的部分源码,对这个结果进行讲解。
解释这个结果之前,我们先了解几个知识点。
一、java的拆箱装箱
Java中的数据类型分为两种,一种是基本数据类型(Primitive Type),另一种是引用数据类型(Reference Type)。
拆箱(Unboxing)指的是将包装类型转换成对应的基本数据类型。例如,将Integer对象转换为int类型。
装箱(Boxing)指的是将基本数据类型转换成对应的包装类型。例如,将int类型转换为Integer对象。
Java自动装箱和自动拆箱是为开发者减少代码量而设计的语法糖。例如,可以这样写:
Integer i = 10; //自动装箱 int j = i; //自动拆箱
自动装箱产生的是一个引用类型的对象,它在栈中只是一个指针,引用一个堆中的对象。自动拆箱直接获取了引用对象中保存的值。
在Integer中,自动装箱和自动拆箱的过程,分别是通过自动调用 valueOf 方法和 intValue方法实现的。
Integer i = 10; // 自动装箱,
在这个过程中,Java 会通过 Integer 的 valueOf 方法将 int 类型自动转换为对应的包装类型 Integer 对象。等价于 Integer i = Integer.valueOf(10);
Integer i = 10; //自动装箱 int j = i; //自动拆箱
在第二行代码这个过程中,Java 会通过 Integer 的 intValue方法将包装类型Integer对象i中保存的值自动转换为对应的基本数据类型int。等价于 int j = i.intValue();
二、IntegerCache缓存
通过上面的讲解,我们知道自动装箱的过程是调用 valueOf 方法实现的。如下是 Integer 的 valueOf 方法的源码。
public static Integer valueOf(int i) { // 判断传入的参数i是否在预设缓存范围内 if (i >= IntegerCache.low && i <= IntegerCache.high) // 如果在,则直接从缓存中获取对应的Integer对象 return IntegerCache.cache[i + (-IntegerCache.low)]; // 如果不在,创建一个新的Integer对象 return new Integer(i); }
从源码中我们可以看到,valueOf 方法并不是直接 new 一个 Integer 对象,而是判断 int 值在某个返回内,返回的是 IntegerCache.cache 数组中的某个值。
那么 IntegerCache 是什么呢?
Java中的Integer对象将整数封装为一个对象,以便在需要时能够对它进行操作。但是每次使用Integer.valueOf()方法创建新的Integer对象会造成内存浪费和GC压力增加,因此Java提供了一种Integer缓存技术,可以使得频繁使用的整数对象可以都从缓存中获取,减少对象的创建和销毁,提高程序性能。
Java认为-128到127是经常使用的数字,这个范围内的数字需要被缓存。当我们使用Integer.valueOf()方法创建Integer对象时,如果创建的整数值在这个范围内,那么该方法就会从Integer缓存中获取相应的对象,而不是每次都新创建一个对象。而 IntegerCache 就是用来管理这个返回的 Integer 缓存对象的。
三、答案讲解
了解了 Integer 的 IntegerCache 之后,我们再回到最初的题目上:
public static void main(String[] args) { Integer i1 = 127; Integer i2 = 127; System.out.println(i1 == i2); Integer i3 = new Integer(127); Integer i4 = new Integer(127); System.out.println(i3 == i4); Integer i5 = 128; Integer i6 = 128; System.out.println(i5 == i6); }
首先第一个结果是true,过程其实如下
// 自动装箱过程,valueOf方法返回的是127的Integer对象缓存 Integer i1 = 127; // 自动装箱过程,valueOf方法返回的也是127的Integer对象缓存 Integer i2 = 127; // i1 和 i2 指向的是同一个127的Integer对象缓存,结果是true System.out.println(i1 == i2);
然后第二个结果是false,过程如下
// 直接new的Integer新对象,没有自动装箱,没有使用IntegerCache缓存 Integer i3 = new Integer(127); // 直接new的Integer新对象,没有自动装箱,没有使用IntegerCache缓存 Integer i4 = new Integer(127); // i3 和 i4 不是同一个对象,结果是false System.out.println(i3 == i4);
最后第三个结果是false,过程如下
// 自动装箱过程,但是超出了 -128 ~ 127的返回,valueOf方法返回的是new Integer的新对象 Integer i5 = 128; // 自动装箱过程,但是超出了 -128 ~ 127的返回,valueOf方法返回的是new Integer的新对象 Integer i6 = 128; // i5 和 i6 不是同一个对象,结果是false System.out.println(i5 == i6);
以上就是这道题目的结果讲解。
除了 Integer 有缓存 IntegerCache 以外,包装类 Long 其实也有缓存 LongCache,它的范围也是 -128~127。
四、使用注意事项
虽然Integer缓存机制可以提高程序性能,但是在使用时,也需要注意一些问题。
1.不要依赖于缓存
对于-128~127之间的整数,Java会进行缓存,因此可以将其看作是单例变量。但是如果过于依赖这个特性,可能会导致代码不可读性、可维护性变差。因此,为了代码的可读性和可维护性,建议还是尽量使用new Integer()或者Integer.valueOf(),而不是依赖缓存来创建Integer对象。
2.避免将包装类型用 == 和 != 判断相等
虽然在一些情况下 == 和 != 返回的结果是正确的,但是我们还是不能直接使用 == 和 != 来进行判断。这样的做法增加了系统的不确定性。正确的方式是使用equals()方法。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/92818.html