Java中避免空指標的幾個方法!
theme: vuepress
前言
Java 程式設計中哪個異常是你印象最深刻的,那 NullPointerException
空指標可以說是臭名昭著的。不要說初級程式設計師會碰到,即使是中級,專家級程式設計師稍不留神,就會掉入這個坑裡。
Java 中任何物件都有可能為空,當我們呼叫空物件的方法時就會丟擲 NullPointerException
空指標異常,這是一種非常常見的錯誤型別。我們可以使用若干種方法來避免產生這類異常,使得我們的程式碼更為健壯。
空指標出現的幾種情況
1、呼叫了空物件的例項方法
```java public class Person { // 屬性 private String name; private int age;
public void print(){
System.out.println("This is Person Class");
}
public String read(){
System.out.println("This is person read");
return null;
}
public static void main(String[] args)
{
Person person = null;
//呼叫了空物件的例項方法
person.print();
}
} ```
2、訪問了空物件的屬性
java
Person person = null;
//呼叫了空物件的屬性
System.out.println(person.age);
3、當陣列是一個空物件時,取它的長度
java
Person person = null;
//當陣列是一個空物件時,取它的長度
System.out.println(person.name.length());
4、方法的返回值是 null, 呼叫方直接去使用
java
Person person = new Person();
//方法的返回值是 null, 呼叫方直接去使用
System.out.println(person.read().contains("Java"));
如何從根源避免空指標
1、使用之前一定要初始化,或檢查是否初始化;
2、儘量避免在函式中返回 NULL, 或給出詳細的註釋;
3、外部傳值,除非有明細的說明,否則,一定要判斷是否為 NULL。
equals和equalsIgnoreCase()方法
Object類中的equals 方法在非空物件引用上實現相等關係,具有對稱性 x.equals(y) 和 y.equals(x) 結果是一樣的,但當x == null時會丟擲空指標異常。
例如:
```java String x = null; String y = "world"; if(x.equals(y)){ // java.lang.NullPointerException
}
if(y.equals(x)){//即便 x 是 null也能避免 NullPointerException
} ```
所以我們要把確定不為null的物件或值放在前面。
valueOf()和toString()
呼叫null物件的toString()
會丟擲空指標異常,使用valueOf()
可以獲得相同的值,傳遞一個 null 給 valueOf() 將會返回null。尤其是在那些包裝類,像Integer、Float、Double和BigDecimal。
例如:
java
Integer i = null;
System.out.println(i.toString()); // 丟擲NullPointerException異常
System.out.println(String.valueOf(i)); // 返回 null不會出現異常
Optional 型別
Java 8 引入了 Optional
在業務系統中,物件中巢狀物件是經常發生的場景,如下示例程式碼:
java
// 最外層物件
class Outer {
Nested nested;
Nested getNested() {
return nested;
}
}
// 第二層物件
class Nested {
Inner inner;
Inner getInner() {
return inner;
}
}
// 最底層物件
class Inner {
String foo;
String getFoo() {
return foo;
}
}
業務中,假設我們需要獲取 Outer
物件對底層的 Inner
中的 foo
屬性,我們必須寫一堆的非空校驗,來防止發生 NullPointerException
:
java
// 繁瑣的程式碼
Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {
System.out.println(outer.nested.inner.foo);
}
在 Java8 中,我們有更優雅的解決方式,那就是使用 Optional
是說,我們可以在一行程式碼中,進行流水式的 map
操作。而 map 方法內部會自動進行空校驗:
java
Optional.of(new Outer())
.map(Outer::getNested)
.map(Nested::getInner)
.map(Inner::getFoo
.ifPresent(System.out::println); // 如果不為空,最終輸出 foo 的值
suppiler 函式自定義增強 API
我們可以利用 suppiler
函式來出一個終極解決方案:
java
public static <T> Optional<T> resolve(Supplier<T> resolver) {
try {
T result = resolver.get();
return Optional.ofNullable(result);
}
catch (NullPointerException e) {
// 可能會丟擲空指標異常,直接返回一個空的 Optional 物件
return Optional.empty();
}
}
利用上面的 resolve
方法來重構上述的非空校驗程式碼段:
java
Outer obj = new Outer();
// 直接呼叫 resolve 方法,內部做空指標的處理
resolve(() -> obj.getNested().getInner().getFoo());
.ifPresent(System.out::println); // 如果不為空,最終輸出 foo 的值
使用null安全的方法和庫
有很多開源庫已經為您做了繁重的空指標檢查工作。其中最常用的一個的是Apache commons 中的StringUtils
。你可以使用StringUtils.isBlank()
,isNumeric()
,isWhiteSpace()
以及其他的工具方法而不用擔心空指標異常。
```java //StringUtils方法是空指標安全的,他們不會丟擲空指標異常 System.out.println(StringUtils.isEmpty(null)); System.out.println(StringUtils.isBlank(null)); System.out.println(StringUtils.isNumeric(null)); System.out.println(StringUtils.isAllUpperCase(null));
輸出: true true false false ```
斷言
斷言是用來檢查程式的安全性的,在使用之前進行檢查條件,如果不符合條件就報異常,符合就繼續。
Java 中自帶的斷言關鍵字:assert,如:
java
assert name == null : "名稱不能為空";
輸出:
java
Exception in thread "main" java.lang.AssertionError: 名稱不正確
不過預設是不啟動斷言檢查的,需要要帶上 JVM 引數:-enableassertions 才能生效。
Java 中這個用的很少,建議使用 Spring 中的,更強大,更方便好用。
Spring中的用法:
java
Assert.notNull(name,"名稱不能為空");
建立返回空集合而不是null的方法
一個非常好的技術是建立返回一個空集合的方法,而不是一個null值。你的應用程式的程式碼可以遍歷空集合並使用它的方法和欄位,而不會丟擲一個NullPointerException。Collections類提供了方便的空 List,Set 和 Map: Collections.EMPTY_LIST
,Collections.EMPTY_SET
,Collections.EMPTY_MAP
。
例如:
```java
public class Example {
private static List
public static List
賦值時自動拆箱出現空指標
1、變數賦值自動拆箱出現的空指標
java
Long i = null;
//變數賦值自動拆箱出現的空指標
long j = i;
Long 自動拆箱為 long 實質是呼叫了 longValue 方法,由於以上的 Long 型別變數值為 null,自動拆箱就會報 NPE。
2、方法傳參時自動拆箱引起空指標異常
```java public static int add(int x, int y) { return x + y; }
public static void main(String[] args) { Integer left = null; Integer right = null; //方法傳參時自動拆箱引起空指標異常 System.out.println(add(left, right)); } ```
以上自動拆箱報 NPE,原因在於裝箱的值為 null,呼叫 null 的任何 方法都會報錯。
規避指南:
- 基本資料型別優於包裝器型別,優先考慮使用基本型別。
- 對於不確定的包裝器型別,一定要校驗是否為 NULL。
基本資料型別比包裝器型別更加節省時間與空間。
結語
關於怎麼有效避免空指標,其實還有更多,如何避免空指標,一是要注意程式碼編寫規範,二是要提高程式碼素養。我們一起加油~
- 一文弄懂Java中執行緒池原理
- 聊聊工作中,如何提升自己的程式設計能力?
- 聊聊MQ,如何避免訊息丟失?如何避免重複消費?
- Java實現串列埠通訊
- SpringBoot為什麼可以使用Jar包啟動?
- 2021,平凡的一年!
- 聊聊 Java 中引數傳遞的原理!
- 聊聊Pulsar,一款非常優秀的訊息中介軟體!!!
- 乾貨,聊聊Java中String類!!!
- 萬字!Java相關的學習資料整理(乾貨滿滿)
- 使用 ArrayList 應當避免的坑
- Java中避免空指標的幾個方法!
- 聊聊Jhipster,強烈推薦Java開發看看,節省很多時間!!!
- 給程式設計師新手的一些建議
- 計算機基礎知識:磁碟分割槽
- Java中的序列化
- Java中的控制流程語句詳解
- 程式設計師必備:Git入門,超詳細
- netty系列:使用 SSL/TLS 加密 Netty 程式
- 聊聊MySQL儲存引擎中索引如何落地?