JDK 之 Integer 源码阅读笔记
Contents
虽然这里只是以 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,
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缓存池的作用
- 它的作用就是在调用 valueOf 时,尽可能地返回缓存池中的 Integer 所代表的 int 的对象。
- 在缓存池中的对象之间的比较,就可以直接用 == 来比较了。
== 之间的比较,肯定是比通过方法调用 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 之后再进行比较。