Null ポインター例外 ( java.lang.NullPointerException
) とは何ですか? また、その原因は何ですか?
例外が原因でプログラムが途中で終了するのを防ぐために、原因を特定するためにどのような方法/ツールを使用できますか?
Null ポインター例外 ( java.lang.NullPointerException
) とは何ですか? また、その原因は何ですか?
例外が原因でプログラムが途中で終了するのを防ぐために、原因を特定するためにどのような方法/ツールを使用できますか?
参照変数 (つまり、オブジェクト) を宣言すると、実際にはオブジェクトへのポインターが作成されます。プリミティブ型の変数を宣言する次のコードを検討してくださいint
。
int x;
x = 10;
この例では、変数x
は でありint
、Java はそれを に初期化し0
ます。10
2 行目にの値を代入すると、10
が参照するメモリ位置に の値が書き込まれx
ます。
しかし、参照型を宣言しようとすると、別のことが起こります。次のコードを使用します。
Integer num;
num = new Integer(10);
最初の行では という名前の変数を宣言していますがnum
、実際にはまだプリミティブ値は含まれていません。代わりに、ポインターが含まれています (型がInteger
参照型であるため)。何を指すかをまだ指定していないため、Java はそれをnull
に設定します。これは、「私は何も指していない」ことを意味します。
2 行目ではnew
、 type のオブジェクトをインスタンス化 (または作成) するためにキーワードが使用されInteger
、ポインター変数num
がそのオブジェクトに割り当てられInteger
ます。
( NullPointerException
NPE) は、変数を宣言したが、変数の内容を使用しようとする前にオブジェクトを作成して変数に代入しなかった場合に発生します (逆参照と呼ばれます)。つまり、実際には存在しないものを指しています。
逆参照は通常、 を使用.
してメソッドまたはフィールドにアクセスする場合、または を使用[
して配列にインデックスを付ける場合に発生します。
オブジェクトを作成するnum
前に逆参照しようとすると、 NullPointerException
. 最も些細なケースでは、コンパイラは問題をキャッチして「num may not have been initialized
,」を通知しますが、オブジェクトを直接作成しないコードを記述する場合もあります。
たとえば、次のようなメソッドがあるとします。
public void doSomething(SomeObject obj) {
// Do something to obj, assumes obj is not null
obj.myMethod();
}
その場合、オブジェクトを作成しているのではなく、メソッドが呼び出されるobj
前に作成されたと想定しています。doSomething()
次のようにメソッドを呼び出すことができることに注意してください。
doSomething(null);
その場合obj
はnull
であり、ステートメントobj.myMethod()
は をスローしNullPointerException
ます。
上記のメソッドのように、メソッドが渡されたオブジェクトに対して何かを行うことを意図している場合、これはNullPointerException
プログラマーのエラーであり、プログラマーはデバッグのためにその情報を必要とするため、 をスローするのが適切です。
NullPointerException
メソッドのロジックの結果としてスローされる に加えて、メソッドの引数のnull
値をチェックし、メソッドの先頭近くに次のようなものを追加することで明示的に NPE をスローすることもできます。
// Throws an NPE with a custom error message if obj is null
Objects.requireNonNull(obj, "obj must not be null");
どのオブジェクトを にできないかをエラー メッセージに明確に記載しておくと役立つことに注意してくださいnull
。これを検証する利点は、1) 独自のより明確なエラー メッセージを返すことができること、および 2) メソッドの残りの部分については、obj
が再割り当てされない限り null ではなく、安全に逆参照できることがわかっていることです。
あるいは、メソッドの目的が、渡されたオブジェクトを操作することだけではない場合があるため、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
}
}
最後に、スタック トレースを使用して例外と原因を特定する方法
例外が原因でプログラムが途中で終了するのを防ぐために、原因を特定するためにどのような方法/ツールを使用できますか?
バグを見つけるソナーは、NPE を検出できます。 JVMによって引き起こされたnullポインター例外を動的にキャッチできますか
Java 14 では、NullPointerException の根本原因を表示する新しい言語機能が追加されました。この言語機能は、2006 年から SAP 商用 JVM の一部になっています。
Java 14 の NullPointerException 例外メッセージの例を次に示します。
スレッド「メイン」で java.lang.NullPointerException: 「リスト」が null であるため、「java.util.List.size()」を呼び出せません
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 ポインターは文字通りどこも指していません。たまたま無効な場所を指している)。
開始するのに適した場所はJavaDocsです。彼らはこれをカバーしています:
オブジェクトが必要な場合に、アプリケーションが null を使用しようとするとスローされます。これらには以下が含まれます:
- null オブジェクトのインスタンス メソッドの呼び出し。
- null オブジェクトのフィールドへのアクセスまたは変更。
- null の長さを配列であるかのように取得します。
- 配列であるかのように null のスロットにアクセスまたは変更します。
- Throwable 値であるかのように null をスローします。
アプリケーションは、このクラスのインスタンスをスローして、null オブジェクトの他の不正な使用を示す必要があります。
で null 参照を使用しようとすると、JLS に従ってsynchronized
この例外がスローされることもあります。
SynchronizedStatement: synchronized ( Expression ) Block
- それ以外の場合、Expression の値が null の場合は、a
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 値を特定する
最初のステップは、例外の原因となっている値を正確に特定することです。このために、デバッグを行う必要があります。stacktraceの読み方を学ぶことは重要です。これにより、例外がスローされた場所が表示されます。
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 であるかを確認します。が null であることがわかりs
、そのメソッドを呼び出すとlength
例外がスローされます。s.length()
をメソッドから削除すると、プログラムが例外のスローを停止することがわかります。
これらの値がどこから来たのかを追跡します
次に、この値がどこから来たのかを確認します。メソッドの呼び出し元をたどると、 がメソッドs
内で渡され、nullであることがわかります。printString(name)
print()
this.name
これらの値を設定する場所をトレースします
どこにthis.name
設定されていますか?setName(String)
メソッドで。さらにデバッグすると、このメソッドがまったく呼び出されていないことがわかります。メソッドが呼び出された場合は、これらのメソッドが呼び出された順序を確認し、set メソッドがprint メソッドの後に呼び出されていないことを確認してください。
これで解決策が得られます: を呼び出すprinter.setName()
前にへの呼び出しを追加しますprinter.print()
。
変数にはデフォルト値を設定できます (また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();
}
}
以下も参照してください。
問題をデバッグしようとしても解決策が見つからない場合は、質問を投稿してヘルプを求めることができますが、これまでに試したことを必ず含めてください。少なくとも、質問にスタックトレースを含め、コード内の重要な行番号をマークしてください。また、最初にコードを単純化してみてください ( SSCCEを参照)。
NullPointerException
質問: (NPE)の原因は何ですか?ご存じのとおり、Java 型はプリミティブ型( boolean
、int
など) と参照型に分けられます。null
Java の参照型を使用すると、「オブジェクトがない」という Java の方法である特別な値を使用できます。
プログラムが a を実際の参照であるかのように使用しようとすると、実行時にANullPointerException
がスローされます。null
たとえば、次のように記述した場合:
public class Test {
public static void main(String[] args) {
String foo = null;
int length = foo.length(); // HERE
}
}
「HERE」というラベルの付いたステートメントは、参照に対してlength()
メソッドを実行しようとします。これにより、 .null
NullPointerException
null
になる値を使用する方法はたくさんありますNullPointerException
。実際、NPE を引き起こさずにで実行できる唯一のことは次のとおりです。null
==
or!=
演算子またはを使用してテストしinstanceof
ます。上記のプログラムをコンパイルして実行するとします。
$ javac Test.java
$ java Test
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:4)
$
最初の観察: コンパイルは成功しました! プログラムの問題は、コンパイル エラーではありません。実行時エラーです。(一部の IDE は、プログラムが常に例外をスローすることを警告する場合がありますが、標準のjavac
コンパイラはそうではありません。)
2 つ目の観察: プログラムを実行すると、2 行の "gobbledy-gook" が出力されます。違う!!それはぐちゃぐちゃじゃない。これはスタックトレースです ...時間をかけて注意深く読むと、コード内のエラーを追跡するのに役立つ重要な情報が提供されます。
それで、それが何を言っているのか見てみましょう:
Exception in thread "main" java.lang.NullPointerException
スタック トレースの最初の行は、多くのことを示しています。
java.lang.NullPointerException
。NullPointerException
エラーメッセージが表示されることはめったにないため、この点で異常です。2 行目は、NPE の診断において最も重要な行です。
at Test.main(Test.java:4)
これは多くのことを教えてくれます:
main
メソッドにいたことを示しています。Test
上記のファイルの行を数えると、4 行目は "HERE" コメントでラベルを付けた行です。
より複雑な例では、NPE スタック トレースに多数の行があることに注意してください。ただし、2 行目 (最初の "at" 行) で、NPE がスローされた場所がわかります1。
つまり、スタック トレースは、プログラムのどのステートメントが NPE をスローしたかを明確に示します。
関連項目:スタック トレースとは何ですか? また、スタック トレースを使用してアプリケーション エラーをデバッグするにはどうすればよいですか?
1 - あまり当てはまりません。ネストされた例外と呼ばれるものがあります...
これは難しい部分です。簡単な答えは、スタック トレース、ソース コード、および関連する API ドキュメントによって提供される証拠に論理的推論を適用することです。
最初に簡単な例 (上記) で説明しましょう。まず、スタック トレースで NPE が発生した場所であることが示されている行を確認します。
int length = foo.length(); // HERE
それはどのようにNPEを投げることができますか?
foo
実際、方法は 1 つしかありません:値が の場合にのみ発生しますnull
。次に、length()
メソッドを実行しようとしnull
ます... BANG!
しかし、(あなたが言うのを聞いたことがあります)length()
メソッド呼び出し内で NPE がスローされた場合はどうなるでしょうか?
それが起こった場合、スタック トレースは異なるように見えます。最初の "at" 行は、例外がjava.lang.String
クラスのある行でスローされたことを示し、4 行Test.java
目は 2 番目の "at" 行になります。
それで、それはnull
どこから来たのですか?この場合、それは明らかであり、それを修正するために何をする必要があるかは明らかです。(null 以外の値を に割り当てますfoo
。)
では、もう少しトリッキーな例を試してみましょう。これには、論理的な推論が必要です。
public class Test {
private static String[] foo = new String[2];
private static int test(String[] bar, int pos) {
return bar[pos].length();
}
public static void main(String[] args) {
int length = test(foo, 1);
}
}
$ javac Test.java
$ java Test
Exception in thread "main" java.lang.NullPointerException
at Test.test(Test.java:6)
at Test.main(Test.java:10)
$
これで、2 つの "at" 行ができました。最初のものはこの行です:
return args[pos].length();
2 つ目は次の行です。
int length = test(foo, 1);
最初の行を見ると、どのようにして NPE がスローされるのでしょうか? 次の 2 つの方法があります。
bar
、null
NPEbar[pos]
がスローされます。bar[pos]
ある場合、null
それを呼び出すとlength()
NPE がスローされます。次に、これらのシナリオのどれが実際に何が起こっているのかを説明する必要があります。最初のものを調べることから始めます。
どこbar
から来たの?これはtest
メソッド呼び出しのパラメーターであり、どのように呼び出されたかを見ると、静的変数test
から来ていることがわかります。さらに、 null 以外の値foo
に初期化したことがはっきりとわかります。foo
この説明を一時的に却下するには、それで十分です。(理論的には、他の何かが... に変わる 可能性がありますが、ここではそれが起こっていません。)foo
null
では、2 番目のシナリオはどうでしょうか。pos
これはであることがわかります。1
つまり、 であるfoo[1]
必要がありますnull
。これは可能ですか?
確かにそうです!そして、それが問題です。次のように初期化すると:
private static String[] foo = new String[2];
に初期化されたString[]
2 つの要素でa を割り当てます。その後、~の内容は変更していませんので、そのままです。null
foo
foo[1]
null
Android では、NPE の直接の原因を突き止めるのは少し簡単です。例外メッセージは通常、使用している null 参照の (コンパイル時の) タイプと、NPE がスローされたときに呼び出そうとしたメソッドを示します。これにより、直接の原因を特定するプロセスが簡素化されます。
しかし一方で、Android にはプラットフォーム固有の一般的な NPE の原因がいくつかあります。非常に一般的なのは、getViewById
予期せずnull
. null
私のアドバイスは、予期しない戻り値の原因に関する Q&A を検索することです。
であるオブジェクトにアクセスしようとしているようですnull
。以下の例を検討してください。
TypeA objA;
この時点では、このオブジェクトを宣言したばかりで、初期化もインスタンス化もされていません。そして、その中のプロパティまたはメソッドにアクセスしようとすると、スロー NullPointerException
されますが、これは理にかなっています。
以下の例も参照してください。
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
オブジェクトが必要な場合にアプリケーションが null を使用しようとすると、null ポインター例外がスローされます。これらには以下が含まれます:
null
オブジェクトのインスタンス メソッドの呼び出し。null
オブジェクトのフィールドへのアクセスまたは変更。null
配列であるかのように取得します。null
あたかも配列であるかのように のスロットにアクセスまたは変更します。null
値であるかのようにスローします。アプリケーションは、このクラスのインスタンスをスローして、null
オブジェクトの他の不正使用を示す必要があります。
参照: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html
ポインターとは、null
どこにも指していないポインターです。pointer を逆参照するときは、p
「"p" に格納されている場所のデータをくれ」と言います。明らかに、これはできないので、.p
null
p
nowhere
null pointer exception
一般に、何かが適切に初期化されていないことが原因です。
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 object;
object = new Object();
配列の概念についても同じです。
Item item[] = new Item[5];
item[0] = new Item();
初期化セクションを指定していない場合は、NullPointerException
発生します。
Java では、宣言するすべての変数は、実際にはオブジェクト (またはプリミティブ) への「参照」であり、オブジェクト自体ではありません。
1 つのオブジェクト メソッドを実行しようとすると、参照は生きているオブジェクトにそのメソッドを実行するように要求します。しかし、参照が NULL (nothing、zero、void、nada) を参照している場合、メソッドが実行される方法はありません。次に、ランタイムは NullPointerException をスローしてこれを知らせます。
あなたの参照はnullを「指している」ため、「Null -> Pointer」です。
オブジェクトは 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
オブジェクト配列を宣言し、すぐにその中の要素を逆参照しようとすると、別の aが発生します。
String[] phrases = new String[10];
String keyPhrase = "Bird";
for(String phrase : phrases) {
System.out.println(phrase.equals(keyPhrase));
}
この特定の NPE は、比較順序を逆にすると回避できます。つまり、.equals
保証された非 null オブジェクトで使用します。
配列内のすべての要素は、共通の初期値に初期化されます。どのタイプのオブジェクト配列でも、すべての要素が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));
}