先看一段代码
1 | Integer f1 = 127, f2 = 127, f3 = 128, f4 = 128; |
结果是:true false false true true true
自动装箱和拆箱的概念
Java 中引入了基本数据类型,针对着集中基本数据类型,又引入了相对应的包装类型。
Java 为每个原始类型提供了包装类型:
- 原始类型: boolean,char,byte,short,int,long,float,double
- 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
从Java 5开始引入了自动装箱/拆箱机制,使得原始类型和包装类型可以相互转换。
前面代码中就用到了自动装箱/拆箱机制:
1 | Integer f1 = 127, f2 = 127, f3 = 128, f4 = 128; // 自动装箱成Integer类型 |
自动装箱来得到一个引用数据类型时,jvm 实际上调用了 valueOf()
方法
问题解析
因为 == 比较的是两个对象是否相同,明显 f1和f2 指向的是同一个对象,f3 和 f4 指向的不是同一个对象。
为什么会这样呢?
这就归结于 Java 对于 Integer 与 int 的自动装箱与拆箱的设计,运用了一种设计模式:享元模式。
为了加大对简单数字的重利用,java定义:在自动装箱时对于值从–128到127之间的值,它们被装箱为Integer对象后,会存在内存中被重用,始终只存在一个对象。
看一下 Integer.valueOf
的代码实现:
1 | public static Integer valueOf(int i) { |
在 IntegerCache.low
和 IntegerCache.high
之间的数会直接向缓存池 IntegerCache
中获取缓存对象,如果在这个范围之外则重新创建对象。
再来看一下 IntegerCache
的实现:
1 | private static class IntegerCache { |
其实就是创建了个 [-128, 127] 的数字的缓存池。
那么,为什么要这么设计呢?一般来说,小数字的使用频率很高,将小数字保存起来,让其始终仅有一个对象可以节约内存,提高效率。
而如果超过了从 –128 到 127 之间的值,被装箱后的 Integer
对象并不会被重用,即相当于每次装箱时都新建一个 Integer
对象。这就是为什么 f3 == f4 为false的原因了。
以上的现象是由于使用了自动装箱所引起的,如果你没有使用自动装箱,而是跟一般类一样,用 new 来进行实例化,就会每次 new 就都一个新的对象,所以 f5 == f6 为false;
至于 i1 == i2 和i3 == i4,这属于基本数据类型的比较。
i5 == f6 :当int和Integer进行 == 比较的时候,Java会把Integer进行自动拆箱,也就是把Integer转成int类型,所以这里进行比较的是int类型的值,所以结果即为true。
String 类的自动装箱拆箱
1 | String s1 = "str", s2 = "str"; |
结果为 true false。
String 有个字符串常量池 - String Pool,通过自动装箱得到的对象都存储在这个字符串常量池里面,相同的字符串不会重复创建。
通过显式 new 出来的对象不在这里面,每次都会创建一个新的对象。
Long 类的自动装箱拆箱
Long 的自动装箱和 Integer 类似:
1 | public static Long valueOf(long l) { |
1 | private static class LongCache { |