淺談Java主動裝箱與拆箱及其圈套。本站提示廣大學習愛好者:(淺談Java主動裝箱與拆箱及其圈套)文章只能為提供參考,不一定能成為您想要的結果。以下是淺談Java主動裝箱與拆箱及其圈套正文
在本文中,筆者向年夜家引見下Java中一個異常主要也異常風趣的特征,就是主動裝箱與拆箱,並從源碼中解讀主動裝箱與拆箱的道理,同時這類特征也留有一個圈套。開辟者假如不留意,就會很輕易跌入這個圈套。
主動裝箱(Autoboxing)
界說
年夜家在日常平凡編寫Java法式時,都經常以以下方法來界說一個Integer對象:
Integer i=100;
從下面的代碼中,年夜家可以得知,i為一個Integer類型的援用,100為Java中的基本數據類型(primitive data type)。而這類直接將一個基本數據類型傳給其響應的封裝類(wrapper class)的做法,就是主動裝箱(Autoboxing)。
在jdk 1.5中,主動裝箱初次被引入。而在jdk 1.5之前,假如你想要界說一個value為100的Integer對象,則須要如許做:
Integer i=new Integer (100);
道理
我們在以上代碼“Integer i=100;”處打一個斷點,跟蹤一下。
接上去,我們可以看到,法式跳轉到了Integer類的valueOf(int i)辦法中
/** * Returns a <tt>Integer</tt> instance representing the specified * <tt>int</tt> value. * If a new <tt>Integer</tt> 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. * * @param i an <code>int</code> value. * @return a <tt>Integer</tt> instance representing <tt>i</tt>. * @since 1.5 */ public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); }
換句話說,裝箱就是jdk本身幫你完成了挪用Integer.valueOf(100)。
拆箱(Unboxing)
界說
Integer integer100=100; int int100=integer100;
從下面的代碼中,年夜家可看出integer100為一個Integer類型的援用,int100為一個int類型的原始數據類型。然則,我們可以將一個Integer類型的對象賦值給其響應原始數據類型的變量。這就是拆箱。
拆箱與裝箱是相反的操作。裝箱是將一個原始數據類型賦值給響應封裝類的變量。而拆箱則是將一個封裝類的變量賦值給響應原始數據類型的變量。裝箱、拆箱的名字也獲得相當貼切。
道理
筆者信任年夜家也都猜到了,拆箱進程中jdk為我們做了甚麼。我們照樣經由過程試驗來證實我們的料想吧。
在以上代碼的第二行代碼打上斷點,即在“int int100=integer100;”上打上斷點,跟蹤一下。
我們可以看到,法式跳轉到了Integer的intValue()辦法。
/** * Returns the value of this <code>Integer</code> as an * <code>int</code>. */ public int intValue() { return value; }
也就是,jdk幫我們完成了對intValue()辦法的挪用。關於以上的試驗而言,就是挪用integer100的intValue()辦法,將其前往值賦給了int100。
擴大
試驗1
Integer integer400=400; int int400=400; System.out.println(integer400==int400);
在以上代碼的第三行中,integer400與int400履行了==運轉。而這兩個是分歧類型的變量,究竟是integer400拆箱了,照樣int400裝箱了呢?運轉成果是甚麼呢?
==運算是斷定兩個對象的地址能否相等或許斷定兩個基本數據類型的值能否相等。所以,年夜家很輕易推想到,假如integer400拆箱了,則解釋比較的是兩個基本類型的值,那此時必定相等,運轉成果為true;假如int400裝箱了,則解釋比較的是兩個對象的地址能否相等,那此時地址必定不相等,運轉成果為false。(至於為何筆者對它們賦值為400,就是前面將要講到的圈套有關)。
我們現實的運轉成果為true。所所以integer400拆箱了。對代碼跟蹤的成果也證實這一點。
試驗2
Integer integer100=100; int int100=100; System.out.println(integer100.equals(int100));
在以上代碼的第三行中,integer100的辦法equals的參數為int100。我們曉得equals辦法的參數為Object,而不是基本數據類型,因此在這裡必定是int100裝箱了。對代碼跟蹤的成果也證實了這一點。
其實,假如一個辦法中參數類型為原始數據類型,所傳入的參數類型為其封裝類,則會主動對其停止拆箱;響應地,假如一個辦法中參數類型為封裝類型,所傳入的參數類型為其原始數據類型,則會主動對其停止裝箱。
試驗3
Integer integer100 = 100; int int100 = 100; Long long200 = 200l; System.out.println(integer100 + int100); System.out.println(long200 == (integer100 + int100)); System.out.println(long200.equals(integer100 + int100));
在第一個試驗中,我們曾經得知,當一個基本數據類型與封裝類停止==運算時,會將封裝類停止拆箱。那假如+、-、*、/呢?我們在這個試驗中,便可曉得。
假如+運算,會將基本數據類型裝箱,那末:
•第4行中,integer100+int100就會獲得一個類型為Integer且value為200的對象o,並履行這個對象的toString()辦法,並輸入”200”;
•第5行中,integer100+int100就會獲得一個類型為Integer且value為200的對象o,==運算將這個對象與long200對象停止比較,明顯,將會輸入false;
•第6行中,integer100+int100就會獲得一個類型為Integer且value為200的對象o,Long的equals辦法將long200與o比較,由於兩都是分歧類型的封裝類,因此輸入false;
假如+運算,會將封裝類停止拆箱,那末:
•第4行中,integer100+int100就會獲得一個類型為int且value為200的基本數據類型b,再將b停止裝箱獲得o,履行這個對象的toString()辦法,並輸入”200”;
•第5行中,integer100+int100就會獲得一個類型為int且value為200的基本數據類型b1,==運算將long200停止拆箱獲得b2,明顯b1==b2,輸入true;
•第6行中,integer100+int100就會獲得一個類型為int且value為200的基本數據類型b,Long的equals辦法將b停止裝箱,但裝箱所獲得的是類型為Integer的對象o,由於o與long200為分歧的類型的對象,所以輸入false;
法式運轉的成果為:
200
true
false
因此,第二種推想是准確,即在+運算時,會將封裝類停止拆箱。
圈套
圈套1
Integer integer100=null;
int int100=integer100;
這兩行代碼是完整正當的,完整可以或許經由過程編譯的,然則在運轉時,就會拋出空指針異常。個中,integer100為Integer類型的對象,它固然可以指向null。但在第二行時,就會對integer100停止拆箱,也就是對一個null對象履行intValue()辦法,固然會拋出空指針異常。所以,有拆箱操作時必定要特殊留意封裝類對象能否為null。
圈套2
Integer i1=100;
Integer i2=100;
Integer i3=300;
Integer i4=300;
System.out.println(i1==i2);
System.out.println(i3==i4);
由於i1、i2、i3、i4都是Integer類型的,所以我們想,運轉成果應當都是false。然則,真實的運轉成果為“System.out.println(i1==i2);”為 true,然則“System.out.println(i3==i4);”為false。也就意味著,i1與i2這兩個Integer類型的援用指向了統一個對象,而i3與i4指向了分歧的對象。為何呢?不都是挪用Integer.valueOf(int i)辦法嗎?
讓我們再看看Integer.valueOf(int i)辦法。
/** * Returns a <tt>Integer</tt> instance representing the specified * <tt>int</tt> value. * If a new <tt>Integer</tt> 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. * * @param i an <code>int</code> value. * @return a <tt>Integer</tt> instance representing <tt>i</tt>. * @since 1.5 */ public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); }
我們可以看到當i>=-128且i<=IntegerCache.high時,直接前往IntegerCache.cache[i + 128]。個中,IntegerCache為Integer的外部靜態類,其原碼以下:
private static class IntegerCache { static final int high; static final Integer cache[]; static { final int low = -128; // high value may be configured by property int h = 127; if (integerCacheHighPropValue != null) { // Use Long.decode here to avoid invoking methods that // require Integer's autoboxing cache to be initialized int i = Long.decode(integerCacheHighPropValue).intValue(); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - -low); } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} }
我們可以清晰地看到,IntegerCache有靜態成員變量cache,為一個具有256個元素的數組。在IntegerCache中也對cache停止了初始化,即第i個元素是值為i-128的Integer對象。而-128至127是最經常使用的Integer對象,如許的做法也在很年夜水平上進步了機能。也正由於如斯,“Integeri1=100;Integer i2=100;”,i1與i2獲得是雷同的對象。
比較擴大中的第二個試驗,我們得知,當封裝類與基本類型停止==運轉時,封裝類會停止拆箱,拆箱成果與基本類型比較值;而兩個封裝類停止==運轉時,與其它的對象停止==運轉一樣,比較兩個對象的地址,也即斷定能否兩個援用能否指向統一個對象。
以上這篇淺談Java主動裝箱與拆箱及其圈套就是小編分享給年夜家的全體內容了,願望能給年夜家一個參考,也願望年夜家多多支撐。