Integer.valueof()和Integer.parseInt()的區別

Integer. valueOf()可以將基本類型int轉換爲包裝類型Integer,或者將String轉換成Integer,String如果爲Null或“”都會報錯。

Integer. valueOf()是高效的

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
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() {}
    }

從他的實現方法可以看出他int在[-128,127]之間的時候他會直接拿緩存,而不會new Integer(),Integer.valueOf()方法基於減少對象創建次數和節省內存的考慮,緩存了[-128,127]之間的數字。此數字範圍內傳參則直接返回緩存中的對象。在此之外,直接new出來。
 

下面我們來看一道選擇題,就是關於Integer.valueOf()的知識,題目如下: 

這裏寫圖片描述

A.System.out.println(i01== i02); 
B.System.out.println(i01== i03); 
C.System.out.println(i03== i04); 
D.System.out.println(i02== i04);


答案呢,我也做對了,但是也是靠排除法做對的,至於這道題考察的具體細節問題,我當時沒考慮,不過等我查看了Integer的相關源碼時,茅舍頓開。答案這裏我這裏先不公佈,我們慢慢開始分析。

分析

選項A

  選項A中比較的是i01和i02,Integer i01=59這裏涉及到自動裝箱過程,59是整型常量,經包裝使其產生一個引用並存在棧中指向這個整型常量所佔的內存,這時i01就是Integer 的引用。 
  而int i02=59由於int是基本類型,所以不存在引用問題,直接由編譯器將其存放在棧中,換一句話說,i02本身就是59。那麼System.out.println(i01== i02)結果任何呢?這裏涉及到了拆箱的過程,因爲等號一邊存在基本類型所以編譯器後會把另一邊的Integer對象拆箱成int型,這時等號兩邊比較的就是數值大小,所以是true。

好了,到了這裏,你有沒有想到這樣一個問題:如果是Integer i01=59;Integer i02=59;然後System.out.println(i01== i02)的結果是?可能你會說比較數值大小所以相等啊,也有可能說等號兩邊對象引用,所以比較的是引用,又因爲開闢了不同的內存空間,所以引用不同所以返回false。可是正確答案是:true. 
再來看這個問題::如果是Integer i01=300;Integer i02=300;然後System.out.println(i01== i02)的結果是? 你可能說上面你不是說了true嘛,怎麼還問這樣的問題,可是這次的答案是false。你是否會吃驚?大牛除外,我是小白,求不打臉!
  

解析:當靠想象無法解決問題的時候,這是就要看源代碼了!!很重要!我們可以在Integer類中找到這樣的嵌套內部類IntegerCache,這個類就是在Integer類裝入內存中時,會執行其內部類中靜態代碼塊進行其初始化工作,做的主要工作就是把一字節的整型數據(-128-127)裝包成Integer類並把其對應的引用存入到cache數組中,這樣在方法區中開闢空間存放這些靜態Integer變量,同時靜態cache數組也存放在這裏,供線程享用,這也稱靜態緩存。 
  所以當用Integer 聲明初始化變量時,會先判斷所賦值的大小是否在-128到127之間,若在,則利用靜態緩存中的空間並且返回對應cache數組中對應引用,存放到運行棧中,而不再重新開闢內存。 
  所以對於Integer i01=59;Integer i02=59;**i01 和 i02是引用並且相等都指向緩存中的數據,所以返回true。而對於**Integer i01=300;Integer i02=300;因爲其數據大於127,所以虛擬機會在堆中重新new (開闢新空間)一個 Integer 對象存放300,創建2個對象就會產生2個這樣的空間,空間的地址肯定不同導致返回到棧中的引用的只不同。所以System.out.println打印出false。

補充:爲什麼1個字節的數據範圍是-128到127呢,因爲Java中數據的表示都是帶符號數,所以最高位是用來表示數據的正負,0表示正數,1表示負數,所以正數最大的情況對應的二進制數爲:01111111,負數最小對應的二進制數爲:10000000.

B選項

  從上面的分析,我們已經知道Integer i01=59返回的是指向緩存數據的引用。那麼Integer.valueOf(59)返回的是什麼或者操作是什麼呢? 
  這個函數的功能就是把int 型轉換成Integer,簡單說就是裝包,那他是新創建一個對象嗎?還是像之前利用緩存的呢?有了之前的經驗,肯定想到的是利用緩存,這樣做既提高程序速度,又節約內存,何樂而不爲? 
來看一下源代碼:

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

很明顯跟之前的思想一致,若在-128到127範圍,直接返回該對象的引用,否則在堆中重新new 一個。 
到這,System.out.println(i01== i03)的結果毋庸置疑就是true.

