1. Integer类的基本信息
NOTE : 以JDK 1.8 为准,补充部分属性、方法在 JDK-1.6 / JDK-1.7中的变化说明;
- 继承自: Number类
- 实现了:Serializable和Comparable接口
Integer类的结构和方法可以划分为以下区域:
- 类定义、属性:7个属性,都很好理解;
- 核心方法:构造Integer的方法和一些核心方法;
- int -> String方法:将int数字转换各种进制的字符串 ;
- String -> int方法:将各种进制的字符串解析为int数字 ;
- unsigned -> signed:java的int是有符号的,需要提供处理未符号的其他进制的方法;
- signed -> unsigned:将无符号的其他进制字符串转换为有符号的int数字;
2. 前提基础
有符号和无符号
在计算机表中,正负数的表示可以使用有符号和无符号两种表示,
- Java使用的是有符号的十进制表示方法,最高位为符号位,所以表示范围为 - 2^31 到 2^31;
- 对于无符号表示并不是说无法表示负数,而是最高位也用来表示数本身,负数用“补码”形式表示;
无符号表示可以翻阅很多资料:http://blog.csdn.net/sunweixiang1002/article/details/53048080
自动装箱和拆卸
自动装箱和拆卸是Java的语法糖,之所以需要分析Integer的源码即因为我们在日常编程中有很多自动装箱和拆卸机制,这些会给我们带来一些“坑”;
16进制语法糖
Java编程允许我们用Oxxxxx这样的形式定义一个16进制数据,如 int x = Ox11 ,实际上x就是17,Java编译时会将Ox11翻译为17,所以Integer的进制转换方法,不需要提供类似 : toDeciamlInt(int i); 这样的方法。
3. 核心方法 源码分析
只看使用频率最高的方法,其他的如 int -> String/ String -> Int 、有符号到无符号、进制转换,这些不常用,有需要再看下源码即可。
3.1 属性
7个属性,1个添加自1.8,主要是提升一些效率,避免运行时计算;
// 在Number中实现了Serializable接口
// 所以子类自动实现Serializable接口
public final class Integer extends Number implements Comparable<Integer> {
//最小值,表示 -2^31
//使用二进制分析就是最高位外后面31个0
@Native public static final int MIN_VALUE = 0x80000000;
//最大值,表示2^31 -1
@Native public static final int MAX_VALUE = 0x7fffffff;
//类型缓存
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
// JDK 1.5添加,表示int 数字的位长度为32位;
@Native public static final int SIZE = 32;
// JDK 1.8添加,计算好需要的字节长度,1字节8位,其实就是 4
public static final int BYTES = SIZE / Byte.SIZE;
//真正的值,final的不可以被动态改变
private final int value;
@Native
private static final long serialVersionUID = 1360826667806852920L;
}
3.2 常用的核心方法
之所以使用Integer对象,就是int作为基本类型不具有对象方法,使用Integer对象频率最高的即以下方法。
另外需要使用Integer对象的场景一般如下两种:
- 需要使用一个特殊的值(null)表示无法计算,程序需要自己处理,比如从CacheUtil提供incry方法,如果key不存在则不计算,那么可以返回Integer,如果key不存在返回null,业务判断null则从其他数据源加载值来计算;
- 作为Map的Key,这是最常用的场景,Map要求必须有hashCode和equals。
// 这里定义了一个内部静态类,用来缓存[-128,127]之间的Integer对象;
// 这个缓存是为了JSL的自动装箱机制提供的,可以通过VM参数:-XX:AutoBoxCacheMax=<size>来调整这个缓存范围
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// cache的最大值可以通过属性和vm参数设置
int h = 127;
// 去VM这个类查询参数,看是否配置了AutoBoxCacheMax的值;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// 检测配置的最大值不能超过 MAX_VALUE - 129;
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
}
}
//下面就是提前初始化,new出这些Integer对象出来
high = h;
// 缓存的最小值无法修改是:-128,
// 最大值不能超过 MAX_VALUE - 129 ,
// 如果high超过了,那么数组分配的时候会抛出:NegativeArraySizeException
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
//断言机制,JLS7 要求范围必须大于[-128, 127]
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
//JDK 1.5提供的方法,优先使用valueOf方法替代new方法;
public static Integer valueOf(int i) {
// 先判断是不是在缓存范围内,内部类直接用属性了。
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
//超出缓存范围,直接new一个
return new Integer(i);
}
public static Integer valueOf(String s, int radix) throws NumberFormatException {
return Integer.valueOf(parseInt(s,radix));
}
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
// 一般情况下不推荐使用
public Integer(int value) {
this.value = value;
}
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
// 缩小位数,会抛出高位内容
public byte byteValue() {
return (byte)value;
}
// 缩小位数,会抛弃高位内容
public short shortValue() {
return (short)value;
}
//非常常用的一个方法
public int intValue() {
return value;
}
//扩宽位数
public long longValue() {
return (long)value;
}
//扩宽位数
public float floatValue() {
return (float)value;
}
//扩宽位数
public double doubleValue() {
return (double)value;
}
//默认转换为10进制
public String toString() {
return toString(value);
}
/**
* 仔细看看这个方法,非常简单,就是返回value值作为hashCode.
* Integer.valueOf(1); -> 1;
* */
@Override
public int hashCode() {
return Integer.hashCode(value);
}
//JDK 1.8 抽象出来
public static int hashCode(int value) {
return value;
}
/**
* equals很熟悉,原理还是比较value是否== 但是这里先判断了是不是Integer的;
* 在代码中 Integer x = 1; x.equasl(1L); 这是能编译过去的,但是返回的是false;
* 这会引发一系列问题,尤其是作为Map的Key,要注意,此类场景不常用,但是容易采坑;
*/
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
//JDK 1.2 提供的比较接口,用来排序
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
// 1.7 新增
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
核心方法不多,而且大部分都很简单,核心方法中有一段IntegerCache,这是第重点,是为了支持Java的autoboxing机制,提升自动装箱和拆箱的效率;
3.2.1 自动装箱和拆箱
Java的自动装箱和拆卸其实是一种语法糖,比如如下代码:
Integer x = 1;
// 编译后实际上的写法是:
Integer x = Integer.valuof(1);
3.2.2 == 比较的问题
对象的 == 判断是判断的引用,那么Integer的 == 也不例外,但是自动装箱可能破坏我们的认知,比如如下代码:
Integer x = 1;
System.out.println(x == 1); // case 1-0 : true 自动拆箱
System.out.println(1 == x); // case 1-1 : true 自动拆箱
Integer y = 1000;
System.out.println(y == 1000); // case 2 : true 自动拆箱
System.out.println(y == new Integer(1000)); // case : false 没有拆箱
根据源码可以分析出:
- X == 1 这个不管是拆箱还是装箱,== 都是true, 那么实际上是被装箱了还是拆箱了呢?
- y == new Integer(10000) ,因为y被自动装箱了,而且IntegerCache还没有缓存,所以肯定是否false;
- y == 1000 这个就比较疑惑了,如果对1000自动装箱,那么就是false,如果对y自动拆箱那么肯定就是true;
javap -c的反编译内容如下:
Code:
## 这三个指令就是Integer x = Integer.valueOf(1);
0: iconst_1
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
## 这几条指令就是System.out.println(x == 1);
## 很明显x == 1使用的是 x.intValue == 1
## 也就是自动拆箱了
5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
8: aload_1
9: invokevirtual #4 // Method java/lang/Integer.intValue:()I
12: iconst_1
13: if_icmpne 20
16: iconst_1
17: goto 21
20: iconst_0
21: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
## 这几条指令就是System.out.println(1 == x);
## 这里也证明了是自动拆箱了
24: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
27: iconst_1
28: aload_1
29: invokevirtual #4 // Method java/lang/Integer.intValue:()I
32: if_icmpne 39
35: iconst_1
36: goto 40
39: iconst_0
40: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
## 这里1000没有被预编译到常量池
## 先自动装箱 Integer y = Integer.valueOf(1000);
43: sipush 1000
46: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
49: astore_2
## 这里自动拆箱了,System.out.println(y.intValue == 1000);
50: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
53: aload_2
54: invokevirtual #4 // Method java/lang/Integer.intValue:()I
57: sipush 1000
60: if_icmpne 67
63: iconst_1
64: goto 68
67: iconst_0
68: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
## 这里编译时没有拆箱用比较的是integer的==
71: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
74: aload_2
75: new #6 // class java/lang/Integer
78: dup
79: sipush 1000
82: invokespecial #7 // Method java/lang/Integer."<init>":(I)V
85: if_acmpne 92
88: iconst_1
89: goto 93
92: iconst_0
93: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
96: return
NOTE : Java的编译器在Integer和int比较的时候使用的是拆箱机制,Integer的比较要使用equals不要使用 == ,虽然这在一定范围内可以被编译器优化为自动拆箱,并且因为缓存的原因有时是正确的,但是实际上很危险。
3.2.3 valueOf的使用
Integer在构造的时候推荐使用valueOf而不是new Integer,valueOf会首先判断是否需要缓存,也会造成如下的不同:
Integer x = 120;
Integer y = 120;
Integer x2 = 150;
Integer y2 = 150;
System.out.println(x == y);(case 1 : true)
System.out.println(x == new Integer(120)); (case 2 : false)
System.out.println(x == Integer.valueOf(120)); (case 2 : true)
System.out.println(x2 == y2) ; // case 4 : false