亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

利用Java8 Optional如何避免空指針異常詳解

2024-07-13 10:16:30
字體:
來源:轉載
供稿:網友

前言

空指針是我們最常見也最討厭的異常,為了防止空指針異常,你不得在代碼里寫大量的非空判斷。

Java 8引入了一個新的Optional類。用于避免空指針的出現,也無需在寫大量的if(obj!=null)這樣的判斷了,前提是你得將數據用Optional裝著,它就是一個包裹著對象的容器。

都說沒有遇到過空指針異常的程序員不是Java程序員,null確實引發過很多問題。Java 8中引入了一個叫做java.util.Optional的新類可以避免null引起的諸多問題。

我們看看一個null引用能導致哪些危害。首先創建一個類Computer,結構如下圖所示:

Java8,Optional,避免空指針異常,java空指針異常,java空指針異常解決

當我們調用如下代碼會怎樣?

String version = computer.getSoundcard().getUSB().getVersion();

上述代碼看似是沒有問題的,但是很多計算機(比如,樹莓派)其實是沒有聲卡的,那么調用getSoundcard()方法可定會拋出空指針異常了。

一個常規的但是不好的的方法是返回一個null引用來表示計算機沒有聲卡,但是這就意味著會對一個空引調用getUSB()方法,顯然會在程序運行過程中拋出控制異常,從而導致程序停止運行。想想一下,當你的程序在客戶端電腦上運行時,突然出現這種錯是多尷尬的一件事?

偉大計算機科學Tony Hoare曾經寫到:"我認為null引用從1965年被創造出來導致了十億美元的損失。當初使用null引用對我最大的誘惑就是它實現起來方便。"

那么該怎么避免在程序運行時會出現空指針異常呢?你需要保持警惕,并且不斷檢查可能出現空指針的情況,就像下面這樣:

String version = "UNKNOWN";if(computer != null) { Soundcard soundcard = computer.getSoundcard(); if(soundcard != null){  USB usb = soundcard.getUSB();  if(usb != null){   version = usb.getVersion();  }  } }

然而,你可以看到上述代碼有太多的null檢查,整個代碼結構變得非常丑陋。但是我們又不得不通過這樣的判斷來確保系統運行時不會出現空指針。如果在我們的業務代碼中出現大量的這種空引用判斷簡直讓人惱火,也導致我們代碼的可讀性會很差。

如果你忘記檢查要給值是否為空,null引用也是存在很大的潛在問題。這篇文章我將證明使用null引用作為值不存在的表示是不好的方法。我們需要一個更好的表示值不存在的模型,而不是再使用null引用。

Java 8引入了一個新類叫做java.util.Optional<T> ,這個類的設計的靈感來源于Haskell語言和Scala語言。這個類可以包含了一個任意值,像下面圖和代碼表示的那樣。你可以把Optional看做是一個有可能包含了值的值,如果Optional不包含值那么它就是空的,下圖那樣。

Java8,Optional,避免空指針異常,java空指針異常,java空指針異常解決

public class Computer { private Optional<Soundcard> soundcard; public Optional<Soundcard> getSoundcard() { ... } ...}public class Soundcard { private Optional<USB> usb; public Optional<USB> getUSB() { ... }}public class USB{ public String getVersion(){ ... }}

上述代碼展現了一臺計算機有可能包換一個聲卡(聲卡是有可能存在也有可能不存在)。聲卡也是有可能包含一個USB端口的。這是一種改善方法,該模型可以更加清晰的反映一個被給定的值是可以不存在的。

但是該怎么處理Optional<Soundcard>這個對象呢?畢竟,你想要獲取的是USB的端口號。很簡單,Optional類包含了一些方法來處理值是否存在的狀況。和null引用相比Optional類迫使你在你要做值是否相關處理,從而避免了空指針異常。

需要說明的是Optional類并不是要取代null引用。相反地,是為了讓設計的API更容易被理解,當你看到一個函數的簽名時,你就可以判斷要傳遞給這個函數的值是不是有可能不存在。這就促使你要打開Optional類來處理確實值的狀況了。

采用Optional模式

啰嗦了這么多,來看一些代碼吧!我們先看一下怎么使用Optional改寫傳統的null引用檢測后是什么樣子。在這邊文章的末尾你將會明白怎么使用Optional。

