古詩詞大全網 - 個性簽名 - 如何解決java中的空指針異常

如何解決java中的空指針異常

原文:/問題

妳的問題的解決方案

問題位置:

在堆棧異常信息的第壹行,您可以找到空指針的位置。如果這不是妳寫的類,妳可以向下滾動找到妳寫的類,就是這裏出現的空指針。

解決問題:

當調用空對象中的方法或屬性時,它將報告壹個空指針並檢查對象為什麽是空的。

Java空指針異常的幾種解決方案

Java中的任何對象都可能是空的。當我們調用壹個空對象的方法時,我們會拋出壹個NullPointerException,這是壹個非常常見的錯誤類型。我們可以使用幾種方法來避免這種異常,並使我們的代碼更加健壯。本文將列出這些解決方案,包括傳統的空檢測、編程規範以及現代Java語言引入的各種工具作為輔助。

運行時檢測

最明顯的方法是使用if (obj == null)檢測所有需要使用的對象,包括函數參數、返回值和類實例的成員變量。當檢測到空值時,可以選擇拋出更有針對性的異常類型,如IllegalArgumentException,並添加消息內容。我們可以使用壹些庫函數來簡化代碼,比如壹開始Java 7提供的Objects#requireNonNull方法:

public void testObjects(Object arg){

object checked = objects . require nonnull(arg," arg不得為null ");

checked . tostring();}

Guava的前提條件類還提供了壹系列檢測參數合法性的工具函數,包括空值檢測:

public void test guava(Object arg){

object checked = preceditions . checknotNull(arg," %s不得為null "," arg ");

checked . tostring();

}

我們還可以使用Lombok生成空檢測代碼,並拋出壹個空指針異常,並給出提示信息:

