1. 程式人生 > >關於Integer與int拆箱裝箱的總結

關於Integer與int拆箱裝箱的總結

    最近在編碼過程中,程式碼提交之後sonar報了Bug警告,引發了對java中Integer類的一些思考。程式碼如下:

public boolean verifyDimensions() {
	if (CollectionUtils.isEmpty(dimensions)) {
		return false;
	} else {
		boolean hasRightDim = false;
		
		for (DimensionInApplyDTO dim : dimensions) {
			if (dim.getIsRight() == Integer.valueOf(1)) { // 報錯位置; 注:dim.getIsRight()返回 0/1/NULL Integer物件
				hasRightDim = true;
				break;
			}
		}
		if (!hasRightDim) 
			return false;
	}
	return true;
}
    Bug警告:   This method compares two reference values using the == or != operator, where the correct way to compare instances of this type is generally with the equals() method. It is possible to create distinct instances that are equal but do not compare as == since they are different objects. Examples of classes which should generally not be compared by reference are java.lang.Integer, java.lang.Float, etc.

     意思就是兩個引用型別的物件進行比較,不要用“==”,要用equals(),因為"=="比較的是地址呀云云...  那為什麼這段程式碼我在測試的時候並沒有發現錯誤呢,後來看了下Integer的原始碼,結合網友大神的解答,一切都明瞭了。

Integer與int在拆箱裝箱過程中主要用到兩個方法: valueOf()  和  intValue(),下面通過具體的例子來說一下拆箱裝箱的過程~

1). intValue(): Integer --> int  拆箱

/**
 * Returns the value of this {@code Integer} as an
 * {@code int}.
 */
public int intValue() {
	return value;
}
@Test
public void test1() throws Exception {
	int i = 10;
	
	Integer j = 10;
	System.out.println(i == j);  // true
	
	Integer z = null;
	System.out.println(i == z);  // java.lang.NullPointerException
}
     第6行結果為true: 在Integer物件與int物件i做比較時, Integer j 物件會呼叫intValue()方法返回一個int物件, 返回的這個int物件再與i進行比較,此時兩個做比較的物件都是int物件,都為基本型別,所以“==”直接就比較值是否相等。

     第9行結果為空指標異常: z為空物件,所以呼叫一個空物件的某個方法時,會報空指標異常,我們在比較Integer與int時,注意Integer不能為null。

2). valueOf(): int --> Integer 裝箱

@Test
public void test2() throws Exception {
	
	Integer i = Integer.valueOf(127);
	Integer j = 127;
	System.out.println(i == j);  //true
	
	
	Integer x = Integer.valueOf(128);
	Integer y = 128;
	System.out.println(x == y);  //false
}
     測試程式碼第5行 j的賦值是將int物件賦值給Integer型別,此時int物件會執行自動裝箱操作,呼叫Integer的靜態方法valueOf(int i),傳入一個int物件,返回一個Integer物件。所以第4行第5行的兩個賦值方式是沒有區別的。

     那麼為什麼兩次比較,第一次比較i,j相等,第二次比較x,y就不相等呢,別急,我們看一下Integer.valueOf(int i)的原始碼:

/**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} instance is not
 * required, this method should generally be used in preference to
 * the constructor {@link #Integer(int)}, as this method is likely
 * to yield significantly better space and time performance by
 * caching frequently requested values.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
	if (i >= IntegerCache.low && i <= IntegerCache.high)
		return IntegerCache.cache[i + (-IntegerCache.low)];
	return new Integer(i);
}
     程式碼中IntegerCache.low為-128, IntegerCache.high為127。 由程式碼可看出, 如果傳入的int值在-128與127之間,則呼叫了一個IntegerCache類,否則new一個新的Integer物件。

     我們都知道new出來的物件位於堆中,new多少次就有多少個不同的物件,但是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.
 * // 陣列的大小可以通過控制虛擬機器的-XX:AutoBoxCacheMax=<size>引數來設定。
 */
private static class IntegerCache {
	static final int low = -128;
	static final int high;
	static final Integer cache[]; // 快取了一個Integer型別的陣列

	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() {}
 }

      由IntegerCache原始碼我們能夠知道,IntegerCache這裡面封裝了一個Integer型別的陣列(程式碼12行),存放-128至127的Integer型別的物件,第一次使用時進行初始化(程式碼34行)。所以Integer.valueOf(int i)方法,在傳入i的值不在-128和127之間時,便new出一個新的Integer物件;如果範圍在-128和127之間,則直接從IntegerCache快取中取出i  對應的Integer物件。

     我們轉過頭來再看下測試程式碼:

