Java中避免空指針異常的方法
沒人會喜歡空指針異常!有什么方法可以避免它們嗎?或許吧。。
本文將討論到以下幾種技術(shù)
1.Optional類型(Java 8中新引入的)
2.Objects類(Java 7中原有的)
Java 8中的Optional類
它是什么?
1.Java 8中新引入的類型
2.它是作為某個指定類型的對象的包裝器或者用于那些不存在對象(null)的場景
簡單來說,它是處理空值的一個更好的替代品(警告:乍一看可能并沒有那么明顯)
基本用法
它是一種類型(一個類)——那么,怎么才能創(chuàng)建一個這個類型的實例?
使用下它的三個靜態(tài)方法就可以了:
public static Optional<String> stringOptional(String input) {
return Optional.of(input);
}
簡單明了——創(chuàng)建一個包含這個值的Optional包裝器。記住——如果這個值是null的話,它會拋出NPE!
public static Optional<String> stringNullableOptional(String input) {
if (!new Random().nextBoolean()) {
input = null;
}
return Optional.ofNullable(input);
}
我個人認(rèn)為是要更好一點。這樣就不會有NPE的風(fēng)險了——如果輸入為null的話,會返回一個空的Optional。
public static Optional<String> emptyOptional() {
return Optional.empty();
}
如果你真的就是希望返回一個”空"值的話?!翱铡敝挡⒉灰馕吨鴑ull。
好吧,那如何去消費/使用Optional呢?
public static void consumingOptional() {
Optional<String> wrapped = Optional.of("aString");
if (wrapped.isPresent()) {
System.out.println("Got string - " + wrapped.get());
}
else {
System.out.println("Gotcha !");
}
}
簡單的方法就是檢查Optional包裝器是否真的有值(使用isPresent方法)——你會懷疑這和使用if(myObj != null)相比有什么好處。別擔(dān)心,這個我會解釋清楚的。
public static void consumingNullableOptional() {
String input = null;
if (new Random().nextBoolean()) {
input = "iCanBeNull";
}
Optional<String> wrapped = Optional.ofNullable(input);
System.out.println(wrapped.orElse("default"));
}
你可以使用orElse方法,這樣萬一封裝的確實是一個null值的話可以用它來返回一個默認(rèn)值——它的好處顯而易見。在提取出真實值的時候可以避免調(diào)用ifPresent方法這樣明顯多余的方式了。
public static void consumingEmptyOptional() {
String input = null;
if (new Random().nextBoolean()) {
input = "iCanBeNull";
}
Optional<String> wrapped = Optional.ofNullable(input);
System.out.println(wrapped.orElseGet(
() -> {
return "defaultBySupplier";
}
));
}
這個我就有點搞不清楚了。為什么有兩個同樣目的的不同方法?orElse和orElseGet明明可以重載的(同名但不同參數(shù))。
不論如何,這兩個方法明顯的區(qū)別就在于它們的參數(shù)——你可以選擇使用lambda表達(dá)式而不是Supplier的實例來完成這個(一個函數(shù)式接口)
為什么使用Optional要比常見的null檢查強?
1.使用Optional最大的好處就是可以更明白地表述你的意圖——返回null值的話會讓消費者感到疑惑(當(dāng)真的出現(xiàn)NPE的時候)這是不是故意返回的,因此還得查看javadoc來進(jìn)一步定位。而使用Optional就相當(dāng)明了了。
2.有了Optional你就可以徹底避免NPE了——如上所提,使用Optional.ofNullable,orElse以及orElseGet可以讓我們遠(yuǎn)離NPE。
另一個救星!
看下這個代碼片段
package com.abhirockzz.wordpress.npesaviors;
import java.util.Map;
import java.util.Objects;
public class UsingObjects {
String getVal(Map<String, String> aMap, String key) {
return aMap.containsKey(key) ? aMap.get(key) : null;
}
public static void main(String[] args) {
UsingObjects obj = new UsingObjects();
obj.getVal(null, "dummy");
}
}
哪個可能會為空?
1.Map對象
2.進(jìn)行搜索使用的key
3.方法調(diào)用的這個實例
如果拋出NPE的話,我們怎么能確定到底是哪個是null的?
package com.abhirockzz.wordpress.npesaviors;
import java.util.Map;
import java.util.Objects;
public class UsingObjects {
String getValSafe(Map<String, String> aMap, String key) {
Map<String, String> safeMap = Objects.requireNonNull(aMap,
"Map is null");
String safeKey = Objects.requireNonNull(key, "Key is null");
return safeMap.containsKey(safeKey) ? safeMap.get(safeKey) : null;
}
public static void main(String[] args) {
UsingObjects obj = new UsingObjects();
obj.getValSafe(null, "dummy");
}
}
requireNonNull方法
1.如果對象不為null的話就返回它本身
2.如果值為null的話,返回的NPE會帶有指定的消息
為什么比if(myObj!=null)要好?
你所看到的棧跟蹤信息會很清楚地看見Objects.requireNonNull的方法調(diào)用。這個再配合你自己的錯誤日志,可以讓你更快地定位問題。。。至少在我看來是更快。
你還可以自己自義校驗器,比如說實現(xiàn)一個簡單的校驗器來確保沒有空值。
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
public class RandomGist {
public static <T> T requireNonEmpty(T object, Predicate<T> predicate, String msgToCaller){
Objects.requireNonNull(object);
Objects.requireNonNull(predicate);
if (predicate.test(object)){
throw new IllegalArgumentException(msgToCaller);
}
return object;
}
public static void main(String[] args) {
//Usage 1: an empty string (intentional)
String s = "";
System.out.println(requireNonEmpty(Objects.requireNonNull(s), (s1) -> s1.isEmpty() , "My String is Empty!"));
//Usage 2: an empty List (intentional)
List list = Collections.emptyList();
System.out.println(requireNonEmpty(Objects.requireNonNull(list), (l) -> l.isEmpty(), "List is Empty!").size());
//Usage 3: an empty User (intentional)
User user = new User("");
System.out.println(requireNonEmpty(Objects.requireNonNull(user), (u) -> u.getName().isEmpty(), "User is Empty!"));
}
private static class User {
private String name;
public User(String name){
this.name = name;
}
public String getName(){
return name;
}
}
}
不要讓NPE在錯誤的地方成為痛苦。我們有許多工具能更好地處理NPE,甚至徹底地根除它們!
相關(guān)文章
Spring Boot整合MyBatis連接Oracle數(shù)據(jù)庫的步驟全紀(jì)錄
這篇文章主要給大家介紹了關(guān)于Spring Boot整合MyBatis連接Oracle數(shù)據(jù)庫的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07
Spring?Security權(quán)限管理小結(jié)
SpringSecurity是一個權(quán)限管理框架,核心是認(rèn)證和授權(quán),前面已經(jīng)系統(tǒng)的給大家介紹過了認(rèn)證的實現(xiàn)和源碼分析,本文重點來介紹下權(quán)限管理,需要的朋友可以參考下2022-08-08
MyBatis-Plus Sequence主鍵的實現(xiàn)
這篇文章主要介紹了MyBatis-Plus Sequence主鍵的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
spring boot2.0圖片上傳至本地或服務(wù)器并配置虛擬路徑的方法
最近寫了關(guān)于圖片上傳至本地文件夾或服務(wù)器,上傳路徑到數(shù)據(jù)庫,并在上傳時預(yù)覽圖片。本文通過實例代碼給大家分享spring boot2.0圖片上傳至本地或服務(wù)器并配置虛擬路徑的方法,需要的朋友參考下2018-12-12
Retrofit+RxJava實現(xiàn)帶進(jìn)度下載文件
這篇文章主要為大家詳細(xì)介紹了Retrofit+RxJava實現(xiàn)帶進(jìn)度下載文件,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05
Spring?boot集成easy?excel實現(xiàn)導(dǎo)入導(dǎo)出功能
這篇文章主要介紹了Spring?boot集成easy?excel實現(xiàn)導(dǎo)入導(dǎo)出操作,使用easyexcel,首先要引入easyexcel的maven依賴,具體的版本根據(jù)你的需求去設(shè)置,本文結(jié)合實例代碼講解的非常詳細(xì),需要的朋友可以參考下2024-05-05
解決idea報錯 Connot resolve column 的問題
這篇文章主要介紹了解決idea報錯 Connot resolve column 的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02