String name = computer.flatMap(Computer::getSoundcard)       .flatMap(Soundcard::getUSB)       .map(USB::getVersion)       .orElse("UNKNOWN");

創建Optional對象

可以創建一個空的Optional對象:

Optional<Soundcard> sc = Optional.empty();

接下來是創建一個包含非null值的Optional:

SoundCard soundcard = new Soundcard();Optional<Soundcard> sc = Optional.of(soundcard);

如果聲卡null,空指針異常會立即被拋出(這比在獲取聲卡屬性時才拋出要好)。

通過使用ofNullable,你可以創建一個可能包含null引用的Optional對象:

Optional<Soundcard> sc = Optional.ofNullable(soundcard);

如果聲卡是null 引用,Optional對象就是一個空的。

對Optional中的值的處理

既然現在已經有了Optional對象,你可以調用相應的方法來處理Optional對象中的值是否存在。和進行null檢測相比,我們可以使用ifPresent()方法,像下面這樣:

Optional<Soundcard> soundcard = ...;soundcard.ifPresent(System.out::println);

這樣就不必再做null檢測,如果Optional對象是空的,那么什么信息將不會打印出來。

你也可以使用isPresent()方法查看Optional對象是否真的存在。另外,還有一個get()方法可以返回Optional對象中的包含的值,如果存在的話。否則會拋出一個NoSuchElementException異常。這兩個方式可以像下面這樣搭配起來使用,從而避免異常:

if(soundcard.isPresent()){ System.out.println(soundcard.get());}

但是這種方式不推薦使用(它和null檢測相比沒有什么改進),下面我們將會探討一下工作慣用的方式。

返回默認值和相關操作

當遇到null時一個常規的操作就是返回一個默認值,你可以使用三元表達式來實現:

Soundcard soundcard = maybeSoundcard != null ? maybeSoundcard : new Soundcard("basic_sound_card");

使用Optional對象的話,你可以orElse()使用重寫,當Optional是空的時候orElse()可以返回一個默認值:

Soundcard soundcard = maybeSoundcard.orElse(new Soundcard("defaut"));

類似地,當Optional為空的時候也可以使用orElseThrow()拋出異常:

Soundcard soundcard =  maybeSoundCard.orElseThrow(IllegalStateException::new);

使用filter過濾特定的值

我們常常會調用一個對象的方法來判斷它的一下屬性。比如,你可能需要檢測USB端口號是否是某個特定值。為了安全起見,你需要檢查指向USB的醫用是否是null,然后再調用getVersion()方法,像下面這樣:

USB usb = ...;if(usb != null && "3.0".equals(usb.getVersion())){ System.out.println("ok");}

如果使用Optional的話可以使用filter函數重寫:

