Java プログラマーの皆さんは NPE 例外 (NullPointerException の略) に精通していると思いますが、最近、ビジネス要件の開発が少し不安になり、テスト環境で時々 NPE 例外が発生し、特別な頭痛の種になっています。最も頻繁に見られる例外の 1 つは、Java 開発を始めると、キャリア全体にわたってそれが付きまとうと言えます。初心者の Xiaobai であろうと、古いドライバーであろうと、NPE の異常に対しては「愛情」があり、冷酷です。愛される主な理由は、対処が簡単であることです。ピットを踏むことになります。コードの品質を向上させるためには、NPE 例外を排除する必要があります。
対処は簡単ですが、特に心配することはありませんか? 正直なところ、検証はまだ終わっていませんが、処理プロセス全体におけるプログラマーのエクスペリエンスは非常に悪いです。
-
コードを冗長にする
多くの場合、コアとなるビジネスロジックのコード量は多くありませんが、さまざまな判断や検証が追加されるとコードが冗長になり、その分可読性や保守性が低下します。
-
純粋な努力
このような機械的な空判断や検証は、本質的には物理的な作業であり、コーディングの楽しさはまったくなく、この種のコードを長時間書いていると、プログラミングに対する情熱が失われます。
-
責任を取るのは簡単
多くのビジネスでは複数人の協力が必要で、他の人が使うときはまぐれで処理してしまうこともあるし、目に見えない穴が掘られていて、注意しないと鍋が空から落ちてくることもある。
上記のあまり良くない経験に基づいて、排除の難易度は大幅に増加しました。
需要が非常に緊急である場合、ほとんどのプログラマは機能に重点を置くことを選択し、重要度の低いものは常に後で追加されると考えられ、重要な内容が最初にスキップされ、スキップした後に何も残らないという結果になることがあります。 .アップ。
開発効率に影響を与えることなく NPE 問題を解決するために、JDK とサードパーティのフレームワークは多くの優れたツールを提供してくれるため、車輪の再発明に時間と労力を無駄にする必要はありません。
NPE 問題を完全に解決するための 10 のクーデターを以下に示します。
1オブジェクトツールクラス
null ポインタを解決する必要があるので、事前にオブジェクトの null チェックを行うのは当然で、通常であればオブジェクトのチェックに使用されますが、if( null != obj )
Java 7 では特別にツール クラスが提供されておりjava.util.Objects
、オブジェクトの null チェックが簡単になります。
特徴
-
Java 7 には追加の依存関係はありません
-
静的メソッド、使いやすい
-
オブジェクトの null 検出のみをサポートします
例
-
Objects.isNull
オブジェクトが空かどうかを判断し、
null
returntrue
、そうでない場合は returnfalse
Object obj = null; System.out.println(Objects.isNull(obj)); // true obj = new Object(); System.out.println(Objects.isNull(obj)); // false
-
Objects.nonNull
逆に
Objects.isNull
、オブジェクトが空ではないと判断してnull
returnfalse
、それ以外の場合は returntrue
Object obj = null; System.out.println(Objects.nonNull(obj)); // false obj = new Object(); System.out.println(Objects.nonNull(obj)); // true
-
Objects.requireNonNull
空でないことを確認してください。オブジェクトが空になると、NullPointerException がスローされます。メソッドを変更して例外の説明をカスタマイズすると、例外発生後に問題をすぐに見つけることができます。
Object obj = null; Objects.requireNonNull(obj); // 自定义错误描述 Objects.requireNonNull(obj,"obj 对象为空");
実行出力:
Exception in thread "main" java.lang.NullPointerException: obj 对象为空 at java.util.Objects.requireNonNull(Objects.java:228) at com.ehang.helloworld.controller.NullTest.t5(NullTest.java:97) at com.ehang.helloworld.controller.NullTest.main(NullTest.java:23)
2 文字列が空であると判断される
文字列は開発プロセスで最もよく使用されるデータ型であるため、文字列の判断と検証は不可欠です。ネイティブの方法では、空のオブジェクトの長さを判断します。
String str = "一行Java"
if ( null != str && s1.length() > 0 ){
// 对str字符串进行使用
}
ただし、空文字列であるかどうかを判断する以外にも、空文字列であるかどうか (String str = )、スペースだけであるかどうか (String str = ) を判断するなど、文字列をチェックするシナリオは数多くあり ""
ます " "
。これらのチェックは少し面倒ですが、既製のツールで十分です。
Spring StringUtil ツール クラス
org.springframework.util.StringUtils
Stringフレームワークに付属する文字列ツールクラスであり、機能は比較的シンプルですが、新バージョンのティーチングツールでは、このツールクラスの文字列null検出メソッドは非推奨となっているため、使用は推奨されません;
-
StringUtils.isEmpty
空のオブジェクトと空の文字列の検証。
String s1 = null; String s2 = ""; String s3 = " "; System.out.println(StringUtils.isEmpty(s1)); // true System.out.println(StringUtils.isEmpty(s2)); // true System.out.println(StringUtils.isEmpty(s3)); // false
Apache lang3 StringUtil ツール クラス
Apache lang3 StringUtil ツール クラス ( org.apache.commons.lang3.StringUtils
) は、Spring フレームワークによってもたらされるツール クラスよりも強力で、String 操作のすべてのカプセル化をカバーします。
StringUtils.isEmpty
null チェックには主に、StringUtils.isNotEmpty
、 、の 4 つがありますStringUtils.isBlank
。StringUtils.isNotBlank
-
頼る
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency>
-
StringUtils.isEmpty
とStringUtils.isNotEmpty
文字列オブジェクトが空かどうか、および文字列の長さが 0 かどうかを判断します。isEmpty と isNotEmpty のチェック結果は逆です。
String s1 = null; String s2 = ""; String s3 = " "; System.out.println(StringUtils.isEmpty(s1)); // true System.out.println(StringUtils.isEmpty(s2)); // true System.out.println(StringUtils.isEmpty(s3)); // false System.out.println(); System.out.println(StringUtils.isNotEmpty(s1)); // false System.out.println(StringUtils.isNotEmpty(s2)); // false System.out.println(StringUtils.isNotEmpty(s3)); // true
-
StringUtils.isBlank
、StringUtils.isNotBlank
StringUtils.isEmpty
合計と判定によりStringUtils.isNotEmpty
、文字列の先頭と末尾のスペースを取り除いた後、長さが0より大きいかどうかを判定しますString s1 = null; String s2 = ""; String s3 = " "; String s4 = " 1 2 "; System.out.println(StringUtils.isBlank(s1)); // true 空对象 System.out.println(StringUtils.isBlank(s2)); // true 长度等于0 System.out.println(StringUtils.isBlank(s3)); // true 去掉前后空格之后,长度也等于0 System.out.println(StringUtils.isBlank(s4)); // false 去掉前后空格(1 2),长度大于0 System.out.println(); System.out.println(StringUtils.isNotBlank(s1)); // false System.out.println(StringUtils.isNotBlank(s2)); // false System.out.println(StringUtils.isNotBlank(s3)); // false System.out.println(StringUtils.isNotBlank(s4)); // true
-
その他の機能
この記事では主に null チェックについて説明します。lang3 の StringUtil ツール クラスは、文字列操作のほぼすべてのカプセル化をカバーしており、文字列処理の複雑さを大幅に軽減します。その他の機能については、公式ドキュメントを参照してください。
https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html
3文字列の比較
文字列を比較するときは、NPE 例外にも特別な注意を払う必要があります。
例えば:
public Boolean isEhang(String name) {
if (name.equals("ehang")) {
return true;
}
return false;
}
名前が null の場合、NPE 例外が発生します。
次の調整を行うことができます。
if ("ehang".equals(name))
...
これにより、名前がnullであってもNPE例外が発生せず、正常に判定できるようになります。
4マップ、リスト、ジャッジを空に設定
Map、List、および Set はよく使用されるデータ構造です。これらにはすべてisEmpty()
コンテナに要素が含まれているかどうかを判断するメソッドが含まれていますが、自己生成されたオブジェクトが空かどうかは判断できません。オブジェクトがインスタンス化されない場合は、isEmpty() を呼び出します。 null ポインタ例外が報告されます。Spring は、最初にオブジェクトが空かどうかを判断し、次に isEmpty() によって要素があるかどうかを判断するツール クラスを提供しており、オブジェクトによって引き起こされる null ポインタを大幅に削減できますorg.springframework.util.CollectionUtils
。isEmpty
空であることは異常である。
Map map = null;
System.out.println(map.isEmpty()); // 空指针异常
System.out.println(CollectionUtils.isEmpty(map)); // true
map = new HashMap();
System.out.println(map.isEmpty()); // true
System.out.println(CollectionUtils.isEmpty(map)); // true
map.put("1", "2");
System.out.println(CollectionUtils.isEmpty(map)); // false
System.out.println(map.isEmpty()); // false
List list = null;
System.out.println(list.isEmpty()); // 空指针异常
System.out.println(CollectionUtils.isEmpty(list)); // true
list = new ArrayList();
System.out.println(list.isEmpty()); // true
System.out.println(CollectionUtils.isEmpty(list)); // true
list.add("1");
System.out.println(CollectionUtils.isEmpty(list)); // false
System.out.println(list.isEmpty()); // false
Set set = null;
System.out.println(set.isEmpty()); // 空指针异常
System.out.println(CollectionUtils.isEmpty(set)); // true
set = new TreeSet();
System.out.println(set.isEmpty()); // true
System.out.println(CollectionUtils.isEmpty(set)); // true
set.add("1");
System.out.println(CollectionUtils.isEmpty(set)); // false
System.out.println(set.isEmpty()); // false
このツール クラスには空判定以外にも、最初の要素の取得: firstElement()、最後の要素の取得: lastElement()、要素が含まれているかどうかの取得: contains() など、便利なメソッドが多数含まれています。
hutoolのコレクション利用法
空きスペースを判断するだけで、以前の Spring CollectionUtils で十分であり、その他の機能もほとんどの使用シナリオを満たすのに十分であり、hutool的CollectionUtil
より充実した機能が提供されており、必要に応じて選択することもできます。
頼る:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.22</version>
</dependency>
5 初期値を割り当て、null オブジェクトを返さないようにします
ローカル変数の定義やオブジェクトのプロパティの定義において、初期値を代入できるものには可能な限り初期値を与える必要があります。
Map map = new HashMap();
private Integer age = 0;
メソッドに戻り値がある場合は、必要な場合を除いて null を返さないようにしてください。
たとえば、メソッドを実行すると最終的に List が返されますが、List に値がない場合は、null オブジェクトを返す代わりに、空の List を返すことができます。
public List select(){
// 这里处理其他逻辑
// 一旦返回的是null是,返回一个空List对象
return Collections.emptyList();
}
6オプション
Optional は Java 8 によって提供されるオブジェクト コンテナです。その目的は、この迷惑な null ポインタ例外を効果的に解決することです。Optional はラッパー クラスのオブジェクトとみなすことができます。
-
オプションのオブジェクトをインスタンス化する
Object o1 = null; Optional<Object> op1 = Optional.of(o1); Optional<Object> op2 = Optional.ofNullable(o1);
オプションの.of()
オブジェクトが null の場合、作成プロセスは NPE 例外をスローします。
Optional.ofNullable()
オブジェクトがnullの場合は、Optionalオブジェクトも正常に返すことができます
-
空の isPresent()
Integer i1 = null; Optional<Integer> op1 = Optional.of(i1); System.out.println(op1.isPresent()); // false Integer i2 = 123; Optional<Integer> op2 = Optional.ofNullable(i2); System.out.println(op2.isPresent()); // true op2.ifPresent(i->{ System.out.println(i); });
isPresent() は、オブジェクトが null の場合は true を返し、空でない場合は false を返します。
ラムダ式の連鎖処理:
op2.ifPresent(obj->{ System.out.println(obj); });
-
価値
// 取出原值,如果原对象为null会报NoSuchElementException异常 Integer integer = op2.get(); // 取出原值,如果原值为空,则返回指点的默认值 Integer integer1 = op1.orElse(456); // 取出原值,如果原值为空,返回默认值,不过在返回之前还需要做一些其他的事情 Integer integer2 = op2.orElseGet(() -> { // 在这里做一些其他的操作 return 456; }); // 取出原值,如果原值为空,就抛出指定的异常 op2.orElseThrow(RuntimeException::new); op2.orElseThrow(() -> new RuntimeException("不好,我的值是空的!"));
-
map() と flatMap()
エンコード処理中に、a.xxx().yyy().zzz().mmm() のようなチェーンコールが頻繁に発生しますが、その処理中のリンクに問題があると NPE が異常になります。この問題を回避するには、map() と flatMap() を使用できます。
テストオブジェクト:
@Data @NoArgsConstructor @AllArgsConstructor static class User { private String name; private Integer age; private Optional<String> addr; }
テスト:
// 得到姓名的长度,如果没有姓名就返回0 Integer nameLen = Optional.of(new User(null, 10, null)) .map(User::getName) .map(String::length) .orElse(0); System.out.println(nameLen); // 得到地址的长度,如果没有姓名就返回0 Integer addr = Optional.of(new User(null, 10, Optional.of("北京"))) .flatMap(User::getAddr) .map(String::length) .orElse(0); System.out.println(addr);
map は返されたオブジェクトを Optional オブジェクトにカプセル化します。返されたオブジェクト自体が Optional オブジェクトの場合は、 flatMap() を使用します。
7断言
Spring での 翻訳はorg.springframework.util.Assert
中国語で「アサーション」であり、実際の動作値が期待される項目と一致するかどうかを判断するために使用され、一致しない場合は例外がスローされます。このクラスを使用すると、null テストを実行することもできます。
Assert クラスは、次の静的メソッドを提供します。
メソッド名 | 説明 | 失敗時に例外をスローする |
---|---|---|
isNull(オブジェクトオブジェクト、文字列メッセージ) | オブジェクトが null ではない場合、例外がスローされます | IllegalArgumentException |
notNull(オブジェクトオブジェクト、文字列メッセージ) | オブジェクトが空の場合、例外がスローされます | IllegalArgumentException |
hasLength(文字列テキスト、文字列メッセージ) | テキストが空の文字列である場合、例外がスローされます | IllegalArgumentException |
hasText(文字列テキスト、文字列メッセージ) | 空白文字列が含まれていない場合、例外がスローされます | IllegalArgumentException |
dosNotContain(String textToSearch, String substring, String message) | textToSearch に部分文字列が含まれているため、例外がスローされます | IllegalArgumentException |
notEmpty(Object[] 配列、文字列メッセージ) | 配列が空であるか長さが 1 の場合、例外がスローされます | IllegalArgumentException |
noNullElements(Object[] 配列、文字列メッセージ) | 配列に null 要素が含まれる場合、例外がスローされます。 | IllegalArgumentException |
notEmpty(コレクションコレクション、文字列メッセージ) | コレクションに要素が含まれていない場合、例外がスローされます | IllegalArgumentException |
notEmpty(マップマップ、文字列メッセージ) | マップに null が含まれる場合、例外がスローされます | IllegalArgumentException |
isInstanceOf(クラスタイプ、オブジェクトobj、文字列メッセージ) | obj が type でない場合、例外がスローされます。 | IllegalArgumentException |
isAssignable(クラス superType、クラス subType、文字列メッセージ) | subType は superType サブクラスではないため、例外がスローされます | IllegalArgumentException |
state(ブール式、文字列メッセージ) | 式が true ではないため、例外がスローされます | IllegalStateException |
isTrue(ブール式、文字列メッセージ) | 式が true ではないため、例外がスローされます | IllegalArgumentException |
Integer i1 = null;
Assert.notNull(i1,"i1 不为空");
Map map = null;
Assert.notEmpty(map,"map 不为空");
異常な:
Exception in thread "main" java.lang.IllegalArgumentException: map 不为空
at org.springframework.util.Assert.notEmpty(Assert.java:555)
at com.ehang.helloworld.controller.NullTest.t6(NullTest.java:119)
at com.ehang.helloworld.controller.NullTest.main(NullTest.java:23)
注意を払う:
Assert は、実際の動作値が期待される項目と一致するかどうかを判断するために使用されるため、他のツール クラスの検証メソッドとは逆です。たとえば、このメソッドはオブジェクトが null であることを期待し、オブジェクトが空でない場合はisNull
、エラーが報告されます;notNull
予期されたオブジェクトが空ではないことを示し、オブジェクトが空の場合はエラーが報告されます。
8 ローカル変数は基本的なデータ型を使用します
前回の記事「Ali がオブジェクトでの基本データ型の使用を禁止する理由」では、パフォーマンスの観点から、ローカル変数の定義には可能な限り基本データ型を使用することを推奨しました。 null ポインタ例外を回避します。
例は次のとおりです。
int x;
Integer y;
System.out.println( x + 1 ); // 编译失败
System.out.println( y + 1 ); // 编译失败
int i = 1;
Integer j = null;
System.out.println( i + 1 ); // 正常
System.out.println( j + 1 ); // 空指针异常
int m = i; // 正常
int n = j; // 空指针异常
変数 x と y が定義されているだけで代入されていない場合、x + 1 と y + 1 はコンパイルできません。また、パッケージ化クラス j はオブジェクトを指定できます。パッケージ化クラスが操作に参加する場合、アンボックス化操作が最初に実行されますnull
。つまり、 intValue() メソッドを呼び出します。オブジェクトは空であるため、呼び出したメソッドは当然 null ポインターを報告します。同時に、ラッパー クラスを基本データ型に割り当てるときに、アンボックス化操作も実行されます。そして当然のことながら空になります ポインタが異常です。
ただし、基本データ型では特定の値を指定する必要があり、後続の演算や代入演算に関係なく、null ポインター例外は発生しません。
9 パラメータを事前に確認する
バックグラウンド データのほとんどはターミナル リクエストを通じて送信されるため、ユーザーに最も近い場所で検証のパラメータを検証する必要があります。たとえば、StringBoot プロジェクトでは、クライアントによって要求されたパラメータをコントローラ層で渡す必要があります。検証の際、必要なパラメータに値が渡されない場合は、要件を満たさない null 値をサービスに渡したり、データベースに保存したりするのではなく、クライアントに直接エラーを報告してユーザーに通知する必要があります。できるだけ早くそれらを確認し、遮断することで、問題が発生する可能性を大幅に減らすことができます。
前回の導入により、hibernate-validator
パラメータ検証の問題は完全に解決できます。詳細については、「SpringBoot!」を参照してください。リクエスト、レスポンス、例外は標準化されていますか?
10アイデアリマインダー
IDEA は空のオブジェクトや null 値を持つ可能性のあるオブジェクトを通知し、通知に従ってそれらを事前に認識して防止できます。
public static String t1(int i){
String name1 = null;
String name2 = null;
if(i>0){
name2 = "ehang";
}
t2(name1);
t2(name2);
return name2;
}