日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java自动装箱与拆箱及其陷阱

發布時間:2024/4/13 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java自动装箱与拆箱及其陷阱 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

在本文中,筆者向大家介紹下Java中一個非常重要也非常有趣的特性,就是自動裝箱與拆箱,并從源碼中解讀自動裝箱與拆箱的原理,同時這種特性也留有一個陷阱。開發者如果不注意,就會很容易跌入這個陷阱。

自動裝箱(Autoboxing)

定義

? ? ? ? ??大家在平時編寫Java程序時,都常常以以下方式來定義一個Integer對象:

[java]?? view plain? copy
  • Integer?i=100;??
  • ? ? ? ? ??從上面的代碼中,大家可以得知,i為一個Integer類型的引用,100為Java中的基礎數據類型(primitive data type)。而這種直接將一個基礎數據類型傳給其相應的封裝類(wrapper class)的做法,便是自動裝箱(Autoboxing)。

    ? ? ? ? ??在jdk 1.5中,自動裝箱首次被引入。而在jdk 1.5之前,如果你想要定義一個value為100的Integer對象,則需要這樣做:

    [java]?? view plain? copy
  • Integer?i=new?Integer?(100);??
  • 原理

    ? ? ? ? ??我們在以上代碼“Integer i=100;”處打一個斷點,跟蹤一下。 ?

    ? ? ? ? ???

    ? ? ? ? ??接下來,我們可以看到,程序跳轉到了Integer類的valueOf(int i)方法中

    [java]?? view plain? copy
  • /**?
  • ?????*?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)

    定義

    [java]?? view plain? copy
  • Integer?integer100=100;??
  • int?int100=integer100;??

  • ? ? ? ? ??從上面的代碼中,大家可看出integer100為一個Integer類型的引用,int100為一個int類型的原始數據類型。但是,我們可以將一個Integer類型的對象賦值給其相應原始數據類型的變量。這便是拆箱。

    ? ? ? ? ??拆箱與裝箱是相反的操作。裝箱是將一個原始數據類型賦值給相應封裝類的變量。而拆箱則是將一個封裝類的變量賦值給相應原始數據類型的變量。裝箱、拆箱的名字也取得相當貼切。

    原理

    ? ? ? ? ??筆者相信大家也都猜到了,拆箱過程中jdk為我們做了什么。我們還是通過實驗來證明我們的猜想吧。

    ? ? ? ? ??在以上代碼的第二行代碼打上斷點,即在“int?int100=integer100;”上打上斷點,跟蹤一下。

    ? ? ? ? ??我們可以看到,程序跳轉到了Integer的intValue()方法。

    [java]?? view plain? copy
  • /**?
  • ?????*?Returns?the?value?of?this?<code>Integer</code>?as?an?
  • ?????*?<code>int</code>.?
  • ?????*/??
  • ????public?int?intValue()?{??
  • ????return?value;??
  • ????}??

  • ? ? ? ? ??也就是,jdk幫我們完成了對intValue()方法的調用。對于以上的實驗而言,便是調用integer100的intValue()方法,將其返回值賦給了int100。

    擴展

    實驗1

    [java]?? view plain? copy
  • Integer?integer400=400;??
  • int?int400=400;??
  • System.out.println(integer400==int400);??
  • ? ? ? ? ??在以上代碼的第三行中,integer400與int400執行了==運行。而這兩個是不同類型的變量,到底是integer400拆箱了,還是int400裝箱了呢?運行結果是什么呢?

    ? ? ? ? ??==運算是判斷兩個對象的地址是否相等或者判斷兩個基礎數據類型的值是否相等。所以,大家很容易推測到,如果integer400拆箱了,則說明對比的是兩個基礎類型的值,那此時必然相等,運行結果為true;如果int400裝箱了,則說明對比的是兩個對象的地址是否相等,那此時地址必然不相等,運行結果為false。(至于為什么筆者對它們賦值為400,就是后面將要講到的陷阱有關)。

    ? ? ? ? ??我們實際的運行結果為true。所以是integer400拆箱了。對代碼跟蹤的結果也證明這一點。

    實驗2

    [java]?? view plain? copy
  • Integer?integer100=100;??
  • int?int100=100;??
  • System.out.println(integer100.equals(int100));??
  • ? ? ? ? ??在以上代碼的第三行中,integer100的方法equals的參數為int100。我們知道equals方法的參數為Object,而不是基礎數據類型,因而在這里必然是int100裝箱了。對代碼跟蹤的結果也證明了這一點。

    ? ? ? ? ??其實,如果一個方法中參數類型為原始數據類型,所傳入的參數類型為其封裝類,則會自動對其進行拆箱;相應地,如果一個方法中參數類型為封裝類型,所傳入的參數類型為其原始數據類型,則會自動對其進行裝箱。

    實驗3

    [java]?? view plain? copy
  • 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;

    ? ? ? ? ??程序運行的結果為: ? ? ?

    [java]?? view plain? copy
  • 200??
  • true??
  • false??
  • ? ? ? ? ??因而,第二種推測是正確,即在+運算時,會將封裝類進行拆箱。

    陷阱

    陷阱1

    [java]?? view plain? copy
  • Integer?integer100=null;??
  • int?int100=integer100;??
  • ? ? ? ? ??這兩行代碼是完全合法的,完全能夠通過編譯的,但是在運行時,就會拋出空指針異常。其中,integer100為Integer類型的對象,它當然可以指向null。但在第二行時,就會對integer100進行拆箱,也就是對一個null對象執行intValue()方法,當然會拋出空指針異常。所以,有拆箱操作時一定要特別注意封裝類對象是否為null。

    陷阱2

    [java]?? view plain? copy
  • 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)方法。

    [java]?? view plain? copy
  • /**?
  • ?????*?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的內部靜態類,其原碼如下:

    [java]?? view plain? copy
  • 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得到是相同的對象。

    ? ? ? ? ??對比擴展中的第二個實驗,我們得知,當封裝類與基礎類型進行==運行時,封裝類會進行拆箱,拆箱結果與基礎類型對比值;而兩個封裝類進行==運行時,與其它的對象進行==運行一樣,對比兩個對象的地址,也即判斷是否兩個引用是否指向同一個對象。

    轉載于:https://my.oschina.net/Chaos777/blog/295073

    總結

    以上是生活随笔為你收集整理的Java自动装箱与拆箱及其陷阱的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。