@Test
public void testName2() throws Exception {
	
	Integer i = Integer.valueOf(127);
	Integer j = 127;
	System.out.println(i == j);  //true
	
	
	Integer x = Integer.valueOf(128);
	Integer y = 128;
	System.out.println(x == y);  //false
}
     127在-128和127之間,所以int型別的127在執行裝箱操作,呼叫valueOf方法時,取出的是IntegerCache快取的Integer物件,所以,不管來多少個127,只要是呼叫valueOf方法,返回的都是一個物件,用“==”返回true,記憶體地址也僅此一份。

     128不在-128和127之間,每次呼叫valueOf方法,都會產生一個獨一無二的Integer物件,所以用“==”返回false;但是因為兩個都是引用物件,用equals()則會返回true,因為x,y的字面值均為128。

     回到文章開頭,sonar給我的程式碼報出了一個嚴重bug,但是我自己在測試時沒有發現錯誤,

     if (dim.getIsRight() == Integer.valueOf(1)) ...

     是因為在專案邏輯中,dim.getIsRight()只可能返回 null、1、0三個情況, 返回的1也是IntegerCache中的引用物件,所以用“==”,等於1的情況下也會返回true,但是sonar不知道這種情況,提出了bug,所以說,無論什麼時候程式碼質量一定要嚴謹。

3).  parseInt()

@Test
public void testName3() throws Exception {
	
	System.out.println(Integer.parseInt("128") == Integer.valueOf(128)); // true
	
	int i = Integer.parseInt("128");
	Integer j =Integer.valueOf(128);
	System.out.println(i == j); // true
}
     上面這個例子用到了Integer的另一個方法parseInt(), 將一個物件轉換為int物件(原始碼不貼了), parseInt()返回值是一個int物件,注意是一個int物件,不是Integer!

     所以測試程式碼中 Integer.parseInt("128") 其實就是int型別的128,第4行也就等價於 System.out.println(128 == Integer.valueOf(128));  後者呼叫valueOf方法返回一個128的Integer物件,與前面的128比較時,後者自動拆箱成為int型別的128,所以最後兩個int型別的128比較,返回true。

     關於int與Integer型別各種各樣的問題,大家如果有問題可以在評論裡發一下,在今後遇到比較好的例子或者刁鑽的面試題,我也會繼續在本文下面列出,持續更新,共同進步~

相關推薦

