NullPointException能夠說是全部java程序員都遇到過的一個異常,雖然java從設計之初就力圖讓程序員脫離指針的苦海,可是指針確實是實際存在的,而java設計者也只能是讓指針在java語言中變得更加簡單、易用,而不能徹底的將其剔除,因此纔有了咱們平常所見到的關鍵字null
。java
空指針異常是一個運行時異常,對於這一類異常,若是沒有明確的處理策略,那麼最佳實踐在於讓程序早點掛掉,可是不少場景下,不是開發人員沒有具體的處理策略,而是根本沒有意識到空指針異常的存在。當異常真的發生的時候,處理策略也很簡單,在存在異常的地方添加一個if語句斷定便可,可是這樣的應對策略會讓咱們的程序出現愈來愈多的null斷定,咱們知道一個良好的程序設計,應該讓代碼中儘可能少出現null關鍵字,而java8所提供的Optional
類則在減小NullPointException的同時,也提高了代碼的美觀度。但首先咱們須要明確的是,它並 不是對null
關鍵字的一種替代,而是對於null斷定提供了一種更加優雅的實現,從而避免NullPointException。程序員
假設咱們須要返回一個字符串的長度,若是不借助第三方工具類,咱們須要調用str.length()
方法:工具
if(null == str) { // 空指針斷定 return 0; } return str.length();
若是採用Optional類,實現以下:this
return Optional.ofNullable(str).map(String::length).orElse(0);
Optional的代碼相對更加簡潔,當代碼量較大時,咱們很容易忘記進行null斷定,可是使用Optional類則會避免這類問題。spa
建立空對象設計
Optional<String> optStr = Optional.empty();
上面的示例代碼調用empty()
方法建立了一個空的Optional<String>
對象型。指針
建立對象:不容許爲空
Optional提供了方法of()
用於建立非空對象,該方法要求傳入的參數不能爲空,不然拋NullPointException
,示例以下:code
Optional<String> optStr = Optional.of(str); // 當str爲null的時候,將拋出NullPointException
建立對象:容許爲空
若是不能肯定傳入的參數是否存在null值的可能性,則能夠用Optional的ofNullable()
方法建立對象,若是入參爲null,則建立一個空對象。示例以下:對象
Optional<String> optStr = Optional.ofNullable(str); // 若是str是null,則建立一個空對象
流式處理也是java8給咱們帶來的一個重量級新特性,讓咱們對集合的操做變得更加簡潔和高效,下一篇關於java8新特性的文章,將對流失處理進行全面的講解。這裏Optional也提供了兩個基本的流失處理:映射和過濾。blog
爲了演示,咱們設計了一個User
類,以下:
/** * @author: zhenchao.Wang 2016-9-24 15:36:56 */ public class User { /** 用戶編號 */ private long id; private String name; private int age; private Optional<Long> phone; private Optional<String> email; public User(String name, int age) { this.name = name; this.age = age; } // 省略setter和getter }
手機和郵箱不是一我的的必須有的,因此咱們利用Optional定義。
映射:map與flatMap
映射是將輸入轉換成另一種形式的輸出的操做,好比前面例子中,咱們輸入字符串,而輸出的是字符串的長度,這就是一種隱射,咱們利用方法map()
得以實現。假設咱們但願得到一我的的姓名,那麼咱們能夠以下實現:
String name = Optional.ofNullable(user).map(User::getName).orElse("no name")
這樣當入參user不爲空的時候則返回其name,不然返回no name
如我咱們但願經過上面方式獲得phone或email,利用上面的方式則行不通了,由於map以後返回的是Optional,咱們把這種稱爲Optional嵌套,咱們必須在map一次才能拿到咱們想要的結果:
long phone = optUser.map(User::getPhone).map(Optional::get).orElse(-1L);
其實這個時候,更好的方式是利用flatMap,一步拿到咱們想要的結果:
long phone = optUser.flatMap(User::getPhone).orElse(-1L);
flapMap能夠將方法返回的各個流扁平化成爲一個流,具體在下一篇專門講流式處理的文章中細說。
過濾:fliter
filiter,顧名思義是過濾的操做,咱們能夠將過濾操做作爲參數傳遞給該方法,從而實現過濾目的,加入咱們但願篩選18週歲以上的成年人,則能夠實現以下:
optUser.filter(u -> u.getAge() >= 18).ifPresent(u -> System.out.println("Adult:" + u));
默認行爲是當Optional爲不知足條件時所執行的操做,好比在上面的例子中咱們使用的orElse()
就是一個默認操做,用於在Optional對象爲空時執行特定操做,固然也有一些默認操做是當知足條件的對象存在時執行的操做。
get()
get用於獲取變量的值,可是當變量不存在時則會拋出NoSuchElementException
,因此若是不肯定變量是否存在,則不建議使用
orElse(T other)
當Optional的變量不知足給定條件時,則執行orElse,好比前面當str爲null時,返回0。
orElseGet(Supplier<? extends X> expectionSupplier)
若是條件不成立時,須要執行相對複雜的邏輯,而不是簡單的返回操做,則可使用orElseGet實現:
long phone = optUser.map(User::getPhone).map(Optional::get).orElseGet(() -> { // do something here return -1L; });
orElseThrow(Supplier<? extends X> expectionSupplier)
與get()方法相似,都是在不知足條件時返回異常,不過這裏咱們能夠指定返回的異常類型。
ifPresent(Consumer<? super T>)
當知足條件時執行傳入的參數化操做。
Optional是一個final類,未實現任何接口,因此當咱們在利用該類包裝定義類的屬性的時候,若是咱們定義的類有序列化的需求,那麼由於Optional沒有實現Serializable接口,這個時候執行序列化操做就會有問題:
public class User implements Serializable{ /** 用戶編號 */ private long id; private String name; private int age; private Optional<Long> phone; // 不能序列化 private Optional<String> email; // 不能序列化
不過咱們能夠採用以下替換策略:
private long phone; public Optional<Long> getPhone() { return Optional.ofNullable(this.phone); }