日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產(chǎn)品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Java自動拆箱空指針異常,救火隊員上線

本文轉(zhuǎn)載自微信公眾號「程序新視界」,作者二師兄 。轉(zhuǎn)載本文請聯(lián)系程序新視界公眾號。 

成都創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供博湖網(wǎng)站建設、博湖做網(wǎng)站、博湖網(wǎng)站設計、博湖網(wǎng)站制作等企業(yè)網(wǎng)站建設、網(wǎng)頁設計與制作、博湖企業(yè)網(wǎng)站模板建站服務,十年博湖做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡服務。

公司搬遷,臨時充當裝修工,提前兩個小時到公司忙著拆卸設備。結果接到客戶反映,某部分功能偶爾不能用。于是參與救火,與寫這段代碼的小伙伴一起排查原因。

最終發(fā)現(xiàn)導致業(yè)務偶爾不能使用是由Long類型自動拆箱導致空指針異常引起的。下面就帶大家分析一下Java中基礎類型的包裝類在拆箱和裝箱過程中都做了什么,為什么會出現(xiàn)空指針異常,以及面試過程中會出現(xiàn)的相關面試題。

問題重現(xiàn)

下面通過一個簡單的示例才重現(xiàn)一下異常出現(xiàn)的場景。

 
 
 
  1. public class BoxTest { 
  2.  
  3.     public static void main(String[] args) { 
  4.         Map result = httpRequest(); 
  5.         long userId = (Long) result.get("userId"); 
  6.     } 
  7.  
  8.     // 模擬一個HTTP請求 
  9.     private static Map httpRequest(){ 
  10.         Map map = new HashMap<>(); 
  11.         map.put("userId",null); 
  12.         return map; 
  13.     } 

基本的場景就是請求一個接口,去接口中取某個值,這個值為Long類型,從Map中取得值之后,進行Long類型的強轉(zhuǎn)。當接口返回的userId為null時,強轉(zhuǎn)這塊就拋出空指針異常:

 
 
 
  1. Exception in thread "main" java.lang.NullPointerException 
  2.  at com.choupangxia.box.BoxTest.main(BoxTest.java:15) 

上面的場景跟下面的代碼出現(xiàn)異常效果一樣:

 
 
 
  1. public class BoxTest { 
  2.  
  3.     public static long getValue(long value) { 
  4.         return value; 
  5.     } 
  6.  
  7.     public static void main(String[] args) { 
  8.         Long value = null; 
  9.         getValue(value); 
  10.     } 

上述代碼也是將Long類型進拆箱導致的異常,只不過一個在代碼中,一個在參數(shù)中。為了分析更簡化,我們以第二個為例進行講解。

原因分析

最初大家可能會疑惑,拋出異常的代碼都沒有對象的方法調(diào)用,怎么會出現(xiàn)空指針呢?

這中間主要涉及到的就是一個自動拆箱操作。是否是拆箱導致的呢?我們來通過字節(jié)碼看一下。

通過javap -c來查看一下對應的字節(jié)碼:

 
 
 
  1. public class com.choupangxia.box.BoxTest { 
  2.   public com.choupangxia.box.BoxTest(); 
  3.     Code: 
  4.        0: aload_0 
  5.        1: invokespecial #1                  // Method java/lang/Object."":()V 
  6.        4: return 
  7.  
  8.   public static long getValue(long); 
  9.     Code: 
  10.        0: lload_0 
  11.        1: lreturn 
  12.  
  13.   public static void main(java.lang.String[]); 
  14.     Code: 
  15.        0: aconst_null 
  16.        1: astore_1 
  17.        2: aload_1 
  18.        3: invokevirtual #2                  // Method java/lang/Long.longValue:()J 
  19.        6: invokestatic  #3                  // Method getValue:(J)J 
  20.        9: pop2 
  21.       10: return 

其中getValue方法調(diào)用對應的是main方法中編號3和6的操作。編號3為命令invokevirtual為方法指令。對應的便是value.longValue,value對應的就是聲明的Long類型。

也就是說編譯器將getValue(value)拆分成了兩步,第一步將通過value的longValue方法將其拆箱,然后再將拆箱之后的結果傳遞給方法。相當于:

 
 
 
  1. long primitive = value.longValue(); 
  2. test(promitive); 

對照最開始的代碼,如果value為null的話,那么在調(diào)用longValue方法時便會拋出NullPointerException。

所以,本質(zhì)上來講,所謂的自動拆箱和裝箱只不過是Java提供的語法糖而已。

再次證實

下面用int類型的實例同時證實一下自動拆箱和自動裝箱兩個操作語法糖底層到底是怎么運行的:

 
 
 
  1. public class IntBoxTest { 
  2.  
  3.     public static void main(String[] args) { 
  4.         Integer index = 11; 
  5.         int primitive = index; 
  6.     } 

同樣查看上面代碼的字節(jié)碼:

 
 
 
  1. public class com.choupangxia.box.IntBoxTest { 
  2.   public com.choupangxia.box.IntBoxTest(); 
  3.     Code: 
  4.        0: aload_0 
  5.        1: invokespecial #1                  // Method java/lang/Object."":()V 
  6.        4: return 
  7.  
  8.   public static void main(java.lang.String[]); 
  9.     Code: 
  10.        0: bipush        11 
  11.        2: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 
  12.        5: astore_1 
  13.        6: aload_1 
  14.        7: invokevirtual #3                  // Method java/lang/Integer.intValue:()I 
  15.       10: istore_2 
  16.       11: return 

可以看到main方法部分,編號2進行了裝箱操作,將原始類型int,裝箱成了Integer,調(diào)用的方法為Integer.valueOf;而編號7進行了拆箱操作將Integer類型轉(zhuǎn)換成了int類型,調(diào)用的方法為Integer.intValue。

自動拆箱裝箱的本質(zhì)

通過上面的分析,我們可以看出所謂的拆箱(unboxing)和裝箱(boxing)操作只不過是一個語法糖的功能。編譯器在編譯操作時,本質(zhì)上還是會調(diào)用對應包裝類的不同方法來進行處理。

裝箱時通常會調(diào)用包裝類的valueOf方法,而拆箱時通常會調(diào)用包裝類的xxxValue()方法,其中xxx為類似boolean/long/int等。

而自動拆箱和裝箱的操作主要發(fā)生在賦值、比較、算數(shù)運算、方法調(diào)用等常見。此時,我們就需要主要空指針的問題。

面試題

看一個面試題:請問下面foo1和foo2被調(diào)用時如何執(zhí)行?并簡單分析一下。

 
 
 
  1. public void foo1() { 
  2.     if ((Integer) null == 1) { 
  3.     } 
  4.  
  5. public void foo2() { 
  6.     if ((Integer) null > 1) { 
  7.         System.out.println("abc"); 
  8.     } 

很明顯在調(diào)用兩個方法時都會拋出空指針異常。關于拋空指針異常的原因及分析過程,上文已經(jīng)講過,大家可以嘗試分析一下字節(jié)碼。

再看一個面試題:下面的語句能正常執(zhí)行嗎?

 
 
 
  1. Integer value1 = (Integer) null; 
  2. Double value2 = (Double) null; 
  3. Boolean value3 = (Boolean) null; 

答案:可以正常執(zhí)行。在Java中null是一個特殊的值,可以賦值給任何引用類型,也可以轉(zhuǎn)化為任何引用類型。

小結

任何一個小的問題,小的異常,如果深入追蹤一下,不僅能夠更清楚的明白底層原理,而且還可以在實踐的過程中更有把握,更少犯錯。


網(wǎng)頁標題:Java自動拆箱空指針異常,救火隊員上線
本文地址:http://www.5511xx.com/article/dpgeoeg.html