Java 包装类型的缓存机制

Java包装类型的缓存机制

Integer 缓存机制

背景

Integer 最常见的面试题,就是问Integer的值如何比较相等。比如:

Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 true

Integer i1 = 33;
Integer i2 = new Integer(33);
System.out.println(i11 == i22);// 输出 false

Integer i1 = new Integer(33);
Integer i2 = new Integer(33);
System.out.println(i3 == i4);// 输出 false
Integer i1 = 129;
Integer i2 = 129;
System.out.println(i1 == i2);// 输出 false

源码解析

前面的几个可能好理解,主要是最后一个都是129,为什么还是输出false,这个就是Integer 的缓存机制,我们看下Integer 的源码。

/**
* 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 {@code -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.
*/
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);
                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() {
    
    }
}

通过这个缓存的静态内部类就可以看出来,Integer的缓存值是从-128 - 127之间的值,我们再看一下,刚刚那个例子的编译以后的class文件的字节码文件内容:

// class version 52.0 (52)
// access flags 0x21
public class com/example/demoweb/controller/tt {

  // compiled from: tt.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 7 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/example/demoweb/controller/tt; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
    // parameter  args
   L0
    LINENUMBER 9 L0
    SIPUSH 129
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    ASTORE 1
   L1
    LINENUMBER 10 L1
    NEW java/lang/Integer
    DUP
    SIPUSH 129
    INVOKESPECIAL java/lang/Integer.<init> (I)V
    ASTORE 2
   L2
    LINENUMBER 11 L2
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    ALOAD 1
    IF_ACMPNE L3
    ICONST_1
    GOTO L4
   L3
   FRAME FULL [[Ljava/lang/String; java/lang/Integer java/lang/Integer] [java/io/PrintStream]
    ICONST_0
   L4
   FRAME FULL [[Ljava/lang/String; java/lang/Integer java/lang/Integer] [java/io/PrintStream I]
    INVOKEVIRTUAL java/io/PrintStream.println (Z)V
   L5
    LINENUMBER 14 L5
    RETURN
   L6
    LOCALVARIABLE args [Ljava/lang/String; L0 L6 0
    LOCALVARIABLE i1 Ljava/lang/Integer; L1 L6 1
    LOCALVARIABLE i2 Ljava/lang/Integer; L2 L6 2
    MAXSTACK = 3
    MAXLOCALS = 3
}

我们可以看到Integer i1 = 129;其实字节码实际执行的是valueOf方法

   L0
    LINENUMBER 9 L0
    SIPUSH 129
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    ASTORE 1
   L1

再看看valueof方法的源码

    public static Integer valueOf(int i) {
    
    
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

先从缓存拿,缓存拿不到则会重新new一个对象,这里的129不在缓存里面,所以i1i2是两个不同的对象,所以相比较为false 。

反思:那Integer 类型的值如何比较呢?

我们再看看equals的方法:

    public boolean equals(Object obj) {
    
    
        //先判断对象是不是int类型,是则比较的是int值,而不是比较两个对象
        if (obj instanceof Integer) {
    
    
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

故Integer类型的比较需要使用equals

Long缓存机制

有了Integer缓存的基础,其实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类型的缓存范围也是-128-127之间取值

其他基础类型

Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False

猜你喜欢

转载自blog.csdn.net/wagnteng/article/details/132042275
今日推荐