虽然这里只是以 Integer 为例,但是其他的 primitive 包装类,原理也是一样的,就不再多解释了。

Integer 类声明

public final class Integer extends Number implements Comparable<Integer>

其中, Number 类的修饰:

public abstract class Number implements java.io.Serializable

通过源码可以看到,Java中 primitive 的类型(byte, short, int, long, float, double)所代表的对象类型(Byte, Short, Integer, Long, Float, Double),都是 final, Serializable , Comparable 的。

Number 抽象类

可以看到,它提供了对各个Number子类的统一各种基础类型的抽象表示接口。

public abstract int intValue();

public abstract long longValue();

public abstract float floatValue();

public abstract double doubleValue();

public byte byteValue() {
        return (byte)intValue();
}

public short shortValue() {
        return (short)intValue();
}

属性

  • public static final int MIN_VALUE = 0x80000000; 表示 Integer 的最小值
  • public static final int MAX_VALUE = 0x7fffffff; 表示 Integer 的最大值
  • public static final Class TYPE = (Class) Class.getPrimitiveClass(“int”); 底层表示 int 的类型的Class
  • public static final int SIZE = 32; 表示 int 是32位的
  • public static final int BYTES = SIZE / Byte.SIZE; 表示 int 占用多少字节数

  • private final int value; 它内部表示的 int 的value

内部Integer缓存类 IntegerCache

看类的注解可知:JLS规范(Java Language Specification)要求,至少 要缓存 -128 <= N <= 127 范围的 Integer 对象(注意,最低的下限是 -128,这个值是不变修改的,只允许修改上限,而且上限必须 >= 127)

可以通过VM参数来控制它的大小

-XX:AutoBoxCacheMax=<size>

或通过系统属性来配置:
-Djava.lang.Integer.IntegerCache.high=<size>

设置参数后,它的范围就是 [-128, && >= 127] 了(当然,size可以设置得比 127 小,如果比 127 小的话,它会忽略你设置的这个值,而是使用 127 这上限了):

i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);

而且,上限最大为 (Integer.MAX_VALUE-(-low) -1,即是 2147483518)

注意

java -XX:+PrintFlagsFinal -version | grep AutoBoxCacheMax
     intx AutoBoxCacheMax                           = 128                                 {C2 product}
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)

可以看到,AutoBoxCacheMax 这个参数,是在 C2 product 中才会生效的(即以 -server 启动的VM)

64位的JDK只会以 server 来启动,32位的,可以才可以选择 -client 或者 -server

IntegerCache 属性

static final int low = -128;
static final int high;
static final Integer cache[];

可以看到,cache 字段缓存了所设置的 Integer 对象。

缓存池大小的影响

默认情况

    public static void main(String[] args) {
        Integer a = 300;
        Integer b = 300;
        System.out.println(a == b);
    }

它返回的是 false

修改参数后的影响

-XX:AutoBoxCacheMax=300
或
-Djava.lang.Integer.IntegerCache.high=300

这样子的话,上面的代码就会返回 true

Integer缓存池的作用

  1. 它的作用就是在调用 valueOf 时,尽可能地返回缓存池中的 Integer 所代表的 int 的对象。
  2. 在缓存池中的对象之间的比较,就可以直接用 == 来比较了。

== 之间的比较,肯定是比通过方法调用 equals 来得更快。

返回缓存对象

valueOf() 方法

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

可以看到,当 i 的值在缓存范围之内,它会返回所缓存的对象,而不是创建新的对象。

valueOf() VS parseInt()

注意,valueOf() 返回的是 Integer,而 parseInt() 返回的是 int

所以,一定要清楚地知道自己到底需要的是什么数据类型,然后去选择适当的方法。

原则上,能用 primitive 类型,就不要用对象类型,但实际还要根据代码的业务逻辑需要去选择。

虽然 valueOf() 有缓存对象,但对象毕竟还是重量级的(无论它是不是有缓存),通常它的性能都要比 primitive 类型低。

equals()

所有 primitive 类的包装类,都已经覆盖了 Object 的这个方法(Object里的 equals 方法是用 == 来比较的),它表示的是包装类所代表对应的 primitive 的值是否相等。

    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

修改内部表示的 int 值

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Integer a = 127;
        Field field = Integer.class.getDeclaredField("value");
        field.setAccessible(true);
        field.set(a, -99999);
        System.out.println(a);

        Integer b = 127;
        System.out.println(b);

        Integer aa = new Integer(127);
        System.out.println(aa);
    }

输出的是

-99999
-99999
127

可以看到,因为 127 在 Integer 缓存池的范围内,所以所有 Integer 的字面值(即不是通过 new 来创建的)其实指向的是同一个对象,那我们通过反射修改了它的运行时表示,那么修改后的表示会反映到所有之后的指向同一个对象的引用了。

Integer 与 int 进行比较时会发生什么?

package org.agoncal.sample.jmh;

public class Test {

    public static void main(String[] args) {
        Integer a = 12342;
        System.out.println(a == 12342323);
    }

}


相对应的字节码:

    Code:
      stack=3, locals=2, args_size=1
         0: sipush        12342
         3: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1
         7: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        10: aload_1
        11: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
        14: ldc           #5                  // int 12342323
        16: if_icmpne     23
        19: iconst_1
        20: goto          24
        23: iconst_0
        24: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
        27: return

通过字节码,可以看到上面的源码被编译后的伪代码:

Integer a = Integer.valueOf(12342);
boolean result = false;
if (a.intValue() == 12342323) {
    result = true;
}
System.out.println(result);

即 Integer 与 int (其他的 primitive 类型与对应的包装类型一样)比较时, Integer 会调用 intValue() 方法(注意,如果这时 Integer 对象为 null,会发生 NullPointerException)转换为 int 之后再进行比较。