NullPointerExceptionとは何ですか?どうすれば修正できますか?
-
03-07-2019 - |
質問
Nullポインター例外( java.lang.NullPointerException
)とは何ですか?また、それらの原因は何ですか?
例外をプログラムが途中で終了させないようにするために、どのメソッド/ツールを使用して原因を特定できますか?
解決
参照変数(つまり、オブジェクト)を宣言すると、実際にはオブジェクトへのポインターが作成されます。プリミティブ型 int
の変数を宣言する次のコードを検討してください。
int x;
x = 10;
この例では、変数 x
は int
であり、Javaはそれを 0
に初期化します。 2行目に 10
の値を割り当てると、 10
の値が x
で参照されるメモリ位置に書き込まれます。
しかし、参照 type を宣言しようとすると、何か違うことが起こります。次のコードを取得します。
Integer num;
num = new Integer(10);
最初の行は num
という名前の変数を宣言していますが、実際にはまだプリミティブ値が含まれていません。代わりに、ポインターが含まれています(型は参照型である Integer
であるため)。あなたはまだ何を指しているのかまだ言っていないので、Javaはそれを null
に設定します。これは" 私は nothing "を指しています。
2行目では、 new
キーワードを使用して、タイプ Integer
およびポインター変数 num
のオブジェクトをインスタンス化(または作成)します。 Integer
オブジェクトに割り当てられます。
NullPointerException
は、変数を宣言したがオブジェクトを作成しなかった場合に発生します。したがって、実際には存在しないものを指していることになります。
オブジェクトを作成する前に num
を逆参照しようとすると、 NullPointerException
が発生します。最も些細なケースでは、コンパイラは問題をキャッチし、" numが初期化されていない可能性がある
、quot;ただし、オブジェクトを直接作成しないコードを記述する場合があります。
たとえば、次のようなメソッドがあります:
public void doSomething(SomeObject obj) {
//do something to obj
}
この場合、オブジェクト obj
を作成するのではなく、 doSomething()
メソッドが呼び出される前にオブジェクトが作成されたと想定します。次のようにメソッドを呼び出すことができます:
doSomething(null);
この場合、 obj
は null
です。メソッドが渡されたオブジェクトに対して何かを行うことを意図している場合、 NullPointerException
をスローするのが適切です。これはプログラマーのエラーであり、プログラマーはデバッグのためにその情報を必要とするからです。
別の方法として、メソッドの目的が、渡されたオブジェクトを操作することだけではない場合があります。そのため、nullパラメーターを使用できます。この場合、 nullパラメーターを確認し、異なる動作をする必要があります。また、ドキュメントでこれを説明する必要があります。たとえば、 doSomething()
は次のように記述できます。
/**
* @param obj An optional foo for ____. May be null, in which case
* the result will be ____.
*/
public void doSomething(SomeObject obj) {
if(obj != null) {
//do something
} else {
//do something else
}
}
他のヒント
NullPointerException
sは、オブジェクトを参照しているかのようにメモリ内の場所を指定しない参照(null)を使用しようとすると発生する例外です。 null参照でメソッドを呼び出すか、null参照のフィールドにアクセスしようとすると、 NullPointerException
がトリガーされます。これらは最も一般的ですが、他の方法はにリストされています NullPointerException
javadocページ。
おそらく、 NullPointerException
を説明するために考え出した最も速いコード例は次のとおりです。
public class Example {
public static void main(String[] args) {
Object obj = null;
obj.hashCode();
}
}
main
内の最初の行で、 Object
参照 obj
を null
に明示的に設定しています。これは、参照はあるが、オブジェクトを指していないことを意味します。その後、参照をメソッドを呼び出してオブジェクトを指しているかのように参照を処理しようとします。参照が指している場所で実行するコードがないため、これは NullPointerException
になります。
(これは技術的ですが、言及する必要があると思います:nullを指す参照は、無効なメモリの場所を指すCポインターと同じではありません。nullポインターは、文字通りどこでもを指していません、これは無効な場所を指すのとは微妙に異なります。)
NullPointerExceptionとは何ですか?
開始するのに適した場所は、 JavaDocs 。彼らはこれをカバーしています:
アプリケーションがnullを使用しようとしたときにスローされます。 オブジェクトが必要です。これらは次のとおりです。
- nullオブジェクトのインスタンスメソッドの呼び出し。
- nullオブジェクトのフィールドへのアクセスまたは変更。
- 配列のようにnullの長さを取得します。
- 配列のようにnullのスロットにアクセスまたは変更します。
- Throwable値であるかのようにnullをスローします。
アプリケーションは、このクラスのインスタンスをスローして、他の nullオブジェクトの不正使用。
synchronized
でnull参照を使用しようとすると、この例外もスローされます JLSごと:
SynchronizedStatement: synchronized ( Expression ) Block
- それ以外の場合、Expressionの値がnullの場合、
NullPointerException
がスローされます。
修正するにはどうすればよいですか
つまり、 NullPointerException
が発生します。どのように修正しますか? NullPointerException
をスローする簡単な例を見てみましょう:
public class Printer {
private String name;
public void setName(String name) {
this.name = name;
}
public void print() {
printString(name);
}
private void printString(String s) {
System.out.println(s + " (" + s.length() + ")");
}
public static void main(String[] args) {
Printer printer = new Printer();
printer.print();
}
}
null値を識別する
最初のステップは、例外の原因となっている値を正確に特定する ことです。このためには、デバッグを行う必要があります。 スタックトレースを読むことを学ぶことが重要です。これにより、例外がスローされた場所が表示されます。
Exception in thread "main" java.lang.NullPointerException
at Printer.printString(Printer.java:13)
at Printer.print(Printer.java:9)
at Printer.main(Printer.java:19)
ここでは、例外が13行目( printString
メソッド内)でスローされていることがわかります。行を見て、どの値がnullであるかを確認します
ロギングステートメントの追加、またはデバッガの使用。 s
がnullであることがわかり、その上で length
メソッドを呼び出すと例外がスローされます。 s.length()
がメソッドから削除されると、プログラムが例外のスローを停止することがわかります。
これらの値の出所を追跡
次に、この値の出所を確認します。メソッドの呼び出し元をたどると、 print()
メソッドの printString(name)
で s
が渡され、< code> this.name はnullです。
これらの値を設定する場所を追跡
this.name
はどこに設定されますか? setName(String)
メソッド内。さらにデバッグすると、このメソッドはまったく呼び出されないことがわかります。メソッドが呼び出された場合、これらのメソッドが呼び出される順序を確認し、setメソッドはprintメソッドの 後に呼び出されません。
これは解決策を提供するのに十分です: printer.print()
を呼び出す前に printer.setName()
への呼び出しを追加します。
その他の修正
変数にはデフォルト値を指定できます(そして setName
はnullに設定されないようにすることができます):
private String name = "";
print
または printString
のいずれのメソッドでも、 nullをチェックできます。例:
printString((name == null) ? "" : name);
または、 name
常にnull以外の値を持つようにクラスを設計することができます:
public class Printer {
private final String name;
public Printer(String name) {
this.name = Objects.requireNonNull(name);
}
public void print() {
printString(name);
}
private void printString(String s) {
System.out.println(s + " (" + s.length() + ")");
}
public static void main(String[] args) {
Printer printer = new Printer("123");
printer.print();
}
}
関連項目:
まだ問題が見つかりません
問題をデバッグしようとしても解決策がない場合は、質問を投稿して詳細なヘルプを求めることができますが、これまでに試したことを必ず含めてください。少なくとも、質問にスタックトレースを含め、コードに重要な行番号をマークしてください。また、単純化してみてください
質問: NullPointerException
(NPE)の原因は何ですか?
ご存じのとおり、Java型はプリミティブ型( boolean
、 int
など)と参照型に分かれています。 Javaの参照型を使用すると、特別な値 null
を使用できます。これは、Javaで「オブジェクトなし」と言う方法です。
実際の参照であるかのようにプログラムが null
を使用しようとすると、実行時に NullPointerException
がスローされます。たとえば、次のように記述した場合:
public class Test {
public static void main(String[] args) {
String foo = null;
int length = foo.length(); // HERE
}
}
&quot; HERE&quot;とラベル付けされたステートメント null
参照で length()
メソッドを実行しようとすると、 NullPointerException
がスローされます。
nullPointerException
が発生する null
値を使用する方法は多数あります。実際、NPEを発生させずに null
で できることは次のとおりです。
- 参照変数に割り当てるか、参照変数から読み取る
- 配列要素に割り当てるか、配列要素から読み取ります(配列参照自体がnullでない場合)
- パラメータとして渡すか、結果として返す、または
-
==
または!=
演算子、またはinstanceof
を使用してテストします。
質問:NPEスタックトレースの読み方
上記のプログラムをコンパイルして実行するとします:
$ javac Test.java
$ java Test
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:4)
$
最初の観察:コンパイルが成功しました!プログラムの問題はコンパイルエラーではありません。これは runtime エラーです。 (一部のIDEは、プログラムが常に例外をスローすることを警告する場合がありますが、標準の javac
コンパイラは例外をスローしません。)
2番目の観察:プログラムを実行すると、「gobbledy-gook」の2行が出力されます。 間違っている!! それはむさぼり食いではありません。それはスタックトレースです...そして、それを注意深く読んだ場合、コードのエラーを追跡するのに役立つ重要な情報を提供します。
だから、それが言うことを見てみましょう:
Exception in thread "main" java.lang.NullPointerException
スタックトレースの最初の行は、多くのことを示しています。
- 例外がスローされたJavaスレッドの名前を示します。 (このような)1つのスレッドを持つ単純なプログラムの場合は、「メイン」になります。次に進みましょう...
- スローされた例外のフルネームを示します。つまり、
java.lang.NullPointerException
。 - 例外にエラーメッセージが関連付けられている場合、例外名の後に出力されます。
NullPointerException
は、この点で異常です。エラーメッセージが表示されることはめったにないからです。
2行目は、NPEを診断する上で最も重要な行です。
at Test.main(Test.java:4)
これは多くのことを教えてくれます:
- &quot; at Test.main&quot;
Test
クラスのmain
メソッドにいたと言います。 - &quot; Test.java:4&quot;クラスのソースファイル名を提供し、これが発生したステートメントがファイルの4行目にあることを示します。
上記のファイル内の行を数える場合、4行目は「HERE」というラベルが付いた行です。コメント。
より複雑な例では、NPEスタックトレースに多くの行があることに注意してください。ただし、2行目(最初の&quot; at&quot;行)でNPEがスローされた場所を確実に確認できます 1 。
要するに、スタックトレースは、プログラムのどのステートメントがNPEをスローしたかを明確に示します。
1-まったくそうではありません。ネストされた例外と呼ばれるものがあります...
質問:コード内のNPE例外の原因を追跡するにはどうすればよいですか
これは難しい部分です。簡単な答えは、スタックトレース、ソースコード、および関連するAPIドキュメントによって提供される証拠に論理的推論を適用することです。
null
のオブジェクトにアクセスしようとしているようです。以下の例を検討してください:
TypeA objA;
現時点では、このオブジェクトを宣言しているだけで、初期化またはインスタンス化はしていません。そして、その中のプロパティまたはメソッドにアクセスしようとするときはいつでも、 NullPointerException
をスローします。これは理にかなっています。
以下の例を参照してください:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
オブジェクトが必要な場合にアプリケーションがnullを使用しようとすると、nullポインター例外がスローされます。これらは次のとおりです。
-
null
オブジェクトのインスタンスメソッドの呼び出し。 -
null
オブジェクトのフィールドへのアクセスまたは変更。 -
null
の長さを配列であるかのように取得します。 -
null
のスロットを配列であるかのようにアクセスまたは変更します。 - Throwable値であるかのように
null
を投げます。
アプリケーションは、このクラスのインスタンスをスローして、 null
オブジェクトの他の不正使用を示す必要があります。
参照: http://docs.oracle。 com / javase / 8 / docs / api / java / lang / NullPointerException.html
null
ポインターは、どこにも指し示していないポインターです。ポインター p
を逆参照すると、「「p」に保存されている場所にあるデータを教えてください」と言います。 p
が null
ポインターである場合、 p
に保存されている場所は nowhere
であり、&quot; giveと言います場所 'nowhere'&quot;のデータを取得します。明らかに、これができないので、 null pointer exception
をスローします。
一般的に、何かが適切に初期化されていないためです。
それがどのように発生し、どのように修正するかを説明する多くの説明が既に存在しますが、 NullPointerException
をまったく回避するためにベストプラクティスに従う必要もあります。
非常に重要なのは、 final
修飾子を適切に使用することです。
&quot; final&quot;の使用Javaで適用可能な場合は常に修飾子
概要:
-
final
修飾子を使用して、適切な初期化を強制します。 - 該当する場合に空のコレクションを返すなど、メソッドでnullを返すことを避けます。
- アノテーション
@NotNull
および@Nullable
を使用する
- 高速で失敗し、アサートを使用して、nullであってはならないアプリケーション全体へのnullオブジェクトの伝播を回避します。
- 既知のオブジェクトで最初にequalsを使用:
if(&quot; knownObject&quot; .equals(unknownObject)
- toString()よりも
valueOf()
を優先します。 - ヌルセーフな
StringUtils
メソッドを使用StringUtils.isEmpty(null)
。
nullポインター例外は、オブジェクトを初期化せずに使用していることを示すインジケーターです。
たとえば、以下はコードで使用する学生クラスです。
public class Student {
private int id;
public int getId() {
return this.id;
}
public setId(int newId) {
this.id = newId;
}
}
次のコードは、nullポインター例外を示します。
public class School {
Student student;
public School() {
try {
student.getId();
}
catch(Exception e) {
System.out.println("Null pointer exception");
}
}
}
student
を使用しているが、次のように初期化するのを忘れたため
以下に示す正しいコード:
public class School {
Student student;
public School() {
try {
student = new Student();
student.setId(12);
student.getId();
}
catch(Exception e) {
System.out.println("Null pointer exception");
}
}
}
Javaでは、すべて(プリミティブ型を除く)はクラスの形式です。
オブジェクトを使用する場合、2つのフェーズがあります:
- 宣言
- 初期化
例:
- 宣言:
オブジェクトオブジェクト;
- 初期化:
object = new Object();
配列の概念と同じ:
- 宣言:
Item item [] = new Item [5];
- 初期化:
item [0] = new Item();
初期化セクションを指定しない場合、 NullPointerException
が発生します。
Java で宣言するすべての変数は、実際には&quot; references&quot;です。オブジェクト自体ではなく、オブジェクト(またはプリミティブ)に。
1つのオブジェクトメソッドを実行しようとすると、参照は生きているオブジェクトにそのメソッドの実行を要求します。しかし、参照がNULL(nothing、zero、void、nada)を参照している場合、メソッドを実行する方法はありません。その後、ランタイムはNullPointerExceptionをスローしてこれを知らせます。
参照は「ポインティング」ですnullに、したがって&quot; Null-&gt;ポインタ&quot;。
オブジェクトはVMメモリ空間に存在し、それにアクセスする唯一の方法は this
参照を使用することです。次の例をご覧ください:
public class Some {
private int id;
public int getId(){
return this.id;
}
public setId( int newId ) {
this.id = newId;
}
}
そしてコードの別の場所:
Some reference = new Some(); // Point to a new object of type Some()
Some otherReference = null; // Initiallly this points to NULL
reference.setId( 1 ); // Execute setId method, now private var id is 1
System.out.println( reference.getId() ); // Prints 1 to the console
otherReference = reference // Now they both point to the only object.
reference = null; // "reference" now point to null.
// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );
// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...
これは知っておくべき重要なことです-オブジェクトへの参照がなくなると(上記の例では reference
と otherReference
が両方ともnullを指す)、オブジェクト「到達不能」です。これを操作する方法はないため、このオブジェクトはガベージコレクションの準備ができており、ある時点でVMはこのオブジェクトが使用するメモリを解放し、別のメモリを割り当てます。
NullPointerException
の別の発生は、オブジェクト配列を宣言し、すぐにその内部の要素を間接参照しようとしたときに発生します。
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
比較の順序を逆にすると、この特定のNPEを回避できます。つまり、保証されたnull以外のオブジェクトで .equals
を使用します。
配列内のすべての要素共通の初期値に初期化されます;どのタイプのオブジェクト配列でも、すべての要素が null
であることを意味します。
配列の要素を初期化する必要があります 要素にアクセスまたは逆参照します。
String[] phrases = new String[] {"The bird", "A bird", "My bird", "Bird"};
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}