java 刪除二維數組中的null_避免在Java中檢查Null語句

bb727a76891334fd851ac29d1a6c3532.png

1.概述

通常,在Java代碼中處理null變量、引用和集合很棘手。它們不僅難以識別,而且處理起來也很復雜。事實上,在編譯時無法識別處理null的任何錯誤,會導致運行時NullPointerException。在本教程中,我們將了解在Java中檢查null的必要性以及幫助我們避免在代碼中進行空檢查的各種替代方法。

2.什么是NullPointerException?

根據 Javadoc for NullPointerException,當應用程序在需要對象的情況下嘗試使用null時拋出它,例如:

  • 調用null對象的實例方法
  • 訪問或修改空對象的字段
  • 取null的長度,就好像它是一個數組一樣
  • 訪問或修改null的插槽,就像它是一個數組一樣
  • 拋出null就好像它是一個Throwable值

讓我們快速查看導致此異常的Java代碼的幾個示例:

publicvoid doSomething(){String result = doSomethingElse();if(result.equalsIgnoreCase("Success"))// success}}privateString doSomethingElse(){returnnull;}

在這里,我們嘗試調用null引用的方法調用。這將導致NullPointerException。另一個常見示例是,如果我們嘗試訪問空數組:

publicstaticvoid main(String[] args){findMax(null);}privatestaticvoid findMax(int[] arr){int max = arr[0];//check other elements in loop}

這會在第6行導致 NullPointerException。因此,訪問空 對象的任何字段,方法或索引會導致 NullPointerException,如上面的示例所示。避免 NullPointerException的 常見方法是檢查 null:

publicvoid doSomething(){String result = doSomethingElse();if(result !=null&& result.equalsIgnoreCase("Success")){// success}else// failure}privateString doSomethingElse(){returnnull;}

在現實世界中,程序員發現很難識別哪些對象可以為 null。積極安全的策略可能是為每個對象檢查 null。但是,這會導致大量冗余空值檢查,并使我們的代碼可讀性降低。在接下來的幾節中,我們將介紹Java中的一些備選方案,以避免這種冗余。

3.通過API約定處理null

如上一節所述,訪問null對象的方法或變量會導致NullPointerException。 我們還討論了在訪問對象之前對對象進行空 檢查可以消除NullPointerException的可能性。但是,通常有API可以處理空值。例如:

publicvoid print(Object param){System.out.println("Printing "+ param);}publicObject process()throwsException{Object result = doSomething();if(result ==null){thrownewException("Processing fail. Got a null response");}else{return result;}}

在 print()方法調用將只打印 null,但不會拋出異常。同樣, process()永遠不會在其響應中返回 null。它反而拋出異常。因此對于訪問上述API的客戶端代碼,不需要進行空檢查。但是此類API必須在約定中明確說明。API發布此類約定的常見位置是JavaDoc。但是,這并未明確指出API約定,因此依賴于客戶端代碼開發人員來確保其合規性。在下一節中,我們將看到一些IDE和其他開發工具如何幫助開發人員解決這個問題。

4.自動化API約定

4.1.使用靜態代碼分析

靜態代碼分析工具有助于提高代碼質量。一些這樣的工具也允許開發人員維護null約定(Null Contracts)。一個例子是 FindBugs。 FindBugs通過 @Nullable和 @NonNull注解幫助管理null約定。我們可以在任何方法,字段,局部變量或參數上使用這些注釋。這使得對客戶端代碼明確指出注釋類型是否為 null。我們來看一個例子:

publicvoid accept(@NonnullObject param){System.out.println(param.toString());}

在這里, @NonNull清楚地表明參數不能為 null。如果客戶端代碼在不檢查 null參數的情況下調用此方法 ,則 FindBugs將在編譯時生成警告。

4.2.使用靜態代碼分析

開發人員通常依靠IDE來編寫Java代碼。使用代碼自動補全和有用警告等功能,例如可能沒有聲明變量,在很大程度上對編碼有幫助。一些IDE還允許開發人員管理API約定(API Contracts),從而消除對靜態代碼分析工具的需求。IntelliJ IDEA提供 @NonNull和 @Nullable注解。要在IntelliJ中添加對這些注釋的支持,我們必須添加以下Maven依賴項:

<dependency><groupId>org.jetbrains</groupId><artifactId>annotations</artifactId><version>16.0.2</version></dependency>

現在,如果沒有對 Null進行檢查,IntelliJ將生成警告,就像我們在上一個示例中一樣。IntelliJ還提供了用于處理復雜API約束的Contract注釋。

5.斷言

到目前為止,我們只討論過從客戶端代碼中去除空檢查的必要性。但是,這很少適用于實際應用。現在,假設我們正在使用一個不能接受空參數的API,或者可以返回必須由客戶端處理的空響應。這表明我們需要檢查參數或空值的響應。這里,我們可以使用Java Assertions代替傳統的 null檢查條件語句:

publicvoid accept(Object param){assert param !=null;doSomething(param);}

在第2行中,我們檢查null參數。如果啟用了斷言,則會導致 AssertionError。盡管這是斷言非空參數等前置條件的好方法,但這種方法主要存在兩個問題:

  1. 通常在JVM中禁用斷言
  2. 一個虛假的聲明將導致在未經檢查的錯誤無法恢復

因此,建議程序員不要使用斷言來檢查條件。在以下部分中,我們將討論處理null檢查的其他方法

6.通過編碼實踐避免NULL檢查

6.1.前提條件

編寫早期失敗的代碼通常是一種很好的做法。因此,如果一個API不允許接受有多個參數為空,更好地方法是預先檢查API中的每一個非空參數。

例如,讓我們看看兩個方法:一個早期失敗,另一個不失敗:

publicvoid goodAccept(String one,String two,String three){if(one ==null|| two ==null|| three ==null){thrownewIllegalArgumentException();}process(one);process(two);process(three);}publicvoid badAccept(String one,String two,String three){if(one ==null){thrownewIllegalArgumentException();}else{process(one);}if(two ==null){thrownewIllegalArgumentException();}else{process(two);}if(three ==null){thrownewIllegalArgumentException();}else{process(three);}}

顯然,我們應該更喜歡 goodAccept()而不是 badAccept()。作為替代方案,我們也可以使用Guava的前置條件來驗證API參數。

6.2.使用原語而不是包裝類

由于 null對于像int這樣的原語來說不是一個可接受的值,我們應該盡可能優先于它們的包裝對象,如 Integer。考慮一個對兩個整數求和的方法的兩個實現:

publicstaticint primitiveSum(int a,int b){return a + b;}publicstaticInteger wrapperSum(Integer a,Integer b){return a + b;}

6.3.空集合

有時,我們需要將一個集合作為方法的響應返回。對于這樣的方法,我們應該總是嘗試返回一個空集合而不是 null

publicList<String> names(){if(userExists()){returnStream.of(readName()).collect(Collectors.toList());}else{returnCollections.emptyList();}}

因此,我們在調用此方法時避免了客戶端執行空檢查的需要。

7.使用 Objects

Java 7引入了新的Objects API。此API有幾個靜態 實用程序方法,可以消除大量冗余代碼。讓我們看看一個這樣的方法, requireNonNull():

publicvoid accept(Object param){Objects.requireNonNull(param);// doSomething()}

現在,讓我們測試 accept方法:

assertThrows(NullPointerException.class,()-> accept(null));

因此,如果將null 作為參數傳遞,則 accept()會拋出 NullPointerException。此類還具有 isNull()和 nonNull()方法,可用作謂詞來檢查對象是否為null。

8.使用Optional

Java8在該語言中引入了一個新的 OptionalAPI。與null相比,這為處理可選值提供了更好的約定。讓我們看看 Optional如何消除對空檢查的需求:

publicOptional<Object> process(boolean processed){String response = doSomething(processed);if(response ==null){returnOptional.empty();}returnOptional.of(response);}privateString doSomething(boolean processed){if(processed){return"passed";}else{returnnull;}}

通過返回一個 Optional,如上所示,該 process()方法使得明確告訴調用者,響應可能是Null,并且必須在編譯時處理。 這顯然消除了客戶端代碼中對空檢查的需求。可以使用 OptionalAPI的聲明性樣式以不同方式處理空響應:

assertThrows(Exception.class,()-> process(false).orElseThrow(()->newException()));

此外,它還為API開發人員提供了一個更好的約定,以向客戶端表明API可以返回空響應。雖然我們不需要對此API的調用者進行空檢查,但我們使用它來返回空響應。為避免這種情況, Optional提供了一個 ofNullable方法,該方法返回具有指定值的 Optional,如果值為 null,則返回 empty:

publicOptional<Object> process(boolean processed){String response = doSomething(processed);returnOptional.ofNullable(response);}

9. 庫

9.1. 使用Lombok

Lombok是一個很棒的庫,可以減少項目中樣板代碼的數量。它附帶了一組注釋,取代了我們經常在Java應用程序中編寫的代碼的常見部分,例如getter,setter和toString(),僅舉幾例。

另一個注釋是 @NonNull。 因此,如果項目已經使用Lombok來消除樣板代碼,則 @NonNull可以代替作為空檢查。

在繼續查看一些示例之前,添加一個Maven依賴項引入Lombok:

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.6</version></dependency>

現在,我們可以在需要進行空檢查的地方 使用 @NonNull:

publicvoid accept(@NonNullObject param){System.out.println(param);}

因此,我們只是注解了需要進行null檢查的對象,并且Lombok生成了已編譯的類:

publicvoid accept(@NonNullObject param){if(param ==null){thrownewNullPointerException("param");}else{System.out.println(param);}}

如果 param為null,則此方法拋出 NullPointerException。該方法必須在其約定中明確說明,并且客戶端代碼必須處理異常。

9.2.使用StringUtils

一般來說,字符串驗證包括除空值檢查空值。因此,常見的驗證聲明是:

publicvoid accept(String param){if(null!= param &&!param.isEmpty())System.out.println(param);}

如果我們必須處理很多 String類型,這很快就會變得多余。這就是 StringUtils派上用場的地方。在我們看到這個動作之前,讓我們為commons-lang3添加一個Maven依賴項:

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version></dependency>

現在讓我們用 StringUtils重構上面的代碼 :

publicvoid accept(String param){if(StringUtils.isNotEmpty(param))System.out.println(param);}

因此,我們使用靜態實用程序方法 isNotEmpty()替換了 null或空檢查。此API提供了其它強大而實用方法來處理常見的String函數。

10.結論

在本文中,我們研究了發生 NullPointerException的各種原因以及難以識別的原因。然后,我們使用了各種方法來避免代碼中的冗余,以及對使用參數,返回類型和其他變量進行空檢查。所有示例都可以在GitHub上找到。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/537168.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537168.shtml
英文地址,請注明出處:http://en.pswp.cn/news/537168.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java并發編程之并發容器ConcurrentHashMap(JDK1.7)解析

最近看了一下ConcurrentHashMap的相關代碼&#xff0c;感覺JDK1.7和JDK1.8差別挺大的&#xff0c;這次先看下JDK1.7是怎么實現的吧 哈希&#xff08;hash&#xff09; 先了解一下啥是哈希&#xff08;網上有很多介紹&#xff09;&#xff0c;是一種散列函數&#xff0c;簡單來…

帶控制端的邏輯運算電路_分別完成正整數的平方、立方和階乘的運算verilog語言...

練習&#xff1a;設計一個帶控制端的邏輯運算電路&#xff0c;分別完成正整數的平方、立方和階乘的運算。 //--------------myfunction---------- modulemyfunction(clk,n,result,reset,sl); output[6:0]result; input[2:0] n; input reset,clk; input [1:0] sl; reg[6:0]resul…

Java并發編程之并發容器ConcurrentHashMap(JDK1.8)解析

這個版本ConcurrentHashMap難度提升了很多&#xff0c;就簡單的談一下常用的方法就好了&#xff0c;可能有些講的不太清楚&#xff0c;麻煩發現的大佬指正一下 主要數據結構 1.8將Segment取消了&#xff0c;保留了table數組的形式&#xff0c;但是不在以HashEntry純鏈表的形式…

simulink顯示多個數據_如何在 Simulink 中使用 PID Tuner 進行 PID 調參?

作者 | 安布奇責編 | 胡雪蕊出品 | CSDN(ID: CSDNnews)本文為一篇技術干貨&#xff0c;主要講述在Simulink如何使用PID Tuner進行PID調參。PID調參器( PIDTuner)概述1.1 簡介使用PID Tuner可以對Simulink模型中的PID控制器&#xff0c;離散PID控制器&#xff0c;兩自由度PID控制…

Java并發編程之堵塞隊列介紹以及SkipList(跳表)

堵塞隊列 先了解一下生產者消費者模式&#xff1a; 生產者就是生產數據的一方&#xff0c;消費者就是消費數據的另一方。在多線程開發中&#xff0c;如果生產者處理速度很快&#xff0c;而消費者處理速度很慢&#xff0c;那么生產者就必須等待消費者處理完&#xff0c;才能繼…

python生成list的時候 可以用lamda也可以不用_python 可迭代對象,迭代器和生成器,lambda表達式...

分頁查找#5.隨意寫一個20行以上的文件(divmod)# 運行程序&#xff0c;先將內容讀到內存中&#xff0c;用列表存儲。# l []# 提示&#xff1a;一共有多少頁# 接收用戶輸入頁碼&#xff0c;每頁5條&#xff0c;僅輸出當頁的內容def read_page(bk_list,n,endlineNone):startline …

數據挖掘技術簡介[轉]

關鍵詞&#xff1a; 關鍵詞&#xff1a;數據挖掘 數據集合 1. 引言  數據挖掘(Data Mining)是從大量的、不完全的、有噪聲的、模糊的、隨機的數據中提取隱含在其中的、人們事先不知道的、但又是潛在有用的信息和知識的過程。隨…

樹莓派安裝smbus_樹莓派使用smbus不兼容問題(no module named 'smbus')

樹莓派使用smbus不兼容問題(no module named ‘smbus’)python3.5–3.6可以使用smbus2代替smbus1. 先參考以下方法&#xff1a;github討論樹莓派社區2.Pypi上可以下載smbus2smbus2PyPi介紹&#xff1a;當前支持的功能有&#xff1a;獲取i2c功能(I2C_FUNCS)read_bytewrite_byter…

Java并發編程之線程池ThreadPoolExecutor解析

線程池存在的意義 平常使用線程即new Thread()然后調用start()方法去啟動這個線程&#xff0c;但是在頻繁的業務情況下如果在生產環境大量的創建Thread對象是則會浪費資源&#xff0c;不僅增加GC回收壓力&#xff0c;并且還浪費了時間&#xff0c;創建線程是需要花時間的&…

面向過程的門面模式

{*******************************************************}{ }{ 業務邏輯一 }{ }{ 版權所有 (C) 2008 陳…

Java并發編程之線程定時器ScheduledThreadPoolExecutor解析

定時器 就是需要周期性的執行任務&#xff0c;也叫調度任務&#xff0c;在JDK中有個類Timer是支持周期性執行&#xff0c;但是這個類不建議使用了。 ScheduledThreadPoolExecutor 繼承自ThreadPoolExecutor線程池&#xff0c;在Executors默認創建了兩種&#xff1a; newSin…

python xml轉換鍵值對_Python 提取dict轉換為xml/json/table并輸出

#!/usr/bin/python#-*- coding:gbk -*-#設置源文件輸出格式import sysimport getoptimport jsonimport createDictimport myConToXMLimport myConToTabledef getRsDataToDict():#獲取控制臺中輸入的參數&#xff0c;并根據參數找到源文件獲取源數據csDict{}try:#通過getopt獲取…

應用開發框架之——根據數據表中的存儲的方法名稱來調用方法

功用一&#xff1a;在框架里面根據存儲在數據表中的方法名來動態調用執行方法。 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 class(TForm) Button1: TButton; procedu…

Spring IOC容器組件注入的幾種方式

整理一下之前Spring的學習筆記&#xff0c;大致有一下幾種Spring注入到容器中的方法: 1&#xff09;、配置在xml的方式。 2&#xff09;、開啟包掃描ComponentScan使用Component&#xff0c;Service&#xff0c;Controller&#xff0c;Repository&#xff08;其實后三個都繼承…

我們是如何拿下Google和Facebook Offer的?

http://posts.careerengine.us/p/57c3a1c1a09633ee7e57803c 大家好&#xff0c;我是小高&#xff0c;CMU CS Master&#xff0c;來Offer第一期學員&#xff0c;2014年初在孫老師的帶領下我在幾個月的時間內進入了Yahoo&#xff0c;并工作了近2年。2016年初&#xff0c;Yahoo工作…

Spring中BeanFactory和FactoryBean的區別

先介紹一下Spring的IOC容器到底是個什么東西&#xff0c;都說是一個控制反轉的容器&#xff0c;將對象的控制權交給IOC容器&#xff0c;其實在看了源代碼之后&#xff0c;就會發現IOC容器只是一個存儲單例的一個ConcurrentHashMap<String, BeanDefinition> BeanDefiniti…

python中數字和字符串可以直接相加_用c語言或者python將文件中特定字符串后面的數字相加...

匿名用戶1級2014-08-31 回答代碼應該不難吧。既然用爬蟲爬下來了&#xff0c;為什么爬取數據的時候沒做處理呢。之前用過Scrapy爬蟲框架&#xff0c;挺好用的&#xff0c;你可研究下。代碼&#xff1a;#!codingutf-8import osimport reimport random# 獲取當前目錄文件列表def …

Spring中Aware的用法以及實現

Aware 在Spring當中有一些內置的對象是未開放給我們使用的&#xff0c;例如Spring的上下文ApplicationContext、環境屬性Environment&#xff0c;BeanFactory等等其他的一些內置對象&#xff0c;而在我們可以通過實現對應的Aware接口去拿到我們想要的一些屬性&#xff0c;一般…

c#字符型轉化為asc_C#字符串和ASCII碼的轉換

//字符轉ASCII碼&#xff1a;public static int Asc(string character){if (character.Length 1){System.Text.ASCIIEncoding asciiEncoding new System.Text.ASCIIEncoding();int intAsciiCode (int)asciiEncoding.GetBytes(character)[0];return (intAsciiCode);}else{thr…