這是《水煮 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
類,已經在之前的文章中分析過了,可以檢視我的另外一篇文章:
- 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
為 -128,與其他的類low
、Byte
、Short
是相同的;Long
-
的緩存最大值IntegerCache
可以通過屬性high
進行設定,而其他的類java.lang.Integer.IntegerCache.high
、Byte
、Short
都是無法設定的;Long
-
的緩存最大值IntegerCache
的最小值為127,如果通過屬性設定的值比127小,則指派為 127,也就是此時的屬性值是無效的;high
關于
Byte
、
Short
、
Integer
、
Long
的内部緩存類詳細的對比與分析,可以參考我的上一篇源碼分析文章:
- 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()
擷取系統屬性的更多應用,可以參考我寫的另一篇文章:
- Java代碼判斷目前作業系統是Windows或Linux或MacOS
4.5 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;
}
4.6 位操作方法
-
:傳回給定數值二進制最高位1的權值;highestOneBit()
-
:傳回給定數值二進制最低位1的權值;lowestOneBit()
-
:傳回給定數值二進制中從最左邊算起 0 的個數;numberOfLeadingZeros()
-
:傳回給定數值二進制中從最右邊算起 0 的個數;numberOfTrailingZeros()
-
:傳回給定數值的二進制中 1 的個數;bitCount()
-
:将給定整數值的二進制補碼數左旋轉給定位數;rotateLeft()
-
:将給定整數值的二進制補碼數右旋轉給定位數;rotateRight()
-
:将給定整數值的二進制補碼數進行反轉;reverse()
-
:傳回給定 int 值的 sign 函數;signum()
-
:将給定整數的二進制補碼按照位元組進行反轉;reverseBytes()
4.7 其他方法
-
:擷取兩個整數的和;sum()
-
:擷取兩個整數之間的最大值,自max()
版本新增的,具體調用的是JDK 1.8
方法;Math.max()
-
:擷取兩個整數之間的最小值,自min()
版本新增的,具體調用的是JDK 1.8
方法;Math.min()