溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何解決Java自動(dòng)拆箱空指針異常

發(fā)布時(shí)間:2021-10-18 14:47:54 來(lái)源:億速云 閱讀:232 作者:iii 欄目:編程語(yǔ)言

這篇文章主要講解了“如何解決Java自動(dòng)拆箱空指針異常”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“如何解決Java自動(dòng)拆箱空指針異常”吧!

問(wèn)題重現(xiàn)

下面通過(guò)一個(gè)簡(jiǎn)單的示例才重現(xiàn)一下異常出現(xiàn)的場(chǎng)景。

public class BoxTest {      public static void main(String[] args) {         Map<String,Object> result = httpRequest();         long userId = (Long) result.get("userId");     }      // 模擬一個(gè)HTTP請(qǐng)求     private static Map<String,Object> httpRequest(){         Map<String,Object> map = new HashMap<>();         map.put("userId",null);         return map;     } }

基本的場(chǎng)景就是請(qǐng)求一個(gè)接口,去接口中取某個(gè)值,這個(gè)值為L(zhǎng)ong類(lèi)型,從Map中取得值之后,進(jìn)行Long類(lèi)型的強(qiáng)轉(zhuǎn)。當(dāng)接口返回的userId為null時(shí),強(qiáng)轉(zhuǎn)這塊就拋出空指針異常:

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

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

public class BoxTest {      public static long getValue(long value) {         return value;     }      public static void main(String[] args) {         Long value = null;         getValue(value);     } }

上述代碼也是將Long類(lèi)型進(jìn)拆箱導(dǎo)致的異常,只不過(guò)一個(gè)在代碼中,一個(gè)在參數(shù)中。為了分析更簡(jiǎn)化,我們以第二個(gè)為例進(jìn)行講解。

原因分析

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

這中間主要涉及到的就是一個(gè)自動(dòng)拆箱操作。是否是拆箱導(dǎo)致的呢?我們來(lái)通過(guò)字節(jié)碼看一下。

通過(guò)javap -c來(lái)查看一下對(duì)應(yīng)的字節(jié)碼:

public class com.choupangxia.box.BoxTest {   public com.choupangxia.box.BoxTest();     Code:        0: aload_0        1: invokespecial #1                  // Method java/lang/Object."<init>":()V        4: return    public static long getValue(long);     Code:        0: lload_0        1: lreturn    public static void main(java.lang.String[]);     Code:        0: aconst_null        1: astore_1        2: aload_1        3: invokevirtual #2                  // Method java/lang/Long.longValue:()J        6: invokestatic  #3                  // Method getValue:(J)J        9: pop2       10: return }

其中g(shù)etValue方法調(diào)用對(duì)應(yīng)的是main方法中編號(hào)3和6的操作。編號(hào)3為命令invokevirtual為方法指令。對(duì)應(yīng)的便是value.longValue,value對(duì)應(yīng)的就是聲明的Long類(lèi)型。

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

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

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

所以,本質(zhì)上來(lái)講,所謂的自動(dòng)拆箱和裝箱只不過(guò)是Java提供的語(yǔ)法糖而已。

再次證實(shí)

下面用int類(lèi)型的實(shí)例同時(shí)證實(shí)一下自動(dòng)拆箱和自動(dòng)裝箱兩個(gè)操作語(yǔ)法糖底層到底是怎么運(yùn)行的:

public class IntBoxTest {      public static void main(String[] args) {         Integer index = 11;         int primitive = index;     } }

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

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

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

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

通過(guò)上面的分析,我們可以看出所謂的拆箱(unboxing)和裝箱(boxing)操作只不過(guò)是一個(gè)語(yǔ)法糖的功能。編譯器在編譯操作時(shí),本質(zhì)上還是會(huì)調(diào)用對(duì)應(yīng)包裝類(lèi)的不同方法來(lái)進(jìn)行處理。

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

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

面試題

看一個(gè)面試題:請(qǐng)問(wèn)下面foo1和foo2被調(diào)用時(shí)如何執(zhí)行?并簡(jiǎn)單分析一下。

public void foo1() {     if ((Integer) null == 1) {     } }  public void foo2() {     if ((Integer) null > 1) {         System.out.println("abc");     } }

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

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

Integer value1 = (Integer) null; Double value2 = (Double) null; Boolean value3 = (Boolean) null;

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

感謝各位的閱讀,以上就是“如何解決Java自動(dòng)拆箱空指針異?!钡膬?nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)如何解決Java自動(dòng)拆箱空指針異常這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI