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 may not have been initialized
」と通知しますが、オブジェクトを直接作成しないコードを書くこともあります。
たとえば、次のような方法があります。
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ポインタは文字通りwhereを指していませんこれは、無効な場所を指すのとは微妙に異なります。)
開始するのに良い場所は 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値を識別する
最初のステップは正確にどの値が例外を引き起こしているを識別することです。そのためには、デバッグをする必要があります。 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
メソッド内)でスローされているのがわかります。行を見て、logging statementsを追加するか、デバッガを使用して、どの値がnullであるかを確認します。 s
がnullであることがわかり、それに対してlength
メソッドを呼び出すと例外がスローされます。 s.length()
がメソッドから削除されると、プログラムは例外をスローしなくなります。
これらの値がどこから来るかをトレースします
次に、この値の由来を確認してください。メソッドの呼び出し元をたどると、printString(name)
メソッドでs
がprint()
とともに渡され、this.name
がnullになることがわかります。
これらの値を設定する場所をトレースします
this.name
はどこに設定されていますか? setName(String)
メソッド内。もう少しデバッグすると、このメソッドはまったく呼び出されないことがわかります。このメソッドが呼び出された場合は、必ずこれらのメソッドが呼び出されるorderを確認してください。setメソッドはafter 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
name__(NPE)の原因は何ですか?ご存じのとおり、Java型はプリミティブ型(boolean
name__、int
name__など)と参照型に分けられます。 Javaの参照型では、特殊な値null
name__を使用できます。これは、Javaで「オブジェクトなし」と表現されているためです。
プログラムが実際の参照であるかのようにNullPointerException
name__を使用しようとすると、実行時にnull
name__がスローされます。たとえば、次のように書きます。
public class Test {
public static void main(String[] args) {
String foo = null;
int length = foo.length(); // HERE
}
}
"HERE"というラベルの文は、null
name__参照に対してlength()
メソッドを実行しようとしますが、これはNullPointerException
name__をスローします。
null
name__になるNullPointerException
name__値を使用できる方法はたくさんあります。実際、NPEを引き起こさずにnull
name__に対してcanを実行する唯一のことは、
==
演算子または!=
演算子、またはinstanceof
name__を使用してテストしてください。上記のプログラムをコンパイルして実行したとします。
$ javac Test.Java
$ Java Test
Exception in thread "main" Java.lang.NullPointerException
at Test.main(Test.Java:4)
$
最初の観察:コンパイルは成功しました!プログラムの問題はコンパイルエラーではありません。これはruntimeエラーです。 (IDEによっては、プログラムが常に例外をスローするように警告する場合がありますが、標準のjavac
name__コンパイラではそうではありません。)
2番目の観察:プログラムを実行すると、2行の "gobbledy-gook"が出力されます。 違う!! ゴブリーグックじゃない。これはスタックトレースであり、注意深く読むのに時間がかかる場合はコード内のエラーを追跡するのに役立つ重要情報を提供します。
それではそれが言うことを見てみましょう:
Exception in thread "main" Java.lang.NullPointerException
スタックトレースの最初の行は、いくつかのことを示しています。
Java.lang.NullPointerException
。NullPointerException
name__は、エラーメッセージを表示することがめったにないので、この点で珍しいです。2行目は、NPEを診断する上で最も重要な行です。
at Test.main(Test.Java:4)
これは私たちに多くのことを伝えます:
main
name__クラスのTest
name__メソッドにいたことを示しています。上記のファイル内の行数を数えると、4行目が "HERE"コメントの付いた行です。
より複雑な例では、NPEスタックトレースに多数の行があることに注意してください。しかし、2行目(最初の "at"行)で、NPEがスローされた場所がわかることを確認できます。1。
つまり、スタックトレースは、プログラムのどのステートメントがNPEをスローしたのかを明確に示しています。
1 - そうではありません。入れ子の例外と呼ばれるものがあります...
これは難しい部分です。簡単な答えは、スタックトレース、ソースコード、および関連するAPIドキュメントによって提供される証拠に論理的な推論を適用することです。
まず上の簡単な例で説明しましょう。まず、スタックトレースから、NPEが発生した場所について説明された行を調べます。
int length = foo.length(); // HERE
どうすればNPEを投げることができますか?
実際には1つの方法しかありません。foo
name__がnull
name__という値を持つ場合にのみ発生します。それからnull
name__でlength()
メソッドを実行してみてください。
しかし、NPEがlength()
メソッド呼び出しの内側でスローされた場合はどうでしょうか?
それが起こったとしたら、スタックトレースは違って見えるでしょう。最初の "at"行は、例外がJava.lang.String
クラスのある行でスローされたことを示し、Test.Java
の4行目は2番目の "at"行になります。
null
name__はどこから来たのでしょうか。この場合それは明白です、そしてそれを修正するために我々がする必要があることは明白です。 (foo
name__にNULL以外の値を割り当ててください。)
それでは、もう少し複雑な例を試してみましょう。これには論理的な控除が必要になります。
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
name__の値がnull
name__の場合、bar[pos]
はNPEをスローします。bar[pos]
の値がnull
name__である場合、それにlength()
を呼び出すとNPEがスローされます。次に、これらのシナリオのどれが実際に起こっているのかを説明する必要があります。最初のものから始めましょう。
bar
name__はどこから来たのですか?これはtest
name__メソッド呼び出しのパラメーターであり、test
name__がどのように呼び出されたかを見ると、それがfoo
name__静的変数から来ていることがわかります。また、foo
name__をnull以外の値に初期化したことがわかります。この説明を暫定的に却下するにはこれで十分です。 (理論的には、他に何か{変更foo
name__}をnull
name__にすることができますが、それはここでは起こりません。)
それでは、2番目のシナリオはどうでしょうか。 pos
name__は1
であることがわかります。つまり、foo[1]
はnull
name__でなければなりません。それは可能ですか?
なるほど!そしてそれが問題です。このように初期化すると:
private static String[] foo = new String[2];
2つの要素これらはnull
name__に初期化されています _を持つString[]
を割り当てます。その後、foo
...の内容は変更されていないので、foo[1]
はnull
name__のままになります。
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
ポインタはどこも指していないものです。ポインタp
を間接参照すると、 "p"に格納されている位置のデータを返す。p
がnull
ポインタになると、p
に格納されている位置はnowhere
となる。明らかに、これはできないので、null pointer exception
をスローします。
一般に、何かが正しく初期化されていないためです。
それがどのように起こり、どのようにそれを修正するかを説明するために多くの説明が既に存在していますが、NullPointerException
を避けるために ベストプラクティス に従うべきです。
以下も参照してください。 ベストプラクティスの良いリスト
非常に重要なこととして、final
修飾子をうまく利用することを付け加えます。 Javaで該当する場合はいつでも "final"修飾子を使う
要約:
final
修飾子を使用してください。@NotNull
と@Nullable
を使うif("knownObject".equals(unknownObject)
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 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
の別の出現は、オブジェクト配列を宣言した後すぐにその内部の要素を間接参照しようとしたときに発生します。
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));
}