關於Integerint裝箱總結

    最近在編碼過程中,程式碼提交之後sonar報了Bug警告,引發了對java中Integer類的一些思考。程式碼如下: public boolean verifyDimensions() { if (CollectionUtils.isEmpty(dimension

Java包裝類、自動裝箱知識總結

因為在學習集合時知道集合裡存放的物件都是Object型別,取出的時候需要強制型別轉換為目標型別(使用泛型集合不需要),如int a = (Integer)arrayList.get(0);然後我們就會發現,為什麼要強制轉換為Integer,而不是int呢?int與Integ

Java中自動裝箱自動機制

以下筆記來自於《演算法 (第4版)》。   型別引數必須被例項化為引用型別,因此Java有一種特殊機制來使泛型程式碼能夠處理原始資料型別。 Java中的封裝型別都是原始資料型別所對應的引用型別:Boolean Byte Character Double Float Intege

Java學習筆記09--靜態匯入 ;增強for迴圈 ;可變引數 ;自動裝箱自動;FILE(檔案操作)

∆ 靜態匯入: 靜態匯入的作用: 簡化書寫。 靜態匯入可以作用一個類的所有靜態成員。 靜態匯入的格式: import static 包名.類名.靜態的成員; 靜態匯入要注意的事項:

Integer的自動和自動裝箱的陷阱(整型數-128到127的值比較問題)

Integer的自動拆裝箱的陷阱(整型數-128到127的值比較問題): 1、先看下面的例子: package integerdemo;   publicclass IntegerDemo {       publicstaticvoid main(String[] args) {         

Java基本型別的自動裝箱自動

一、簡介 java是面入物件程式設計的語言,所有的都表示為物件。那麼對於基本型別,java提供了對應的包裝類。 相應的就有自動裝箱與自動拆箱操作。 二、自動裝箱操作 1、定義 自動裝箱是指基本型別轉為對應的包裝類,如int轉Integer,double轉為Double等。

JAVA包裝類及其裝箱Integer裝箱的細節

在JAVA中,資料型別可分為兩大種,基本資料型別(值型別)和類型別(引用資料型別)。基本型別的資料不是物件,所以對於要將基本資料型別當物件使用的情況,JAVA提供的包裝類。基本型別和包裝類的對應關係如下: int -- Integer char -- Character

Java Integer int 深刻理解

true ger style blog 問題: pan left www. out   今天在做Object 自動轉為Integer 類型之後的判斷,遇到一個不理解的點,當數值超過127之後,兩個數值相同的Object 對象用 == 判斷的結果是false。

WPF中多線程統計裝箱和泛型的運行效率

eset 主線程 ted fonts reset 通知 ack foreach -c WPF中多線程統計拆箱裝箱和泛型的執行效率。使用的知識點有泛型、多線程、托付。

java基礎(八) 深入解析常量池機制

java引言 ??本文將介紹常量池 與 裝箱拆箱機制,之所以將兩者合在一起介紹,是因為網上不少文章在談到常量池時,將包裝類的緩存機制,java常量池,不加區別地混在一起討論,更有甚者完全將這兩者視為一個整體,給初學者帶來不少困擾,我就是過來的。同時,也因為包裝類的緩存 與 字符串常量池的思想是一樣的,很容易混

Integerint和String物件字串之間的"=="和equals情況

一、Integer與int ①程式碼 @Test public void testInteger() { int i = 129; int j = 129; Integer m = 128; Integer n

java的自動裝箱

以下程式執行結果為 public class Test { public static void main(String[] args) { Integer i=null; if(i==0){ System.out

自動裝箱

public class Demo4_JDK5 { /** * * A:JDK5的新特性 * 自動裝箱:把基本型別轉換為包裝類型別 * 自動拆箱:把包裝類型別轉換為基本型別 * B:案例演示 * JDK5的新特性自動裝箱和拆箱 * Integer

資料型別之Integerint

資料型別之Integer與int Java入門  基本資料型別 眾所周知,Java是面向物件的語言,一切皆物件。但是為了相容人類根深蒂固的資料處理習慣,加快常規資料的處理速度,提供了9種基本資料型別,他們都不

JAVA高階特性--自動-裝箱,列舉型別

基本資料型別轉換為引用型別物件 一個自動裝箱的例子 Integer i=10; 相當於 Integer i=new Integer(10);   一個自動拆箱的例子 Integer m=10; int n=m; 相當於n=m.intValue();  

Integer Int 的區別

Integer 類整形 與  Int 整形 的區別   最近在翻java的原始碼,發現了一些平常不注意,但很容易出錯的東西,最近有空的話就整理出來。   Integer:是一個int的包裝類,Integer變數的儲存需要例項化Integer的例項物件,

Integerint的區別?

1、Integer 是 int 提供的封裝類,從 Java 5開始引入了自動裝箱/拆箱機制,使得二者可以相互轉化,而 int 是 Java 的基本資料型別; 2、Integer 預設值是 null ,而 int 預設值是0; 3、Integer 是物件,用一個引用指向這個物件,而 int

Integerint使用==比較的總結

/** * Returns an {@code Integer} instance representing the specified * {@code int} value. If a new {@code Integer} instance is not * required, thi

Integerint的區別

徹底讓你明白 Integer 類和 int 基本資料型別的區別 2018年02月01日 16:46:33 鄭州尚學堂李老師 閱讀數:1866 標籤: Integer 類 int 基本資料型別  淺談 Integer 類 目錄 1、Integer 類簡介 2、

Integerint及String的總結

秉承著總結髮表是最好的記憶,我把之前遇到的問題在這裡總結和大家分享一下,希望大家共同進步: 一.Integer和int首先說下自動拆裝箱,基本資料型別轉換為包裝型別的過程叫裝箱,反之則是拆箱,其中最特殊也是面試經常遇到的是Integer,下面總結說明,話不多說,show the code,先猜一下下面這6個