自动装箱与缓存
现象
有以下代码:
1 public class Main { 2 public static void main(String[] args) { 3 Integer i1 = 127; 4 Integer i2 = 127; 5 Integer i3 = new Integer(127); 6 Integer i4 = new Integer(127); 7 8 System.out.println(i1 == i2);//true 9 System.out.println(i3 == i4);//false 10 } 11 }
控制台输出: true false,
我们知道,第3、4行发生了自动装箱,生成了Integer对象,并将对象的引用赋值给i1和i2,“==”比较的是对象的引用,控制台输出看,i1和i2保存了同一个Integer对象的引用。
下面对上述代码进行反编译:
/* * Decompiled with CFR 0.149. */ package com.learn.java; public class Main { public static void main(String[] args) { Integer i1 = Integer.valueOf((int)127); Integer i2 = Integer.valueOf((int)127); Integer i3 = new Integer((int)127); Integer i4 = new Integer((int)127); System.out.println((i1 == i2 ? 1 : 0) != 0); System.out.println((i3 == i4 ? 1 : 0) != 0); } }
从反编译结果看,Integer类自动装箱执行了valueOf方法。
本质
下面是Integer类中构造方法和ValueOf(int)方法的源码以及注释:
package java.lang; import java.lang.annotation.Native; public final class Integer extends Number implements Comparable<Integer> { /** * Constructs a newly allocated Integer object that represents the specified int value. * 创建一个新的、包装了指定int数值的Integer实例。 * @params value - the value to be represented by the Integer object. */ public Integer(int value) { this.value = value; } /** * Returns an Integer instance representing the specified int value. If a new Integer instance * is not required, this method should generally be used in preference to the constructor * Integer(int), as this method is likely to yield significantly better space and time * performance by caching frequently requested values. * 返回一个包装了指定int数值的Integer实例。除非必须返回一个Integer实例,应该优先使用valueOf(int)方法而 * 不是Integer(int)方法。valueOf(int)方法通过事先缓存常用的数值,表现出更好的时间、空间性能。 * * This method will always cache values in the range -128 to 127,inclusive, and may cache other * values outside of this range. * 默认情况下,缓存-128~127范围内的值,缓存范围可根据需要进行一定的调整。 * * @param i - an int value. * @return an 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); } }
下面是valueOf(int)方法中使用到的IntegerCache内部类的源码:
/** * Cache to support the object identity semantics of autoboxing for values between -128 and 127 * (inclusive) as required by JLS. * * The cache is initialized on first usage. The size of the cache may be controlled by the * {-XX:AutoBoxCacheMax=<size>} option. During VM initialization, * java.lang.Integer.IntegerCache.high property may be set and saved in the private system * properties in the sun.misc.VM class. * 缓存在首次使用时完成初始化,缓存的范围(上界)可通过 -XX:AutoBoxCacheMax 配置。 */ private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); // 127是缓存的最小上界 i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }
上面源码中的信息可以总结为以下几点:
- valueOf(int)方法不一定返回新Integer实例的引用。如果int数值在缓存范围内,返回的是缓存中已经存在的Integer实例的引用,这意味着,在不同的地方,使用相同的int数值调用valueOf(int)方法,可能得到相同的引用。
- 缓存在第一次使用valueOf(int)方法时创建。所以第一次使用valueOf(int)方法需要消耗额外的时间。
- 默认情况下,缓存数值在-128~127范围内的实例。
- 可以使用参数调整缓存的上界。最大上界为Integer.MAX_VALUE,最小上界为127(默认为该值)
- 最多缓存Integer.MAX_VALUE个元素。
拓展
下面看看其他包装类有没有与Integer类一样的缓存机制。
1. Byte
public final class Byte extends Number implements Comparable<Byte> { public static Byte valueOf(byte b) { final int offset = 128; return ByteCache.cache[(int)b + offset]; } private static class ByteCache { private ByteCache(){} static final Byte cache[] = new Byte[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Byte((byte)(i - 128)); } } }
从源码看,Byte也有类似的缓存机制,但是缓存的范围是固定的。
2. Short
public final class Short extends Number implements Comparable<Short> { public static Short valueOf(short s) { final int offset = 128; int sAsInt = s; if (sAsInt >= -128 && sAsInt <= 127) { // must cache return ShortCache.cache[sAsInt + offset]; } return new Short(s); } private static class ShortCache { private ShortCache(){} static final Short cache[] = new Short[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Short((short)(i - 128)); } } }
从源码看,Short也有类似的缓存机制,但是缓存的范围是固定的。
3. Long
public final class Long extends Number implements Comparable<Long> { public static Long valueOf(long l) { final int offset = 128; if (l >= -128 && l <= 127) { // will cache return LongCache.cache[(int)l + offset]; } return new Long(l); } private static class LongCache { private LongCache(){} static final Long cache[] = new Long[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Long(i - 128); } } }
从源码看,Long也有类似的缓存机制,但是缓存的范围是固定的。
4. Float
public final class Float extends Number implements Comparable<Float> { public static Float valueOf(float f) { return new Float(f); } }
从源码看,Float没有缓存机制。
5. Double
public final class Double extends Number implements Comparable<Double> { public static Double valueOf(double d) { return new Double(d); } }
从源码看,Double没有缓存机制。