Jdk源码(一)Integer

摘要:略

类关系图

一些重要点

一些字段

一些方法

类关系图


IntegerNumber家族中的一个类,另外还实现了Comparable接口,用于比较大小。

1
2
3
4
5
6
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

一些重要点

自动拆箱与装箱

  在jdk1.5之后,引入了自动装箱与拆箱的机制,能自动的将原始类型和其相应的对象类型相互转换。
  例如,在1.5之前,我们必须要这样写

1
2
3
4
5
6
Integer a = valueOf(3);   
Double b = (4);
int c = intValue(b);
````

&emsp;&emsp;例如,下面写法在1.5之后都是正确的

Integer a = 3; //autobox
Double b = 4; //autobox
int c = b; //auto unboxing

1
2
3
4
5
6
7
8
9
10
<hr>

<h4 id="2.2">-128~127的缓存</h4>

&emsp;&emsp;对于以下代码,初学者都应该知道,打印结果是false和true
```java
Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a == b); /** false **/
System.out.println(a.equals(b)); /** true **/

  因为这是两个不一样的对象,虽然值相同,但她们的地址是不同的,所以 == 为false,equeal为真。

  但下面的例子,打印结果却为都为true:

1
2
3
4
Integer a = 1; 
Integer b = 1;
System.out.println(a == b); /** true **/
System.out.println(a.equals(b)); /** true **/

因为这里产生了自动装箱的操作,需要从int包装为Integer,装箱操作会调用valueOf方法:

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

  首先会对 i 进行一个比较,如果在某个范围内,返回数组中的一个对象,否则直接new一个对象。那么,查看一下IntegerCache类,这是一个静态内部类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private static class IntegerCache {
//定义了最小值为 -128
static final int low = -128;
static final int high;
//对象的缓存数组
static final Integer cache[];
//定义默认值为 127,但可以在启动参数中设置最大值
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;
//new 一个缓存数组,并且初始化
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);

//[-128,127]这个范围必须包括在内
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}

private IntegerCache() {}
}

  这个类中有三个成员变量,low、high和cache[],cache就是缓存所用到的数组,low定义了最小缓存的值,默认-128,high定义了最大缓存值,默认127。类中有一个静态代码块,主要是获取缓存的最大值(如果你有通过属性设置),之后呢,对cache数组进行初始化,任务就完成了。
  缓存起一部分对象最大的好处就是能够节省内存,选择-128~127这个范围也是这个区间的发生装箱拆箱的频率最高。而且,
大量的无用对象也会给GC造成不小的压力。

  而我们直接new一个Integer对象的话,调用了构造函数,是不可能使用缓存的,Integer包装类为我们维护了一个int类型的变量,仅此而已。

1
2
3
4
5
6
7
8
9
10
//这是Integer对象中维护的值,被final修饰,不可更改
private final int value;
//构造函数
public Integer(int value) {
this.value = value;
}
//也可以传一个字符串进行转换
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}

一些字段

MIN_VALUE

1
2
3
4
5
6
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/

@Native public static final int MIN_VALUE = 0x80000000;

java中,int是32位的,MIN_VALUE 是int类型能表示的最小值,-2147483648,0x8000000是补码的表示形式。


MAX_VALUE

1
2
3
4
5
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
@Native public static final int MAX_VALUE = 0x7fffffff;

MAX_VALUE 是int所能表示的最大的数,2147483647。


TYPE

1
2
3
4
5
6
7
8
     /**
* The {@code Class} instance representing the primitive type
* {@code int}.
*
* @since JDK1.1
*/
@SuppressWarnings("unchecked")
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");

注意:
  Integer.TYPE == int.class
  Integer.TYPE != Integer.class


digits

1
2
3
4
5
6
7
8
9
10
11
/**
* All possible chars for representing a number as a String
*/
final static char[] digits = {
'0' , '1' , '2' , '3' , '4' , '5' ,
'6' , '7' , '8' , '9' , 'a' , 'b' ,
'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
'o' , 'p' , 'q' , 'r' , 's' , 't' ,
'u' , 'v' , 'w' , 'x' , 'y' , 'z'
};

  Integer类中,String toString(int i, int radix)方法可以将数字转成转成2至32进制中任意一个进制,返回结果就是String,这个digits数组,就是用来存储表示各个进制的的字符。


一些方法

static int highestOneBit(int i)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Returns an {@code int} value with at most a single one-bit, in the
* position of the highest-order ("leftmost") one-bit in the specified
* {@code int} value. Returns zero if the specified value has no
* one-bits in its two's complement binary representation, that is, if it
* is equal to zero.
*
* @param i the value whose highest one bit is to be computed
* @return an {@code int} value with a single one-bit, in the position
* of the highest-order one-bit in the specified value, or zero if
* the specified value is itself equal to zero.
* @since 1.5
*/
public static int highestOneBit(int i) {
// HD, Figure 3-1
i |= (i >> 1);
i |= (i >> 2);
i |= (i >> 4);
i |= (i >> 8);
i |= (i >> 16);
return i - (i >>> 1);
}

  读一下注释,可以理解,这个方法返回的是指定的某个数,从左到右开始(leftmost),第一个”1”后面的位数全部补0,返回其int结果。
  举个栗子,这个函数的 i 传入 88888888,因为java 中 int 是32位的,她的值用二进制表示是:0000,0101,0100,1100,0101,0110,0011,1000,最左边的“1”右边全部清零,就变成了0000,0100,0000,0000,0000,0000,0000,0000,其int结果是2^26,等于67108864。

代码解读

函数只有简短五行代码(不算注释)。

  1. 把 i 右移一位,并与原数据按位取或,这样就使最高位1和她的下一位都变成了1;
  2. 把刚刚得到的两个1再右移两位,并与原数据按位取或,这样刚刚连续的两个1,就变成四个1了;
  3. 把刚刚的四个1右移四位,并与原数据按位取或,这样就得到八个连续的1了;
  4. 省略两步。结果是右移完16位后,最高位1和她右边的都是1;
  5. 最后 i 减去 i无符号右移1位的值,就得到结果。
    因为最后一步是无符号右移,所以参数是负数得到的结果不是0,永远是0x80000000,也就是int的最小值

static int lowestOneBit(int i)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Returns an {@code int} value with at most a single one-bit, in the
* position of the lowest-order ("rightmost") one-bit in the specified
* {@code int} value. Returns zero if the specified value has no
* one-bits in its two's complement binary representation, that is, if it
* is equal to zero.
*
* @param i the value whose lowest one bit is to be computed
* @return an {@code int} value with a single one-bit, in the position
* of the lowest-order one-bit in the specified value, or zero if
* the specified value is itself equal to zero.
* @since 1.5
*/
public static int lowestOneBit(int i) {
// HD, Section 2-1
return i & -i;
}

和上面的方法很相似,这个方法返回的是指定的某个数,从右到左开始(rightmost),第一个”1”后面的位数全部补0,返回其int结果。我们依旧把 88888888 传入这个方法,那么得到的结果是 8。

算法很简单,将其与其的负数相与即可。因为这两个数有一个是负数,负数的补码是原码取反再加一,这就导致了最右边的1 位置是相同的。


static String toString(int i, int radix)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static String toString(int i, int radix) {
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
radix = 10;

/* Use the faster version */
if (radix == 10) {
return toString(i);
}

char buf[] = new char[33];
boolean negative = (i < 0);
int charPos = 32;

if (!negative) {
i = -i;
}

while (i <= -radix) {
buf[charPos--] = digits[-(i % radix)];
i = i / radix;
}
buf[charPos] = digits[-i];

if (negative) {
buf[--charPos] = '-';
}

return new String(buf, charPos, (33 - charPos));
}

  这个方法注释有点长,没贴,功能是将数字,转成某种进制的字符串。参数 i 是需要转的数字,参数 radix 是进制数。
  方法首先检查了radix的范围,因为10个数字,加26个字母,最多只能展示到36进制,最低当然是2进制,参数不处于这个范围的,用10进制代替,如果进制是10,那直接返回 tostring(int i)
  int是32位的,再加上一个符号位,所以要33位的数组
  之后的操作是将正数转成负数再进行计算,因为负数的最小值是-2147483648,正数最大值是2147483647,负数转正数的话可能会产生溢出。
  再之后就是比较平常的进制转换算法了。