先假設(shè)一個場景衣屏。如下所示
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
我們有一個Person類躏升,有一個屬性是name。有如下代碼:
public static void main(String[] args) {
Person person = new Person();
String name = person.getName();
if (name.equals("Jackson")) {
System.out.println("My name is Jackson!");
}
}
我們判斷獲取person的name狼忱,然后判斷person的name是不是Jackson膨疏。因為person的name為null,所以name.equals("Jackson")
這段代碼會報空指針的異常钻弄。
我們有多種方式來處理空指針的異常佃却,一種是我們考察我們的業(yè)務(wù)邏輯,當(dāng)需要返回一個null值時返回一個有意義的NullObject窘俺。例如空字符串對應(yīng)返回字符串的情況饲帅,空列表對應(yīng)返回list的情況,對一些特殊的對象,可以根據(jù)不同的業(yè)務(wù)邏輯創(chuàng)建一些Null對象灶泵。參考《Effictive Java》第43條育八,《重構(gòu)》9.7節(jié)。
對于我們的例子赦邻,當(dāng)name為null時髓棋,getName方法返回一個""
字符串。當(dāng)接下來取出name來進(jìn)行操作時就不會出現(xiàn)空指針的異常惶洲。
public String getName() {
if (name == null) {
return "";
} else {
return name;
}
}
另一種是在調(diào)用的時候判斷是否為空按声,對于我們的例子,如下代碼:
if (name != null && name.equals("Jackson")){}
通過判斷name是否為空湃鹊,若非空再判斷name是否等于Jackson儒喊。通過這樣的方式可以解決空指針異常的問題。
但是兩種方案都有一定的缺點币呵,對于第一種方案,String和集合類型的Null對象很好確認(rèn)侨颈,就是空字符串和空集合余赢。但是對于一些特殊的對象,創(chuàng)建Null對象需要更多的步驟哈垢,更多的設(shè)計妻柒。總之就是兩個字耘分,麻煩举塔。
對于第二種方案,這個也是我們最常用的求泰。但是這個方案有一個問題央渣,就是經(jīng)常會忘記判空。對于一個方法other.method(a, b)
,我們常常不會忘記對a和b進(jìn)行判空渴频,而對方法的返回值卻經(jīng)常忘記判空芽丹。而這個空指針異常是在運行時才會被發(fā)現(xiàn),如果空指針的情況十分的罕見卜朗,那么很可能很長一段時間都不會發(fā)現(xiàn)這個異常拔第。我常常碰到的一種情況是當(dāng)數(shù)據(jù)庫中存在臟數(shù)據(jù)的時候,正常的數(shù)據(jù)是不應(yīng)該存在null的场钉,但是即使是生產(chǎn)環(huán)境也很有可能混入一兩個臟數(shù)據(jù)蚊俺,很可能這罕見的臟數(shù)據(jù)導(dǎo)致程序報錯」渫颍可能這個時候再去修復(fù)bug泳猬,代價比開發(fā)階段高很多。
為了優(yōu)雅地處理null的情況,可以使用google guava的Optional暂殖。API不說了价匠,直接上代碼
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Optional<String> getName() {
return Optional.fromNullable(name);
}
public static void main(String[] args) {
Person person = new Person();
Optional<String> optionalName = person.getName();
if (optionalName.isPresent()) {
String name = optionalName.get();
System.out.println("My name is :" + name);
} else {
System.out.println("Who am I?");
}
}
}
在getName里,我們由原來直接返回name呛每,改成返回Optional<String>踩窖。通過Optional.fromNullable(T)靜態(tài)方法,可以創(chuàng)建一個Optional對象晨横。如果傳入的對象是null洋腮,那么isPresent返回false,否則返回true手形。在上訴代碼中啥供,Person的name沒有設(shè)置,optionalName.isPresent()返回的是false库糠,所以打印出的應(yīng)該是Who am I?
伙狐。
從代碼上看,這個和第二種方案瞬欧,對對象判空的方法沒有區(qū)別贷屎,都是先判斷是否存在,然后再對對象進(jìn)行操作艘虎。我起初也是百思不得其解唉侄,后來在在stackoverflow發(fā)現(xiàn)一篇回答,What's the point of Guava's Optional class野建。我個人的感覺是属划,因為我們沒法直接獲取所需的對象,而是獲得Optional對象候生,需要從Optional中獲取我們需要的對象同眯,所以我們就不會忘了判空這步操作了。因為不判空取不到所需對象陶舞,google也是挺會幫程序員克服遺忘的啊嗽测。