選項C

  Integer.valueOf(59)返回的是已緩存的對象的引用,而Integer i04 = new Integer(59)是在堆中新開闢的空間,所以二者的引用的值必然不同,返回false,這道題呢就選C

選項D

  System.out.println(i02== i04) i02是整型變量,i04是引用,這裏又用到了解包,虛擬機會把i04指向的數據拆箱爲整型變量再與之比較,所以比較的是數值,59==59,返回true.


出一道題:

System.out.println(Integer.valueOf("127")==Integer.valueOf("127"));
System.out.println(Integer.valueOf("128")==Integer.valueOf("128"));
System.out.println(Integer.parseInt("128")==Integer.valueOf("128"));

輸出是:

true

false

true

 回答#1:

Integer.valueOf(String)確有一個不同尋常的行爲。

valueOf會返回一個Integer(整型)對象,當被處理的字符串在-128和127(包含邊界)之間時,返回的對象是預先緩存的。這就是爲什麼第一行的調用會返回true-127這個整型對象是被緩存的(所以兩次valueOf返回的是同一個對象)——第二行的調用返回false是因爲128沒有被緩存,所以每次調用,都會生成一個新的整型對象,因此兩個128整型對象是不同的對象。

重要的是你要知道在上面的比較中,你實際進行比較的是integer.valueOf返回的對象引用,所以當你比較緩存外的整型對象時,相等的判斷不會返回true,就算你傳個valueOf的值是相等的也沒用。(就像第二行中Integer.valueOf(128)==Integer.valueOf(128))。想讓這個判斷返回true,你需要使用equals()方法。

parseInt()返回的不是整型對象,而是一個int型基礎元素。這就是爲什麼最後一個判斷會返回true,第三行的判斷中,在判斷相等時,實際比較的是128 == 128,所以它必然是相等的。

再來說說第三種比較中的一點區別,使得它的結果與第二種比較不一樣了:

一個unboxing conversion(一種比較時的轉換,把對對象的引用轉換爲其對應的原子類型)在第三行的比較中發生了。因爲比較操作符使用了==同時等號的兩邊存在一個int型和一個Integer對象的引用。這樣的話,等號右邊返回的Integer對象被進一步轉換成了int數值,才與左邊進行相等判斷。

所以在轉換完成後,你實際比較的是兩個原子整型數值。這種轉換正是你在比較兩個原子類型時所期待看到的那樣,所以你最終比較了128等於128。

回答#2:

Integer類有一個靜態緩存,存儲了256個特殊的Integer對象——每個對象分別對應-128 和127之間的一個值。

有了這個概念,就可以知道上面三行代碼之間的區別。

new Integer(123);

顯示創建了一個新的Integer對象。

Integer.parseInt("123");

解析完字符串後返回一個int值。

Integer.valueOf("123");

這種情況比其他的要更復雜一些。首先進行了字符串解析,然後如果解析的值位於-128和127之間,就會從靜態緩存中返回對象。如果超出了這個範圍,就會調用Integer()方法並將解析的值作爲參數傳入,得到一個新的對象。

現在,讓我們看一下問題中的3個表達式。

Integer.valueOf("127")==Integer.valueOf("127");

上面的表達式返回true,因爲Integer的值從靜態緩存中取了2次,表達式返回了對象與自己比較的結果。因爲只有一個Integer對象,所以返回結果爲true。

Integer.valueOf("128")==Integer.valueOf("128");

上面的表達式返回false,因爲128沒有存在靜態緩衝區。所以每次在判斷相等時等式兩邊都會創建新的Integer對象。由於兩個Integer對象不同,所以==只有等式兩邊代表同一個對象時纔會返回true。因此,上面的等式返回false。

Integer.parseInt("128")==Integer.valueOf("128");

上面的表達式比較的是左邊的原始int值128與右邊新創建的Integer對象。但是因爲int和Integer之間比較是沒有意義的,所以Java在進行比較前會將Integer自動拆箱,所以最後進行的是int和int值之間的比較。由於128和自己相等,所以返回true。

注意:此文只適應於jdk7或以上版本,因爲jdk6與jdk7的Integer具體實現有差別,詳情可查看下源代碼.


Integer.parseInt()

Integer.valueof() 和 Integer.parseInt() 的底層都是用的Integer.parseInt(String s ,int radix)這個方法。在這裏給這個方法做一下解釋。

  Integer.parseInt(String s ,int radix),radix用來表示傳進來的值是什麼進制的,並返回10進制的 int 類型的結果 

  比如Integer.parseInt(“A”,16),則輸出結果爲10進制的10,其中16表示"A"是一個16進制的值。

  根據:Character.MIN_RADIX=2和Character.MAX_RADIX=36 則,parseInt(String s, int radix)參數中radix的範圍是在2~36之間,超出範圍會拋異常。其中s的長度也不能超出7,否則也會拋異常。其中限制在36位之內是因爲數字加字母剛好可以表示到36位,比如Integer.parseInt(“Z”,36),輸出結果爲35。

  以下爲parseInt(String s ,Int radix)的源代碼實現。

