今天分享遇到的一個線上的 bug,線上代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Scratch { public static void main(String[] args) { JSONArray arrays = JSONUtil.parseArray( "[{'type':1},{},{'type':2},{'type':2}" + ",{'name':'zhangsan'},{'type':1},{'type':1},{'type':1}]" ); List<User> users = JSONUtil.toList(arrays, User. class ); Set<User> set = users.stream().filter(u -> u.getType() == 1 ).collect(Collectors.toSet()); System.out.println(set); } @Data static class User { private String name; private Integer type; } } |
類似于這樣子的一段代碼會拋出一個空指針異常,你可以嘗試找一下哪里有可能會出現空指針異常。
異常堆棧長這樣子:
1
2
3
4
5
6
7
8
9
10
|
Exception in thread "main" java.lang.NullPointerException at Scratch.lambda$main$ 0 (scratch.java: 14 ) at java.util.stream.ReferencePipeline$ 2 $ 1 .accept(ReferencePipeline.java: 174 ) at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java: 1382 ) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java: 481 ) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java: 471 ) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java: 708 ) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java: 234 ) at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java: 499 ) at Scratch.main(scratch.java: 14 ) |
這個空指針異常還是比較好找到的,位于 Stream 中的 filter 中比較出現了異常:
1
|
u -> u.getType() == 1 |
我一開始的想法是對象 u 是一個 null
但后來發現不是,最終找到的地方是 u.getType()
是一個null,是由于 null == 1
拋出了一個空指針異常。
這就涉及到一個 java 的基礎點了 null == 1
等于什么?
== 是 java 中一個雙目比較運算符,可以用于基礎數據類型和引用數據類型的比較,當基礎數據類型之間比較時,會進行值之間的比較,比如:
1
2
3
|
1 == 1 // true 1 == 2 // false 1.33 == 1.33 // true |
諸如以上的例子。
同樣的還可以進行對象之間的比較,如果是對象之間的比較的話,則會比較兩個變量所指向對象在內存中的地址,也就是說如果兩個變量沒有指向同一個對象的話,得到的就是 false;
1
2
3
4
5
6
|
null == Integer.valueOf( 1 ) // false new Integer( 1 ) == Integer.valueOf( 1 ) // false Integer val1 = new Integer( 13 ); Integer val2 = new Integer( 13 ); val1 == val2; // false |
這里不對 ==
與 equals
的區別做介紹,如果想要了解的可以自行查閱。
我想詳細描述的是我遇到的一種情況,是引用數據類型與基本數據類型之間用==比較的話會發生什么。
因為我的印象中 == 是不會引起空指針異常的,頂多一方為 null 而另外一方有值時會返回 false。
但是在這種情況在引用數據類型與基本數據類型進行比較的時候發生了。
1
|
null == 1 // NullPointerException |
正常的情況來講,當引用數據類型與基本數據類型進行比較的時候,會將引用數據類型一方先進行拆箱操作(unbox),然后對兩方進行值比較:
1
2
|
1 == Integer.valueOf( 1 ); // true 1 == new Integer( 1 ); // true |
但是如果傳入的變量是一個 null
的話,就會導致拆箱操作無法正常進行,從而導致拋出一個 NullPointerException
。
由于拆箱操作是隱式進行的,對于開發者而言如果不知道發生了拆箱操作的話,就很難定位到空指針的位置,因此在進行等值判斷的時候,建議盡量使用jdk自帶的工具方法:
1
|
Objects.equals( null , 1 ); // false |
而它內部的實現是這樣子的:
1
2
3
|
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); } |
對于引用數據類型和基本數據類型的比較,它首先會將傳入的基本數據類型進行裝箱操作(box),然后進行對象之間的比較(比較地址),在不相同的情況下再通過 equals進行判斷,也就是對==等值操作做了進一步的封裝。
參考資料
總結
- 如果是基本數據類型,==判斷的是值
- 如果是對象類型,==判斷的是對象的地址
- 如果一邊是基本數據類型,另一邊是對象類型,則會首先對對象類型進行拆箱,然后按照基本數據類型來處理。
我需要畫重點的地方是 == 有可能會引起拆箱操作,當傳入對象為 null時拆箱操作會引發空指針異常問題。
建議在使用 ==
的場景下統一使用 Objects.equals
來代替。
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!
原文鏈接:https://blog.csdn.net/qq_19922839/article/details/120461483