Java中避免空指針的幾種方法解析
前言
Java 編程中哪個(gè)異常是你印象最深刻的,那 NullPointerException 空指針可以說是臭名昭著的。不要說初級(jí)程序員會(huì)碰到,即使是中級(jí),專家級(jí)程序員稍不留神,就會(huì)掉入這個(gè)坑里。
Java 中任何對(duì)象都有可能為空,當(dāng)我們調(diào)用空對(duì)象的方法時(shí)就會(huì)拋出 NullPointerException 空指針異常,這是一種非常常見的錯(cuò)誤類型。我們可以使用若干種方法來避免產(chǎn)生這類異常,使得我們的代碼更為健壯。
空指針出現(xiàn)的幾種情況
1、調(diào)用了空對(duì)象的實(shí)例方法
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;
//調(diào)用了空對(duì)象的實(shí)例方法
person.print();
}
}2、訪問了空對(duì)象的屬性
Person person = null; //調(diào)用了空對(duì)象的屬性 System.out.println(person.age);
3、當(dāng)數(shù)組是一個(gè)空對(duì)象時(shí),取它的長(zhǎng)度
Person person = null; //當(dāng)數(shù)組是一個(gè)空對(duì)象時(shí),取它的長(zhǎng)度 System.out.println(person.name.length());
4、方法的返回值是 null, 調(diào)用方直接去使用
Person person = new Person();
//方法的返回值是 null, 調(diào)用方直接去使用
System.out.println(person.read().contains("Java"));
如何從根源避免空指針
1、使用之前一定要初始化,或檢查是否初始化;
2、盡量避免在函數(shù)中返回 NULL, 或給出詳細(xì)的注釋;
3、外部傳值,除非有明細(xì)的說明,否則,一定要判斷是否為 NULL。
equals和equalsIgnoreCase()方法
Object類中的equals 方法在非空對(duì)象引用上實(shí)現(xiàn)相等關(guān)系,具有對(duì)稱性 x.equals(y) 和 y.equals(x) 結(jié)果是一樣的,但當(dāng)x == null時(shí)會(huì)拋出空指針異常。
例如:
String x = null;
String y = "world";
if(x.equals(y)){ // java.lang.NullPointerException
}
if(y.equals(x)){//即便 x 是 null也能避免 NullPointerException
}所以我們要把確定不為null的對(duì)象或值放在前面。
valueOf()和toString()
調(diào)用null對(duì)象的toString()會(huì)拋出空指針異常,使用valueOf()可以獲得相同的值,傳遞一個(gè) null 給 valueOf() 將會(huì)返回null。尤其是在那些包裝類,像Integer、Float、Double和BigDecimal。
例如:
Integer i = null; System.out.println(i.toString()); // 拋出NullPointerException異常 System.out.println(String.valueOf(i)); // 返回 null不會(huì)出現(xiàn)異常
Optional 類型
Java 8 引入了 Optional 類型,我們可以用它來對(duì)函數(shù)的返回值進(jìn)行包裝。這種方式的優(yōu)點(diǎn)是可以明確定義該方法是有可能返回空值的,因此調(diào)用方必須做好相應(yīng)處理,這樣也就不會(huì)引發(fā)空指針異常。但是,也不可避免地需要編寫更多代碼,而且會(huì)產(chǎn)生很多垃圾對(duì)象,增加 GC 的壓力,因此在使用時(shí)需要酌情考慮。
在業(yè)務(wù)系統(tǒng)中,對(duì)象中嵌套對(duì)象是經(jīng)常發(fā)生的場(chǎng)景,如下示例代碼:
// 最外層對(duì)象
class Outer {
Nested nested;
Nested getNested() {
return nested;
}
}
// 第二層對(duì)象
class Nested {
Inner inner;
Inner getInner() {
return inner;
}
}
// 最底層對(duì)象
class Inner {
String foo;
String getFoo() {
return foo;
}
}業(yè)務(wù)中,假設(shè)我們需要獲取 Outer 對(duì)象對(duì)底層的 Inner 中的 foo 屬性,我們必須寫一堆的非空校驗(yàn),來防止發(fā)生 NullPointerException:
// 繁瑣的代碼
Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {
System.out.println(outer.nested.inner.foo);
}
在 Java8 中,我們有更優(yōu)雅的解決方式,那就是使用 Optional是說,我們可以在一行代碼中,進(jìn)行流水式的 map 操作。而 map 方法內(nèi)部會(huì)自動(dòng)進(jìn)行空校驗(yàn):
Optional.of(new Outer())
.map(Outer::getNested)
.map(Nested::getInner)
.map(Inner::getFoo
.ifPresent(System.out::println); // 如果不為空,最終輸出 foo 的值suppiler 函數(shù)自定義增強(qiáng) API
我們可以利用 suppiler 函數(shù)來出一個(gè)終極解決方案:
public static <T> Optional<T> resolve(Supplier<T> resolver) {
try {
T result = resolver.get();
return Optional.ofNullable(result);
}
catch (NullPointerException e) {
// 可能會(huì)拋出空指針異常,直接返回一個(gè)空的 Optional 對(duì)象
return Optional.empty();
}
}利用上面的 resolve 方法來重構(gòu)上述的非空校驗(yàn)代碼段:
Outer obj = new Outer();
// 直接調(diào)用 resolve 方法,內(nèi)部做空指針的處理
resolve(() -> obj.getNested().getInner().getFoo());
.ifPresent(System.out::println); // 如果不為空,最終輸出 foo 的值使用null安全的方法和庫(kù)
有很多開源庫(kù)已經(jīng)為您做了繁重的空指針檢查工作。其中最常用的一個(gè)的是Apache commons 中的StringUtils。你可以使用StringUtils.isBlank(),isNumeric(),isWhiteSpace()以及其他的工具方法而不用擔(dān)心空指針異常。
//StringUtils方法是空指針安全的,他們不會(huì)拋出空指針異常 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
斷言
斷言是用來檢查程序的安全性的,在使用之前進(jìn)行檢查條件,如果不符合條件就報(bào)異常,符合就繼續(xù)。
Java 中自帶的斷言關(guān)鍵字:assert,如:
assert name == null : "名稱不能為空";
輸出:
Exception in thread "main" java.lang.AssertionError: 名稱不正確
不過默認(rèn)是不啟動(dòng)斷言檢查的,需要要帶上 JVM 參數(shù):-enableassertions 才能生效。
Java 中這個(gè)用的很少,建議使用 Spring 中的,更強(qiáng)大,更方便好用。
Spring中的用法:
Assert.notNull(name,"名稱不能為空");
創(chuàng)建返回空集合而不是null的方法
一個(gè)非常好的技術(shù)是創(chuàng)建返回一個(gè)空集合的方法,而不是一個(gè)null值。你的應(yīng)用程序的代碼可以遍歷空集合并使用它的方法和字段,而不會(huì)拋出一個(gè)NullPointerException。Collections類提供了方便的空 List,Set 和 Map: Collections.EMPTY_LIST,Collections.EMPTY_SET,Collections.EMPTY_MAP。
例如:
public class Example {
private static List<Integer> numbers = null;
public static List<Integer> getList() {
if (numbers == null){
return Collections.EMPTY_LIST;
} else {
return numbers;
}
}
}賦值時(shí)自動(dòng)拆箱出現(xiàn)空指針
1、變量賦值自動(dòng)拆箱出現(xiàn)的空指針
Long i = null; //變量賦值自動(dòng)拆箱出現(xiàn)的空指針 long j = i;
Long 自動(dòng)拆箱為 long 實(shí)質(zhì)是調(diào)用了 longValue 方法,由于以上的 Long 類型變量值為 null,自動(dòng)拆箱就會(huì)報(bào) NPE。
2、方法傳參時(shí)自動(dòng)拆箱引起空指針異常
public static int add(int x, int y)
{
return x + y;
}
public static void main(String[] args)
{
Integer left = null;
Integer right = null;
//方法傳參時(shí)自動(dòng)拆箱引起空指針異常
System.out.println(add(left, right));
}以上自動(dòng)拆箱報(bào) NPE,原因在于裝箱的值為 null,調(diào)用 null 的任何 方法都會(huì)報(bào)錯(cuò)。
規(guī)避指南:
- 基本數(shù)據(jù)類型優(yōu)于包裝器類型,優(yōu)先考慮使用基本類型。對(duì)于不確定的包裝器類型,一定要校驗(yàn)是否為 NULL。
- 基本數(shù)據(jù)類型比包裝器類型更加節(jié)省時(shí)間與空間。
小結(jié)
關(guān)于怎么有效避免空指針,其實(shí)還有更多,如何避免空指針,一是要注意代碼編寫規(guī)范,二是要提高代碼素養(yǎng)。
到此這篇關(guān)于Java中避免空指針的幾種方法解析的文章就介紹到這了,更多相關(guān)Java避免空指針內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用java和sqlserver建立簡(jiǎn)易圖書管理系統(tǒng)的完整步驟
圖書館管理系統(tǒng)是圖書館管理工作中不可缺少的部分,它對(duì)于圖書館的管理者和使用者都非常重要,下面這篇文章主要給大家介紹了關(guān)于利用java和sqlserver建立簡(jiǎn)易圖書管理系統(tǒng)的完整步驟,需要的朋友可以參考下2022-06-06
SpringBoot使用Mybatis&Mybatis-plus文件映射配置方法
這篇文章主要介紹了SpringBoot使用Mybatis&Mybatis-plus文件映射配置方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05
List集合中對(duì)數(shù)據(jù)實(shí)現(xiàn)多重規(guī)則進(jìn)行排序的案例
今天小編就為大家分享一篇關(guān)于List集合中對(duì)數(shù)據(jù)實(shí)現(xiàn)多重規(guī)則進(jìn)行排序的案例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12
Struts2+Hibernate實(shí)現(xiàn)數(shù)據(jù)分頁(yè)的方法
這篇文章主要介紹了Struts2+Hibernate實(shí)現(xiàn)數(shù)據(jù)分頁(yè)的方法,結(jié)合實(shí)例形式分析了Struts2結(jié)合Hibernate實(shí)現(xiàn)數(shù)據(jù)分頁(yè)的原理,步驟與相關(guān)實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-03-03
使用SQL保留兩位小數(shù)的實(shí)現(xiàn)方式
SQL中保留兩位小數(shù)有三種方法:1、使用ROUND()函數(shù)進(jìn)行四舍五入;2、使用CONVERT()函數(shù)和3、CAST()函數(shù)進(jìn)行強(qiáng)制類型轉(zhuǎn)換,這兩種方法會(huì)截?cái)喽嘤嗟奈粩?shù),ROUND()函數(shù)會(huì)保留0位,而CONVERT()和CAST()會(huì)刪除多余的02024-11-11
簡(jiǎn)單了解java中int和Integer的區(qū)別
這篇文章主要介紹了簡(jiǎn)單了解java中int和Integer的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
Java項(xiàng)目中防止SQL注入的四種方案總結(jié)
SQL注入是一種代碼注入技術(shù),通過把SQL命令插入到Web表單遞交或輸入域名或頁(yè)面請(qǐng)求的查詢字符串,最終達(dá)到欺騙服務(wù)器執(zhí)行惡意的SQL命令,下面我們就來看看如何在項(xiàng)目中防止SQL注入吧2023-10-10