Optional<USB> maybeUSB = ...;maybeUSB.filter(usb -> "3.0".equals(usb.getVersion())     .ifPresent(() -> System.out.println("ok"));

filter方法需要一個predicate對向作為參數。如果Optional中的值存在并且滿足predicate,那么filter函數將會返回滿足條件的值;否則,會返回一個空的Optional對象。

使用map方法進行數據的提取和轉化

一個常見的模式是提取一個對象的一些屬性。比如,對于一個Soundcard對象,你可能需要獲取它的USB對象,然后判斷它的的版本號。通常我們的實現方式是這樣的:

if(soundcard != null){ USB usb = soundcard.getUSB(); if(usb != null && "3.0".equals(usb.getVersion()){ System.out.println("ok"); }}

我們可以使用map方法重寫這種檢測null,然后再提取對象類型的對象。

Optional<USB> usb = maybeSoundcard.map(Soundcard::getUSB);

這個和使用stream的map函數式一樣的。使用stream需要給map函數傳遞一個函數作為參數,這個傳遞進來的函數將會應用于stream中的每個元素。當stream時空的時候,什么也不會發生。

Optional中包含的值將會被傳遞進來的函數轉化(這里是一個從聲卡中獲取USB的函數)。如果Optional對象時空的,那么什么也不會發生。

然后,我們結合map方法和filter方法過濾掉USB的版本號不是3.0的聲卡。

maybeSoundcard.map(Soundcard::getUSB)  .filter(usb -> "3.0".equals(usb.getVersion())  .ifPresent(() -> System.out.println("ok"));

這樣我們的代碼開始變得像有點像開始我們給出的樣子,沒有了null檢測。

使用flatMap函數傳遞Optional對象

現在已經介紹了一個可以使用Optional重構代碼的例子,那么我們應該如何使用安全的方式實現下面代碼呢?

String version = computer.getSoundcard().getUSB().getVersion();

注意上面的代碼都是從一個對象中提取另一個對象,使用map函數可以實現。在前面的文章中我們設置了Computer中包含的是一個Optional<Soundcard>對象,Soundcard包含的是一個Optional<USB>對象,因此我們可以這么重構代碼

String version = computer.map(Computer::getSoundcard)     .map(Soundcard::getUSB)     .map(USB::getVersion)     .orElse("UNKNOWN");

不幸的是,上面的代碼會編譯錯誤,那么為什么呢?computer變量是Optional<Computer>類型的,所以它調用map函數是沒有問題的。但是getSoundcard()方法返回的是一個Optional<Soundcard>的對象,返回的是Optional<Optional<Soundcard>>類型的對象,進行了第二次map函數的調用,結果調用getUSB()函數就變成非法的了。

下面的圖描述了這種場景:

Java8,Optional,避免空指針異常,java空指針異常,java空指針異常解決

map函數的源碼實現是這樣的:

 public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {  Objects.requireNonNull(mapper);  if (!isPresent())   return empty();  else {   return Optional.ofNullable(mapper.apply(value));  } }

可以看出map函數還會再調用一次Optional.ofNullable() , 從而導致返回Optional<Optional<Soundcard>>
Optional提供了flatMap這個函數,它的設計意圖是當對Optional對象的值進行轉化(就像map操作)然后一個兩級Optional壓縮成一個。下面的圖展示了Optional對象通過調用map和flatMap進行類型轉化的不同:

Java8,Optional,避免空指針異常,java空指針異常,java空指針異常解決

因此我們可以這樣寫:

String version = computer.flatMap(Computer::getSoundcard)     .flatMap(Soundcard::getUSB)     .map(USB::getVersion)     .orElse("UNKNOWN");

第一個flatMap保證了返回的是Optional<Soundcard>而不是Optional<Optional<Soundcard>> ,第二個flatMap實現了同樣的功能從而返回的是 Optional<USB> 。注意第三次調用了map() ,因為getVersion()返回的是一個String對象而不是一個Optional對象。

我們終于把剛開始使用的嵌套null檢查的丑陋代碼改寫了可讀性高的代碼,也避免了空指針異常的出現的代碼。

總結

在這片文章中我們采用了Java 8提供的新類java.util.Optional<T> 。這個類的初衷不是要取代null引用,而是幫助設計者設計出更好的API,只要讀到函數的簽名就可知道該函數是否接受一個可能存在也可能不存在的值。另外,Optional迫使你去打開Optional,然后處理值是否存在,這就使得你的代碼避免了潛在的空指針異常。

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩欧美精品网站| 欧美激情在线有限公司| 狠狠操狠狠色综合网| 疯狂蹂躏欧美一区二区精品| 中文字幕欧美亚洲| 亚洲另类xxxx| 2019中文字幕在线观看| 国产综合视频在线观看| 韩国精品美女www爽爽爽视频| 少妇久久久久久| 91久久精品国产91久久性色| 人九九综合九九宗合| 91热精品视频| 欧美激情视频在线免费观看 欧美视频免费一| 亚洲欧美日韩精品久久奇米色影视| 国产精品久久久久久久7电影| 成人情趣片在线观看免费| 欧美一级片免费在线| 亚洲香蕉在线观看| 国产亚洲精品va在线观看| 欧美成人免费va影院高清| 国产综合在线观看视频| 91在线|亚洲| 欧美洲成人男女午夜视频| 亚洲男子天堂网| 亚洲最大激情中文字幕| 国产亚洲欧美日韩精品| 九九热在线精品视频| 欧美与黑人午夜性猛交久久久| 日韩精品在线视频美女| 国产91精品黑色丝袜高跟鞋| 51ⅴ精品国产91久久久久久| 欧美激情中文字幕在线| 超碰精品一区二区三区乱码| 一区二区三区视频免费在线观看| 国产日本欧美一区| 91免费在线视频| 成人欧美一区二区三区在线湿哒哒| 午夜免费久久久久| 精品magnet| 成人激情视频免费在线| 久久久久久久久中文字幕| 国产乱人伦真实精品视频| 欧美激情videoshd| 国产精品福利在线| 国产精品亚发布| 亚洲国产日韩欧美在线图片| 国产亚洲欧美一区| 尤物tv国产一区| 日韩**中文字幕毛片| 亚洲男人第一网站| 国产91精品久久久久久| 日本精品免费一区二区三区| 久久人人爽亚洲精品天堂| 国产精品毛片a∨一区二区三区|国| 午夜精品一区二区三区av| 国产一区二区日韩精品欧美精品| 欧美成人剧情片在线观看| 两个人的视频www国产精品| 欧美日韩免费区域视频在线观看| 欧美国产日韩一区二区三区| 亚洲欧美日韩高清| 日韩网站免费观看高清| 91中文字幕一区| 97人人模人人爽人人喊中文字| 亚洲午夜精品久久久久久久久久久久| 成人激情视频在线播放| 91精品在线一区| www欧美日韩| 亚洲精品资源在线| 91国内揄拍国内精品对白| 欧美成人免费一级人片100| 日韩电影中文 亚洲精品乱码| 色樱桃影院亚洲精品影院| 欧美一区二区大胆人体摄影专业网站| 欧美国产日韩中文字幕在线| 九九热精品视频| 精品亚洲va在线va天堂资源站| 国产精品欧美日韩| 国产日韩中文字幕在线| 欧美刺激性大交免费视频| 国外成人免费在线播放| 久久激情视频久久| 色99之美女主播在线视频| 亚洲精品videossex少妇| 亚洲欧美国产日韩天堂区| 成人激情视频在线播放| 国产视频久久久久久久| 国产v综合v亚洲欧美久久| 国产成人综合久久| 美女福利视频一区| 亚洲国产成人精品久久| 日本一区二区不卡| 国产精品影院在线观看| 日韩av网站电影| 欧美日韩国产色| 91夜夜未满十八勿入爽爽影院| 欧美专区国产专区| 日韩综合中文字幕| 国产精品久久久久久久久粉嫩av| 亚洲最大的免费| 欧美裸体男粗大视频在线观看| 美女精品视频一区| 日本91av在线播放| 国产精品永久免费观看| 美女精品视频一区| 国产精品观看在线亚洲人成网| 91高清在线免费观看| 国产精品日韩久久久久| 91精品国产91久久久久久久久| 久久中文字幕视频| 日韩在线观看网址| 国产精品久久久久久五月尺| 国产香蕉一区二区三区在线视频| 久久久亚洲精选| 日韩中文字幕在线看| 78m国产成人精品视频| 日韩av免费在线| 91在线观看免费高清完整版在线观看| 久久精品一本久久99精品| 亚洲精品视频久久| 欧美精品成人在线| 欧美一性一乱一交一视频| 在线电影中文日韩| 亚洲欧美日韩第一区| 久久国产精品久久久久| 久久久久久久久久国产| 亚洲天堂av在线免费| 色琪琪综合男人的天堂aⅴ视频| 91系列在线观看| 欧美亚洲另类视频| 欧洲永久精品大片ww免费漫画| 亚洲男人天堂九九视频| 日韩av片电影专区| 欧美日韩福利视频| 国产亚洲成精品久久| 91视频九色网站| 中文国产成人精品久久一| 日韩在线视频一区| 欧洲成人午夜免费大片| 亚洲一级黄色片| 欧美精品久久久久久久久久| 国内精品久久久久影院优| 国产精品一区二区在线| 久久免费视频这里只有精品| 中文字幕国产亚洲| 亚洲成av人乱码色午夜| 中文字幕亚洲欧美日韩高清| 91久久国产综合久久91精品网站| 亚洲美女在线视频| 富二代精品短视频| 国产亚洲美女精品久久久| 2019精品视频| 日本欧美中文字幕| 亚洲尤物视频网| 成人高h视频在线| 亚洲欧美国产视频| 欧洲成人在线观看| 色哟哟入口国产精品| 精品国产一区二区三区久久久| 亚洲天堂av在线免费| 91精品久久久久久综合乱菊| 欧美高清性猛交| 久久国产精品久久精品|