int 是 Java 八大原始類型之一,是 Java 語言中為數不多不是對象的東西,Integer 是 int 的包裝類,里面使用了一個 int 類型的變量來存儲數據,提供了一些整數之間的常用操作,常規性的介紹就這么一點,程序員不喜歡說,程序員就喜歡源碼,我們還是來看源碼吧
* @author Lee Boynton
* @author Arthur van Hoff
* @author Josh Bloch
* @author Joseph D. Darcy
* @since JDK1.0
*/
public final class Integer extends Number implements Comparable<Integer> {
/**
* A constant holding the minimum value an {@code int} can
* have, -2<sup>31</sup>.
*/
@Native public static final int MIN_VALUE = 0x80000000;
/**
* A constant holding the maximum value an {@code int} can
* have, 2<sup>31</sup>-1.
*/
@Native public static final int MAX_VALUE = 0x7fffffff;
/**
* The value of the {@code Integer}.
*
* @serial
*/
private final int value;
/**
* Constructs a newly allocated {@code Integer} object that
* represents the specified {@code int} value.
*
* @param value the value to be represented by the
* {@code Integer} object.
*/
public Integer(int value) {
this.value = value;
}
/**
* Constructs a newly allocated {@code Integer} object that
* represents the {@code int} value indicated by the
* {@code String} parameter. The string is converted to an
* {@code int} value in exactly the manner used by the
* {@code parseInt} method for radix 10.
*
* @param s the {@code String} to be converted to an
* {@code Integer}.
* @exception NumberFormatException if the {@code String} does not
* contain a parsable integer.
* @see java.lang.Integer#parseInt(java.lang.String, int)
*/
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
上面這段源碼是我截取出來的,在 Integer 類中,這些代碼不是連在一起的,把他們放在一起,那是因為我想說明點事情,我們仔細看看這段代碼,Integer 類是被 final ,這說明了什么?用於存放變量的 value 也被 private final 修飾,這又說明了什么?看着這些是不是有點熟悉呢? 沒錯,String 對象也是這樣的,這說明 Integer 對象也是不可變的,所以以后如果被問到 Integer 對象是不是不可變對象時,記得回答是喔。為什么 Integer 對象也會設計成不可變對象呢?其實我也不知道,我沒有從文檔中找到答案,但是在楊曉峰老師的文章中看到過有關說明,楊曉峰老師說 Integer 類設計成不可變跟 getInteger() 方法有關系,getInteger()方法的源碼如下:
public static Integer getInteger(String nm, Integer val) {
String v = null;
try {
v = System.getProperty(nm);
} catch (IllegalArgumentException | NullPointerException e) {
}
if (v != null) {
try {
return Integer.decode(v);
} catch (NumberFormatException e) {
}
}
return val;
}
getInteger() 方法是用來獲取系統屬性的,我們通過屬性來設置服務器的某個服務器的端口,如果 Integer 可變的話,那么我們就能夠輕易的改變這個屬性的值,這會使得我們的產品存在安全風險。
上面我們我么簡單的聊了一下 Integer 類的實現,聊到 int 與 Integer,自然就少不了自動裝箱和自動拆箱。
1、自動裝箱、拆箱
自動裝箱和拆箱是從 JDK 1.5 開始引進的功能,它是一種語法糖,Java 可以根據上下文,自動的在原始類型和包裝類型中進行轉換,簡單的來說就是 Java 平台保證了不同的寫法通過編譯之后會產生相同的字節碼,保證了運行時是等價的。自動裝箱和拆箱極大地簡化了相關編程。
自動裝箱:將原始類型轉化為包裝類型的過程
比如將 int 類型轉換成 integer 類型,這就是原始類型到包裝類型的轉變,在編譯的時候,編譯器就會幫我們做自動轉換,這個過程對我們程序員來說是無感知的,例如這段給 Integer 對象賦值的代碼:
Integer x = 3000;
這段代碼經過編譯器之后,會轉換成下面這段代碼:
Integer x = Integer.valueOf(3000);
這就是自動裝箱過程,這個過程你是不知道的,所以它才叫自動裝箱,在自動裝箱的過程中使用到了 valueOf() 方法,來看看 JDK 中這個方法的源碼:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
這個方法里,前面先進行了一個緩存判斷,如果你不知道的話,先忽略掉它,最后返回了 new Integer(i) 對象引用,這個方法就是幫你去調用了 Integer 類的構造器。這就是自動裝箱。
自動拆箱:將包裝類型轉換成原始類型的過程
將 Integer 類型轉換為 Int 類型,這是一個包裝類型轉成成原始類型的過程,在這個過程中就會涉及到自動拆箱。來看看這段代碼(這是一段很操蛋的代碼,實際中應該沒人這樣寫):
Integer mm = 1000;
int mmm = mm;
在編譯的時候,這段代碼會被編譯器編譯成下面這段代碼:
Integer mm = Integer.valueOf(1000);
int mmm = mm.intValue();
主要看int mmm = mm.intValue();
這行代碼,這行代碼跟我們寫的不一樣了,使用到了一個 intValue() 方法,來看看 Integer 類中 intValue() 方法的源碼:
/**
* Returns the value of this {@code Integer} as an
* {@code int}.
*/
public int intValue() {
return value;
}
這個方法的作用就是把 Integer 對象中用來存儲值的 value 變量返回了,這就是自動拆箱,好了,關於自動裝箱和自動拆箱我們都了解了,還記得自動裝箱過程中涉及到的緩存嗎?接下來我們一起了解一下。
2、Integer 緩存策略
在自動裝箱的 valueOf() 方法中,我們看到了有一個緩存判斷的操作,是的,Integer 類中有緩存池,會將使用頻繁的值緩存起來,以便提高系統的使用性能,在自動裝箱的過程中,會先判斷該值是否存在緩存池中,如果存在直接從緩存池中取出引用返回,如果不存在則調用構造函數構造對象。緩存是自動裝箱操作獨享的,直接通過構造函數構造出來的 Integer 對象即使值在緩存范圍內,也不會使用到緩存池。在 Integer 類中,使用了一個內部類來實現緩存,這個內部類叫做 IntegerCache,IntegerCache 類的源代碼如下:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
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;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
從源碼和 Java 注釋中我們可以看出 IntegerCache 的緩存默認值范圍 -128 ~ 127 。但是我們也可以在啟動時通過 JVM 命令來設置緩存范圍的最大值,只需要在啟動時添加 -XX:AutoBoxCacheMax=
參數就可以了,但是記得這個 size 可不要亂設置,需要全方位考慮,比如你設置成 10 萬,那么這 10 萬個數都會在啟動剛啟動時就添加到內存中,想想這會占用你多少內存?這樣做就得不償失了,Java 公司設置成 -128 ~ 127 是有道理的,發現大部分人使用的值都在 -128 ~ 127 之間,這些值占用的內存比較少,性能上比通過構造函數構造對象要好不少。如何你使用的 Integer 的值在緩存范圍的話,就用 Integer i = value 的形式構建對象,如果你的值不在緩存范圍內,則使用 Integer i = new Integer(value) 的形式構建 Integer 對象,避免自動裝箱的過程。最后我們來看一下 Integer 對象比較常用的方法 parseInt 方法
3、parseInt() 方法
parseInt() 方法的作用是用來將整數型的字符串轉換成整數,parseInt 方法需要和 valueOf 方法區分開來,有不少人會問這兩方法有什么區別,最后度會返回 int 類型,都能將整數型的字符串轉換成整數型,比如這段代碼
System.out.println(Integer.parseInt("+12"));
System.out.println(Integer.valueOf("+12"));
最后都會輸出 12 ,輸出的結果相同是因為 valueOf 方法使用中會調用 parseInt 方法將整數型字符轉換為整數,並且會在內存中創建一個值為 12 的 Integer 對象,然后返回這個對象引用。而 parseInt 方法只會幫你將整數型字符轉換為整數,不會額外的創建對象。所以它們兩得到相同的結果純屬是巧合。一起瞅瞅 parseInt 源代碼:
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;
}
在調用 parseInt 方法時,我們可以傳入一個 radix 變量,用來告訴它使用什么進制來進行轉換,默認使用的是十進制。
文章不足之處,望大家多多指點,共同學習,共同進步
最后
打個小廣告,歡迎掃碼關注微信公眾號:「平頭哥的技術博文」,一起進步吧。