NPE : NullPointerExceptionjava
空指針異常是最多見的Java異常之一,拋出NPE錯誤不是用戶操做的錯誤,而是開發人員的錯誤,應該被避免,那麼只能在每一個方法中加入非空檢查,閱讀性和維護性都比較差。程序員
如下是一個常見的嵌套對象:一個用戶所擁有的汽車,以及爲這個汽車配備的保險。安全
public class User {
private String userName;
private Car car;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
}
public class Car {
private String carName;
private Insurance insurance;
public String getCarName() {
return carName;
}
public void setCarName(String carName) {
this.carName = carName;
}
public Insurance getInsurance() {
return insurance;
}
public void setInsurance(Insurance insurance) {
this.insurance = insurance;
}
}
public class Insurance {
private String insuranceName;
public String getInsuranceName() {
return insuranceName;
}
public void setInsuranceName(String insuranceName) {
this.insuranceName = insuranceName;
}
}
複製代碼
若是咱們此時,須要獲取一個用戶對應的汽車保險名稱,咱們可能會寫出來如下的代碼bash
private String getInsuranceName(User user) {
return user.getCar().getInsurance().getInsuranceName();
}
複製代碼
顯然上面的程序是存在諸多NullPointerException隱患的,爲了保證程序的健壯性,咱們須要儘可能避免出現空指針NullPointerException,那麼一般咱們會有如下兩種寫法。app
private String getInsuranceName(User user) {
if (user != null) {
Car car = user.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
return insurance.getInsuranceName();
}
}
}
return "not found";
}
複製代碼
private String getInsuranceName(User user) {
if (user == null) {
return "not found";
}
Car car = user.getCar();
if (car == null) {
return "not found";
}
Insurance insurance = car.getInsurance();
if (insurance == null) {
return "not found";
}
return insurance.getInsuranceName();
}
複製代碼
爲了不出現空指針,咱們一般會採用以上兩種寫法,可是它們複雜又冗餘,爲了鼓勵程序員寫更乾淨的代碼,代碼設計變得更加的優雅。JAVA8提供了Optional類來優化這種寫法。函數
Java 8中引入了一個新的類java.util.Optional。這是一個封裝Optional值的類。舉例來講,使用新的類意味着,若是你知道一我的可能有也可能沒有車,那麼User類內部的car變量就不該該聲明爲Car, 遇某人沒有車時把null引用值給它,而是應該以下圖所示直接將其聲明爲Optional類型。優化
變量存在時,Optional類只是對類簡單封裝。變量不存在時,缺失的值會被建模成一個「空」 的Optional對象,由方法Optional.empty()返回。它返回Optional類的特定單一實例。ui
null引用和Optional.empty() 有什麼本質的區別嗎?this
從語義上,你能夠把它們看成一回事兒,可是實際中它們之間的差異很是大:若是你嘗試直接引用一個null,必定會觸發NullPointerException,不過使用 Optional.empty()就徹底沒事兒,它是Optional類的一個有效對象。spa
使用Optional而不是null的一個很是重要而又實際的語義區別是,第一個例子中,咱們在聲明變量時使用的是Optional類型,而不是Car類型,這句聲明很是清楚地代表了這裏發生變量缺失是容許的。與此相反,使用Car這樣的類型,可能將變量賦值爲null,你只能依賴你對業務模型的理解,判斷一個null是否屬於該變量的有效值又或是異常狀況。
public class User {
private String userName;
private Optional<Car> car;
public String getUserName() {
return userName;
}
public Optional<Car> getCar() {
return car;
}
}
public class Car {
private String carName;
private Optional<Insurance> insurance;
public String getCarName() {
return carName;
}
public Optional<Insurance> getInsurance() {
return insurance;
}
}
public class Insurance {
private String insuranceName;
public String getInsuranceName() {
return insuranceName;
}
}
複製代碼
發現Optional是如何 富你模型的語義了吧。代碼中user引用的是Optional, 而car引用的是Optional,這種方式很是清晰地表達了你的模型中一個user 可能有也可能沒有car的情形,一樣,car可能進行了保險,也可能沒有保險。
與此同時,咱們看到insurance的名稱insuranceName被聲明成String類型,而不是Optional,這很是清楚地代表聲明爲insurance的類中的名稱字段insuranceName是必須存在的。
使用這種方式, 一旦經過引用insurance獲取insuranceName時發生NullPointerException,你就能很是肯定地知道出錯的緣由,再也不須要爲其添加null的檢查查,由於null的檢查查只會掩蓋問題,並未真正地修復問題。
insurance必須有個名字,因此,若是你遇到一個insurance沒有名稱,你須要調查你的數據出了什麼問題,而不該該再添加一段代碼,將這個問題隱藏。
若是構造參數是一個null,這段代碼會當即 出一個NullPointerException,而不是等到你 圖訪問car的屬性值時才返回一個錯誤。
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
複製代碼
建立一個容許null值的Optional對象
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
複製代碼
建立一個空的Optional對象
public static<T> Optional<T> empty() {
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
複製代碼
注意:orElse(T other)和orElseGet(Supplier<? extends T> other)的區別
這兩個函數的區別:當value值不爲null時,orElse函數依然會執行返回T的方法,而orElseGet函數並不會執行返回T的方法。
能夠把Optional對象當作一種特殊的集合數據,它至多包含一個元素。若是Optional包含一個值,那函數就將該值做爲參數傳遞給map,對該值進行轉換。若是Optional爲空,就什麼也不作。
String optionMap = Optional.ofNullable("abc").map(value -> value.toUpperCase()).get();
複製代碼
將兩層的optional合併爲一個
String optionFlatMap = Optional.ofNullable("abc").flatMap(value -> Optional.of((value + "flat-map").toUpperCase())).get();
複製代碼
filter方法接受一個謂詞做爲參數。若是Optional對象的值存在,而且它符合謂詞的條件, filter方法就返回其值;不然它就返回一個空的Optional對象。
Optional<String> filterOptional = Optional.ofNullable("abc").filter(value -> Objects.equals(value, "abc"));
複製代碼
嘗試獲取用戶的用戶名稱,不存在則返回默認值
String userName = Optional.ofNullable(userOfNull).orElse(new User()).getUserName();
複製代碼
嘗試獲取用戶的carName,不存在則返回null
String carName = Optional.ofNullable(userOfNull).map(u -> u.getCar()).map(c -> c.getCarName()).orElse(null);
複製代碼
用戶名存在的時候轉爲大寫
Optional.ofNullable(user).map(u -> u.getUserName()).ifPresent(userName -> System.out.println(userName.toUpperCase()));
複製代碼
過濾出來用戶名稱是張三的用戶
Optional.ofNullable(user).filter(u -> Objects.equals(u.getUserName(),"張三")).map(u -> u.getUserName()).ifPresent(userName -> System.out.println(userName + "實戰Test"));
複製代碼
將張三的用戶名稱更改成李四
Optional.ofNullable(user).ifPresent(x -> {
if (Objects.equals(user.getUserName(),"張三")){
user.setUserName("李四");
}
});
Optional.ofNullable(user).filter(u -> Objects.equals(user.getUserName(),"張三")).ifPresent(x -> user.setUserName("李四"));
複製代碼