/**
 * 字符串轉換成整數
 * @param s	待轉換字符串
 * @param radix	進制
 * @return
 */
public static int parseInt(String s,int radix){
	//邊界值處理
	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;
	
	int digit;
	
	int max=s.length();
	
	//最大邊界值
    int limit;
	
	//最大邊界值右移一位
	int multmin;
	
	if(max>0){
		//處理符號
		if(s.charAt(0)=='-'){
			negative=true;
			//邊界值爲0x80000000
			limit=Integer.MIN_VALUE;
			i++;
		}
		else{
			//邊界值爲-0x7fffffff
			limit=-Integer.MAX_VALUE;
		}
		//計算multmin 值 ,multmin = -214748364 負數跟整數的limit是不同的
		multmin=limit/radix;
		if(i<max){
			digit=Character.digit(s.charAt(i++), radix);
			if(digit<0){
				throw NumberFormatException.forInputString(s);
			}
			else{
				result=-digit;
			}
		}
        //開始循環追加數字,比如輸入“123” 10進制數
		while(i<max){
			//獲取字符轉換成對應進制的整數,如上,這裏第一次循環獲取1
            //第二次循環獲取2
            //第三次循環獲取3
			digit=Character.digit(s.charAt(i++), radix);
			if(digit < 0){
				throw NumberFormatException.forInputString(s);
			}
			//判斷,在追加後一個數字前,判斷其是否能能夠在繼續追加數字,比如multmin = 123
            //那麼再繼續追加就會變爲123*10+下一個數字,就會溢出
			if(result < multmin){
				throw NumberFormatException.forInputString(s);
			}

            //第一次循環   result = 0;
            //第二次循環   result = -10;
            //第三次循環   result = -120;
			result*=radix;
			
			if(result<limit+digit){
                //第一次循環  limit + digit = -2147483647+1;
                //第二次循環   limit + digit = -2147483647+2;
                //第三次循環   limit + digit = -2147483647+3;
				throw NumberFormatException.forInputString(s);
			}
			result-=digit;
            //第一次循環 result = -1;
            //第二次循環 result = -12;
            //第三次循環 result = -123;
		}
	}
	else{
		throw NumberFormatException.forInputString(s);
	}
	if(negative){
		if(i>1){
			return result;
		}
		else{
			throw NumberFormatException.forInputString(s);
		}
	}
	else{
        //negative 值爲false,所以 -result = -(-123) = 123  返回結果
		return -result;
	}
}

關鍵點:

  1. 正數的邊界值爲1至0x7fffffff;負數的邊界值爲-1至0x80000000;
  2. 代碼中將所有數據當做負數(正數)來處理,最後處理符號問題;
  3. 方法中multmin這個變量是爲了在循環中result*=radix不會發生越界;

Integer.parseInt("")、 Integer.valueOf("")new Integer("")它們之間有什麼區別呢?我們可以分別看一下它們的源碼

//Integer.parseInt("")
public static int parseInt(String s) throws NumberFormatException {
  return parseInt(s,10);
}
//Integer.valueOf("")
public static Integer valueOf(String s) throws NumberFormatException {
  return Integer.valueOf(parseInt(s, 10));
}
//new Integer("")
public Integer(String s) throws NumberFormatException {
  this.value = parseInt(s, 10);
}

從源碼中可以看出,Integer.valueOf("")Integer.parseInt("")內部實現是一樣的,它們之間唯一的區別就是Integer.valueOf(“”)返回的是一個Integer對象,而Integer.parseInt(“”)返回的是一個基本類型的int

我們再看Integer.valueOf("")new Integer(""),它們同樣返回的是一個Integer對象,但它們又有什麼區別呢?我們再進入Integer.valueOf(parseInt(s, 10) )方法內部:

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

我們可以看到,Integer.valueOf("")會用到IntegerCache對象,當IntegerCache中存在時就從cache中取,不存在時纔會調用new Integer(i)構造函數返回一個Integer對象。所以Integer.valueOf("")會用到cache,其效率可能會比用構造函數new Integer(i)

關於IntegerCache,在-127~128之間的值都會被cache,所以當我們要的值位於這個區間時返回的都是同一個實例,例如:

System.out.println(Integer.valueOf(5) == Integer.valueOf(5)); 

System.out.println(Integer.valueOf(500) == Integer.valueOf(500));

輸出結果:

true //會用到緩存

false //不會用到緩存

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章