这是《水煮 JDK 源码》系列 的第8篇文章,计划撰写100篇关于JDK源码相关的文章
在 Java 语言中,默认定义的整数类型为 int
类型,默认定义的浮点类型为 double
类型,如果定义的数值在基本类型所允许的范围内,那么在定义时就不需要显式申明,直接定义即可,如下所示:
// 下面的定义都是合法的
int intNum = 2;
// byte 的范围为 -128 到 127,所以 2 在范围内
byte byteNum = 2;
// short 的范围为 -32768 到 32767,所以 2 在范围内
short shortNum = 2;
// long 的范围为 -2的63次方 到 2的63次方-1
long longNum = 2;
// float 的范围为 1.4e-45f 到 3.4028235e+38f
float floatNum = 2;
// double 的范围为 4.9e-324 到 1.7976931348623157e+308
double doubleNum = 2;
// 下面的定义是不合法的,数值都超过了类型所允许的最大值
// 这种情况需要选择合适的数据类型
byte byteNum2 = 200;
byte shortNum2 = 40000;
// 该定义也是不合法的,默认的浮点类型为 double
float floatNum2 = 2.0;
虽然基本数据类型 byte
、short
、int
、long
、float
、double
经常使用,但是我们平时可能并不会过多关注其相应的包装类 Byte
、Short
、Int
、Long
、Float
、Double
,这里以 Integer
类为例,分析其具体源码的实现。
Integer
类位于 java.lang
下,其继承于 Number
类,实现了 Comparable
比较接口,其定义如下:
public final class Integer extends Number implements Comparable<Integer>
关于父类 Number
类,已经在之前的文章中分析过了,可以查看我的另外一篇文章:
对于 Comparable
比较接口,其定义也是很简单的,只有一个 compareTo()
方法,如下:
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
整个 Integer
类的源码还是比较多的,加上注释的话,接近 1600 行代码了,在分析源码的时候,按照以下的分类进行:
- 成员变量
- 构造函数
- 内部缓存类
- 方法
1、成员变量
Integer
类中定义了一个 value
成员变量,用于表示 Integer
对应的 int
值,如下:
// Integer 对象对应的 int 值
private final int value;
2、构造函数
Integer
类提供了2个构造函数,其定义如下:
public Integer(int value) {
this.value = value;
}
public Integer(String s) throws NumberFormatException {
// 调用 parseInt 方法,默认基数为 10,将字符串解析成 int
this.value = parseInt(s, 10);
}
从上面可以看出,既可以从 int
类型构建 Integer
,也可以从 String
类型构建,在实际的开发过程中,使用构造函数来创建 Integer
其实很少用到,因为 Integer
类和其他的普通类不一样,它对应于基础数据类型 int
,而自从 JDK 1.5
提供的自动装箱和拆箱操作,可以使用一个数值来创建 Integer
对象;如果是对于 String
类型,想要转换为 Integer
类型,实际中更多直接使用 Integer.parseInt()
方法进行,如下:
// 自动装箱
Integer num = 1;
// Integer.parseInt() 方法首先返回的是 int 类型,然后会涉及自动装箱
Integer strNum = Integer.parseInt("1");
Java 的自动装箱和拆箱操作是编译器自动完成的,装箱的时候调用的是包装类的 valueOf()
方法,上面代码定义的 num
和 strNum
对象其实是相同的,至于为什么会相同,在下文分析 valueOf()
方法时会作出具体的解释。
3、内部缓存类
在 Integer
类的内部,定义了缓存类 IntegerCache
,该类是一个静态类,其定义如下:
private static class IntegerCache {
// 最小值 -128
static final int low = -128;
// 最大值
static final int high;
// 缓存数组
static final Integer cache[];
static {
// 最大值默认为 127,也可以通过属性字段配置,具体属性名为 java.lang.Integer.IntegerCache.high
int h = 127;
// 获取 java.lang.Integer.IntegerCache.high 属性值
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
// 如果属性值不为空
if (integerCacheHighPropValue != null) {
try {
// 使用 parseInt() 方法将属性值转换为 int 类型
int i = parseInt(integerCacheHighPropValue);
// 取属性值与127两者之间的最大值,也就是说 i 的最小值其实是127
i = Math.max(i, 127);
// 数组的最大值为 Integer.MAX_VALUE
// 取 i 和 Integer.MAX_VALUE - (-low) -1 两者之间最小值
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;
// 创建大小为 (high - low) + 1 的整型数组
cache = new Integer[(high - low) + 1];
int j = low;
// 创建并缓存 Integer 对象
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
// 断言缓存最大值 high 大于等于 127
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
从 IntegerCache
的定义可以看出,3个成员变量都是静态常量,只有一个静态代码块,没有任何其他的方法,关于静态代码块的分析已经在代码中备注了,从静态代码块中可以得出以下的几点:
IntegerCache
的缓存最小值low
为 -128,与其他的类Byte
、Short
、Long
是相同的;IntegerCache
的缓存最大值high
可以通过属性java.lang.Integer.IntegerCache.high
进行设置,而其他的类Byte
、Short
、Long
都是无法设置的;IntegerCache
的缓存最大值high
的最小值为127,如果通过属性设置的值比127小,则赋值为 127,也就是此时的属性值是无效的;
关于 Byte
、Short
、Integer
、Long
的内部缓存类详细的对比与分析,可以参考我的上一篇源码分析文章:
4、方法
Integer
类提供的方法较多,大部分都是静态方法,主要分为以下的几类:
toString()
方法:主要是将Integer
转换为字符串,这类方法中还有toUnsignedString()
、toHexString()
等等;parseInt()
方法:这个方法和toString()
方法相反,主要是将String
类型转换为int
类型;valueOf()
方法:主要是通过int
或String
类型创建Integer
对象;getInteger()
方法:主要用于获取指定名称的系统属性的整数值;compare()
方法:用于比较两个整型数值是否相等;- 位操作方法
- 其他方法
4.1 toString() 方法
public String toString() {
return toString(value);
}
public static String toString(int i) {
// 如果为 int 类型最小值
if (i == Integer.MIN_VALUE)
return "-2147483648";
// 获取 int 数值字符长度
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
getChars(i, size, buf);
return new String(buf, true);
}
public static String toString(int i, int radix) {
// MIN_RADIX 为 2,MAX_RADIX 为36,如果不在范围内,默认为 10
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));
}
/** 转换为指定进制的无符号字符串 */
public static String toUnsignedString(int i, int radix) {
return Long.toUnsignedString(toUnsignedLong(i), radix);
}
/** 转换为十六进制 */
public static String toHexString(int i) {
return toUnsignedString0(i, 4);
}
/** 转换为八进制 */
public static String toOctalString(int i) {
return toUnsignedString0(i, 3);
}
/** 转换为二进制 */
public static String toBinaryString(int i) {
return toUnsignedString0(i, 1);
}
/** 转换为无符号字符串,int 类型转换为无符号可能越界,因此使用 long 类型 */
public static String toUnsignedString(int i) {
return Long.toString(toUnsignedLong(i));
}
private static String toUnsignedString0(int val, int shift) {
// assert shift > 0 && shift <=5 : "Illegal shift value";
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
formatUnsignedInt(val, shift, buf, 0, chars);
// Use special constructor which takes over "buf".
return new String(buf, true);
}
toString()
方法又细分了很多方法,比如转换为二进制、八进制、十六进制、无符号字符串等方法,利用 toString()
方法可以将 int
整型转换为二进制、八进制、十六进制,如下:
public static void main(String[] args) {
// 由于最小的 MIN_RADIX 为 2,而传入的 1 小于 2,所以默认为 10进制
System.out.println(Integer.toString(10, 1));
// 十进制
System.out.println(Integer.toString(10, 10));
// 二进制
System.out.println(Integer.toBinaryString(10));
// 八进制
System.out.println(Integer.toOctalString(10));
// 十六进制
System.out.println(Integer.toHexString(10));
}
输出结果如下:
10
10
1010
12
a
4.2 parseInt() 方法
public static int parseInt(String s) throws NumberFormatException {
// 默认转换为 10 进制数值,这个方法在实际中应用最多
return parseInt(s,10);
}
public static int parseInt(String s, int radix)
throws NumberFormatException
{
/*
* WARNING: This method may be invoked early during VM initialization
* before IntegerCache is initialized. Care must be taken to not use
* the valueOf method.
*/
if (s == null) {
throw new NumberFormatException("null");
}
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}
int result = 0;
boolean negative = false;
int i = 0, len = s.length();
int limit = -Integer.MAX_VALUE;
int multmin;
int digit;
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;
}
public static int parseUnsignedInt(String s) throws NumberFormatException {
// 转换为无符号的十进制数值
return parseUnsignedInt(s, 10);
}
public static int parseUnsignedInt(String s, int radix)
throws NumberFormatException {
if (s == null) {
throw new NumberFormatException("null");
}
int len = s.length();
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar == '-') {
throw new
NumberFormatException(String.format("Illegal leading minus sign " +
"on unsigned string %s.", s));
} else {
if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits
(radix == 10 && len <= 9) ) { // Integer.MAX_VALUE in base 10 is 10 digits
return parseInt(s, radix);
} else {
long ell = Long.parseLong(s, radix);
if ((ell & 0xffff_ffff_0000_0000L) == 0) {
return (int) ell;
} else {
throw new
NumberFormatException(String.format("String value %s exceeds " +
"range of unsigned int.", s));
}
}
}
} else {
throw NumberFormatException.forInputString(s);
}
}
4.3 valueOf() 方法
valueOf()
方法主要是通过 int
或 String
类型创建 Integer
对象,其源码如下:
public static Integer valueOf(int i) {
// 如果数值 i 在缓存的区间之内,则直接从缓存复用 Integer 对象
// 自动装箱的时候会调用此方法
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
public static Integer valueOf(String s) throws NumberFormatException {
// 先通过 parserInt() 方法将字符串转为 int,默认为十进制
return Integer.valueOf(parseInt(s, 10));
}
public static Integer valueOf(String s, int radix) throws NumberFormatException {
// 先通过 parserInt() 方法将字符串转为指定进制的 int
return Integer.valueOf(parseInt(s,radix));
}
int
与 Integer
之间涉及到自动装箱和拆箱操作,自动装箱操作调用的是 valueOf()
方法,如果数值在缓存区间的最小值与最大值之间时,会复用缓存中的对象,默认缓存区间为 [-128, 127]
。
4.4 getInteger() 方法
getInteger()
方法主要用于获取指定名称的系统属性的整数值,相关源码如下:
public static Integer getInteger(String nm) {
return getInteger(nm, null);
}
public static Integer getInteger(String nm, int val) {
Integer result = getInteger(nm, null);
// 如果属性值不存在,则返回默认值 val
return (result == null) ? Integer.valueOf(val) : result;
}
public static Integer getInteger(String nm, Integer val) {
String v = null;
try {
// 获取属性名为 nm 的系统属性
v = System.getProperty(nm);
} catch (IllegalArgumentException | NullPointerException e) {
}
if (v != null) {
try {
// 调用 decode 方法将属性值转换为 Integer
return Integer.decode(v);
} catch (NumberFormatException e) {
}
}
return val;
}
关于 System.getProperty()
获取系统属性的更多应用,可以参考我写的另一篇文章:
4.5 compare()`方法
compare()
方法用于比较两个整型数值是否相等,其源码如下:
public int compareTo(Integer anotherInteger) {
// 这里比较的是数值
return compare(this.value, anotherInteger.value);
}
public static int compare(int x, int y) {
// 如果 x 小于 y,则返回 -1
// 如果 x 等于 y,则返回 0
// 如果 x 大于 y,则返回 1
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
public static int compareUnsigned(int x, int y) {
// 无符号数值比较
return compare(x + MIN_VALUE, y + MIN_VALUE);
}
public boolean equals(Object obj) {
if (obj instanceof Integer) {
// 比较数值是否相等
return value == ((Integer)obj).intValue();
}
return false;
}
对于 compare()
比较方法,返回的结果有 -1、0、1
三种,如果不需要返回 boolean
类型,可以使用这个方法,对于 Integer
包装类的比较,建议使用 equals()
方法。
4.6 位操作方法
Integer
类中提供了很多位操作的方法,主要有以下的这些:
highestOneBit()
:返回给定数值二进制最高位1的权值;lowestOneBit()
:返回给定数值二进制最低位1的权值;numberOfLeadingZeros()
:返回给定数值二进制中从最左边算起 0 的个数;numberOfTrailingZeros()
:返回给定数值二进制中从最右边算起 0 的个数;bitCount()
:返回给定数值的二进制中 1 的个数;rotateLeft()
:将给定整数值的二进制补码数左旋转给定位数;rotateRight()
:将给定整数值的二进制补码数右旋转给定位数;reverse()
:将给定整数值的二进制补码数进行反转;signum()
:返回给定 int 值的 sign 函数;reverseBytes()
:将给定整数的二进制补码按照字节进行反转;
4.7 其他方法
sum()
:获取两个整数的和;max()
:获取两个整数之间的最大值,自JDK 1.8
版本新增的,具体调用的是Math.max()
方法;min()
:获取两个整数之间的最小值,自JDK 1.8
版本新增的,具体调用的是Math.min()
方法;