public void test lombok(@ NonNull Object arg){

arg . tostring();

生成的代碼如下:

public void testlombokkgenerated(Object arg){

if (arg == null) {

拋出new NullPointerException("arg被標記為@NonNull,但為null ");

}

arg . tostring();

}

這個註釋也可以用在類實例的成員變量上,所有的賦值操作都會自動檢測空值。

編程規範

通過遵守壹些編程規範,也可以在壹定程度上減少空指針異常的發生。

使用已經判斷空值的方法,如String#equals、String#valueOf和三方庫中使用的函數來判斷字符串和集合是否為空:

if (str!= null & amp& ampstr . equals(" text "){ }

if("文本"。equals(str)) {}

if (obj!= null){ obj . tostring();}

string . value of(obj);//"空"

//來自彈簧芯

string utils . isempty(str);

collection utils . isempty(col);

//來自番石榴

strings . isnullorempty(str);

//來自commons-collections4

collection utils . isempty(col);

如果壹個函數的參數可以接收null值,可以考慮將其重寫為兩個函數,並使用不同的函數簽名,這樣可以強制每個參數不為空:

public void method a(Object arg 1){

methodB(arg1,新對象[0]);

}

public void method b(Object arg 1,Object[] arg2) {

for (Object obj : arg2) {} //無空檢查

}

如果函數的返回值是集合類型,當結果為空時,不返回null值,而是返回壹個空集;如果返回值類型是對象,則可以選擇引發異常。這是Spring JdbcTemplate的處理方式:

//當查詢結果為空時,返回新的ArrayList

JDBC template . query forlist(" SELECT * FROM person ");

//如果找不到記錄,將引發EmptyResultDataAccessException。

JDBC template . query for object(" SELECT age FROM person WHERE id = 1 ",integer . class);

//支持泛型集合

公共& ltT & gt列表& ltT & gttestReturnCollection() {

返回collections . empty list();

}

靜態代碼分析

Java語言中有很多靜態代碼分析工具,如Eclipse IDE、SpotBugs、Checker Framework等。,它可以幫助程序員檢測編譯時錯誤。結合@Nullable和@Nonnull等註釋,我們可以在程序運行前找到可能拋出空指針異常的代碼。

然而,零檢測的註釋還沒有被標準化。雖然社區在2006年9月提出了JSR 305規範,但它已經被擱置了很長時間。許多第三方庫提供類似的註釋,並由不同的工具支持,其中包括:

Javax.annotation.Nonnull:由JSR 305提出,參考實現為com . Google . code . FindBugs . JSR 305;

Org。Eclipse . JDT . annotation . nonnull:Eclipse IDE原生支持的空檢測註釋;

edu . UMD . cs . FindBugs . Annotations . nonnull:spot bugs使用的註釋,基於findbugs.jsr305;

org . Spring Framework . lang . nonnull:Spring Framework 5.0可用;

org . Checker framework . Checker . nullness . qual . nonnull:Checker framework使用;

Android . support . annotation . nonnull:集成在Android開發工具中;

我建議使用跨IDE的解決方案,比如SpotBugs或者Checker Framework,可以和Maven很好的結合。

SpotBugs和@NonNull,@CheckForNull

SpotBugs是FindBugs的繼承者。通過向方法的參數和返回值添加@NonNull和@CheckForNull註釋,SpotBugs可以幫助我們在編譯時檢測空值。需要註意的是,SpotBugs不支持@Nullable批註,必須替換為@CheckForNull。如官方文檔所述,@Nullable只在需要覆蓋@ParametersAreNonnullByDefault時使用。

官方文檔解釋了如何將SpotBugs應用於Maven和Eclipse。我們還需要將spotbugs-annotations添加到項目依賴項中,以使用相應的註釋。

& lt依賴性& gt

& ltgroupId & gtcom . github . spot bugs & lt;/groupId & gt;

& ltartifactId & gtspot bugs-註釋& lt/artifact id & gt;

& lt版本& gt3.1.7 & lt;/version & gt;

& lt/dependency & gt;

以下是不同使用場景的描述:

@NonNull

私有對象returnNonNull() {

//錯誤:returnNonNull()可能返回Null,但它已被聲明為@Nonnull。

返回null

}

@CheckForNull

私有對象returnNullable() {

返回null

}

public void testReturnNullable(){

object obj = return nullable();

//錯誤:方法的返回值可能為空。

system . out . println(obj . tostring());

}

私有void argumentNonNull(@ NonNull Object arg){

system . out . println(arg . tostring());

}

public void testArgumentNonNull(){

//錯誤:不能將null傳遞給非空參數。

argumentNonNull(null);

}

public void testNullableArgument(@ CheckForNull Object arg){

//錯誤:參數可能為空。

system . out . println(arg . tostring());

}

對於Eclipse用戶,也可以使用IDE內置的null檢測工具,只需替換默認的annotation org即可。帶有SpotBugs註釋的eclipse . JDT . annotation . nullable:

Checker框架和@NonNull,@Nullable

Checker Framework可以作為javac編譯器的插件運行,可以檢測代碼中的數據類型,防止出現各種問題。我們可以參考官方文檔,把Checker框架和maven-compiler-plugin結合起來,然後在每次執行mvn compile的時候進行檢查。Checker Framework的空檢測器幾乎支持所有註釋,包括JSR 305、Eclipse,甚至lombok.NonNull

導入org . checker framework . checker . nullness . qual . nullable;

@Nullable

私有對象returnNullable() {

返回null

}

public void testReturnNullable(){

object obj = return nullable();

//錯誤:obj可能為空。

system . out . println(obj . tostring());

}

默認情況下,Checker Framework會將@NonNull應用於所有函數參數和返回值。因此,即使不添加此註釋,下列程序也無法編譯:

私有對象returnNonNull() {

//錯誤:該方法被聲明為@NonNull,但返回Null。

返回null

}

私有void argumentNonNull(Object arg){

system . out . println(arg . tostring());

}

public void testArgumentNonNull(){

//錯誤:該參數被聲明為@NonNull,但傳入了Null。

argumentNonNull(null);

}

Checker Framework對於使用Spring Framework 5.0或以上版本的用戶來說非常有用,因為Spring提供了內置的空檢測註釋,並且可以被Checker Framework支持。壹方面,我們不需要引入額外的Jar包,更重要的是,Spring框架代碼本身就使用了這些註釋,這樣我們在調用它的API時就可以有效地處理空值。比如StringUtils類中可以傳入null值的函數和返回null值的函數都添加了@Nullable註釋,而沒有添加的方法則繼承了整個框架的@NonNull註釋。因此,Checker框架可以檢測到以下代碼中的空指針異常:

//這是spring-core中定義的類和方法。

公共抽象類StringUtils {

str參數繼承了global @NonNull批註。

公共靜態字符串大寫(String str) {}

@Nullable

公共靜態字符串getFilename(@可空字符串路徑){}

}

//錯誤:該參數被聲明為@NonNull,但傳入了Null。

string utils . capital(null);

string filename = string utils . get filename("/path/to/file ");

//錯誤:文件名可能為空。

system . out . println(filename . length());

可選類型

Java 8引入了可選

可選& lt字符串& gtopt

//創建

opt = optional . empty();

opt = optional . of(" text ");

opt = optional . of nullable(null);

//判斷並閱讀

if (opt.isPresent()) {

opt . get();

}

//默認值

opt . or else(" default ");

opt . orelseget(()-& gt;“默認”);

opt . orelsthrow(()-& gt;new NullPointerException());

//相關操作

opt . if present(value-& gt;{

System.out.println(值);

});

opt.filter(值-& gt;value . length()& gt;5);

opt . map(value-& gt;value . trim());

opt . flat map(value-& gt;{

string trimmed = value . trim();

返回trimmed.isEmpty()?optional . empty():optional . of(trimmed);

});

方法的鏈調用很容易導致空指針異常,但如果返回值都包裝在Optional中,則可以使用flatMap方法實現安全的鏈調用:

String zipCode = getUser()

。平面圖(User::getAddress)

。平面圖(地址::getZipCode)

。orElse(" ");

Java 8流API也使用Optional作為返回類型:

stringList.stream()。findFirst()。orElse("默認");

stringList.stream()

。max(Comparator.naturalOrder())

。if present(system . out::println);

此外,Java 8還為基本類型提供了單獨的可選類,如OptionalInt和OptionalDouble,非常適合對性能要求較高的場景。

其他JVM語言中的空指針異常

Scala語言中的Option類對於Java 8可以是可選的。它有兩個子類型,壹些代表值,沒有代表空值。

val opt:Option[String]= Some(" text ")

opt.getOrElse("default ")

除了使用選項#isEmpty判斷,還可以使用Scala的模式匹配:

選擇匹配{

case Some(text)= & gt;打印(文本)

case None = & gtprintln("默認")

Scala的集合處理函數庫非常強大,Option可以直接作為集合操作,比如filer、map、for-analysis:

opt.map(_。修剪)。過濾器(_)。長度& gt0).地圖(_。toUpperCase)。getOrElse("DEFAULT ")

val upper = for {

text & lt- opt

修剪& lt- Some(text.trim())

upper & lt-壹些(修剪過的),如果修剪過的話。0

}收益率上限

upper.getOrElse("DEFAULT ")

Kotlin使用了另壹種方式,用戶在定義變量時需要明確區分可空類型和不可空類型。當使用可空類型時,必須進行空值檢測。

var a: String = "text "

A = null //錯誤:不能將null賦給非空字符串類型。

val b:字符串?= "文本"

//錯誤:必須使用安全運算符(?。)或者強制忽略(!!。)。

打印長度

val l: Int?= b?。長度//安全操作

b!!。length //強制忽略,這可能會引發空異常。

Kotlin的特點之壹是與Java的互操作性,但是Kotlin編譯器無法知道Java類型是否為空,所以需要在Java代碼中使用註釋,而Kotlin支持的註釋範圍很廣。從Spring Framework 5.0開始,Kotlin得到了原生支持,其空值檢測也是通過註釋進行的,這樣Kotlin就可以安全地調用Spring Framework的所有API。

結論

在上述方案中,我推薦使用註釋來防止空指針異常,因為這種方法非常有效,而且對代碼的侵害較小。所有的公共* *** API都應該用@Nullable和@NonNull進行註釋,這可以強制調用者防止空指針異常,使我們的程序更加健壯。