web-dev-qa-db-ja.com

NullPointerExceptionとは何ですか、どうすれば修正できますか?

NULLポインタ例外(Java.lang.NullPointerException)とは何ですか?また、それらの原因は何ですか?

例外を発生させてプログラムが途中で終了しないようにするために、原因を特定するためにどのような方法やツールを使用できますか。

210
Ziggy

参照変数(つまりオブジェクト)を宣言すると、実際にはオブジェクトへのポインタが作成されます。プリミティブ型intの変数を宣言する次のコードを検討してください。

int x;
x = 10;

この例では、変数xintであり、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);

その場合、objnullです。メソッドが渡されたオブジェクトに何かをすることを目的としている場合、それはプログラマのエラーであり、プログラマはデバッグのためにその情報を必要とするので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
    }
}

最後に、 スタックトレースを使用して例外と原因を特定する方法

3526

NullPointerExceptionsは、オブジェクトを参照しているかのように、メモリ内の位置を指していない参照(null)を使用しようとしたときに発生する例外です。 null参照に対してメソッドを呼び出す、またはnull参照のフィールドにアクセスしようとすると、NullPointerExceptionがトリガされます。これらは最も一般的ですが、他の方法は NullPointerException javadocページにリストされています。

おそらくNullPointerExceptionを説明するために私が思い付くことができる最も速い例のコードは以下のようになるでしょう。

public class Example {

    public static void main(String[] args) {
        Object obj = null;
        obj.hashCode();
    }

}

main内の最初の行で、Object参照objnullと等しく設定しています。これは私が参照を持っていることを意味しますが、それはどのオブジェクトも指していません。その後、私はそれがメソッドを呼び出すことによってオブジェクトを指しているかのように参照を扱うことを試みます。参照が指している場所に実行するコードがないため、これはNullPointerExceptionになります。

(これは技術的なことですが、nullを指す参照は無効なメモリ位置を指すCポインタと同じではありません。nullポインタは文字通りwhereを指していませんこれは、無効な場所を指すのとは微妙に異なります。)

834
Bill the Lizard

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値を識別する

最初のステップは正確にどの値が例外を引き起こしているを識別することです。そのためには、デバッグをする必要があります。 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)メソッドでsprint()とともに渡され、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 を参照)。

664
fgb

質問:NullPointerExceptionname__(NPE)の原因は何ですか?

ご存じのとおり、Java型はプリミティブ型booleanname__、intname__など)と参照型に分けられます。 Javaの参照型では、特殊な値nullname__を使用できます。これは、Javaで「オブジェクトなし」と表現されているためです。

プログラムが実際の参照であるかのようにNullPointerExceptionname__を使用しようとすると、実行時にnullname__がスローされます。たとえば、次のように書きます。

public class Test {
    public static void main(String[] args) {
        String foo = null;
        int length = foo.length();   // HERE
    }
}

"HERE"というラベルの文は、nullname__参照に対してlength()メソッドを実行しようとしますが、これはNullPointerExceptionname__をスローします。

nullname__になるNullPointerExceptionname__値を使用できる方法はたくさんあります。実際、NPEを引き起こさずにnullname__に対してcanを実行する唯一のことは、

  • 参照変数に代入するか、参照変数から読み込む
  • それを配列要素に代入するか、または配列要素から読み込む(ただし、配列参照自体がnullではない場合)
  • パラメータとして渡すか、結果として返す
  • ==演算子または!=演算子、またはinstanceofname__を使用してテストしてください。

質問:NPEスタックトレースを読むにはどうすればいいですか?

上記のプログラムをコンパイルして実行したとします。

$ javac Test.Java 
$ Java Test
Exception in thread "main" Java.lang.NullPointerException
    at Test.main(Test.Java:4)
$

最初の観察:コンパイルは成功しました!プログラムの問題はコンパイルエラーではありません。これはruntimeエラーです。 (IDEによっては、プログラムが常に例外をスローするように警告する場合がありますが、標準のjavacname__コンパイラではそうではありません。)

2番目の観察:プログラムを実行すると、2行の "gobbledy-gook"が出力されます。 違う!! ゴブリーグックじゃない。これはスタックトレースであり、注意深く読むのに時間がかかる場合はコード内のエラーを追跡するのに役立つ重要情報を提供します。

それではそれが言うことを見てみましょう:

Exception in thread "main" Java.lang.NullPointerException

スタックトレースの最初の行は、いくつかのことを示しています。

  • 例外がスローされたJavaスレッドの名前がわかります。 (このような)1つのスレッドを持つ単純なプログラムの場合、それは "main"になります。次へ移りましょう ...
  • スローされた例外のフルネームがわかります。すなわちJava.lang.NullPointerException
  • 例外に関連するエラーメッセージがある場合は、例外名の後に出力されます。 NullPointerExceptionname__は、エラーメッセージを表示することがめったにないので、この点で珍しいです。

2行目は、NPEを診断する上で最も重要な行です。

at Test.main(Test.Java:4)

これは私たちに多くのことを伝えます:

  • "at Test.main"は、mainname__クラスのTestname__メソッドにいたことを示しています。
  • "Test.Java:4"はクラスのソースファイル名を与えます、そしてそれはこれが起こったところのステートメントがファイルの4行目にあることを私達に告げます。

上記のファイル内の行数を数えると、4行目が "HERE"コメントの付いた行です。

より複雑な例では、NPEスタックトレースに多数の行があることに注意してください。しかし、2行目(最初の "at"行)で、NPEがスローされた場所がわかることを確認できます。1

つまり、スタックトレースは、プログラムのどのステートメントがNPEをスローしたのかを明確に示しています。

1 - そうではありません。入れ子の例外と呼ばれるものがあります...

質問:コード内のNPE例外の原因をどのようにして追跡するのですか?

これは難しい部分です。簡単な答えは、スタックトレース、ソースコード、および関連するAPIドキュメントによって提供される証拠に論理的な推論を適用することです。

まず上の簡単な例で説明しましょう。まず、スタックトレースから、NPEが発生した場所について説明された行を調べます。

int length = foo.length(); // HERE

どうすればNPEを投げることができますか?

実際には1つの方法しかありません。fooname__がnullname__という値を持つ場合にのみ発生します。それからnullname__でlength()メソッドを実行してみてください。

しかし、NPEがlength()メソッド呼び出しの内側でスローされた場合はどうでしょうか?

それが起こったとしたら、スタックトレースは違って見えるでしょう。最初の "at"行は、例外がJava.lang.Stringクラスのある行でスローされたことを示し、Test.Javaの4行目は2番目の "at"行になります。

nullname__はどこから来たのでしょうか。この場合それは明白です、そしてそれを修正するために我々がする必要があることは明白です。 (fooname__に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つの方法があります。

  • barname__の値がnullname__の場合、bar[pos]はNPEをスローします。
  • bar[pos]の値がnullname__である場合、それにlength()を呼び出すとNPEがスローされます。

次に、これらのシナリオのどれが実際に起こっているのかを説明する必要があります。最初のものから始めましょう。

barname__はどこから来たのですか?これはtestname__メソッド呼び出しのパラメーターであり、testname__がどのように呼び出されたかを見ると、それがfooname__静的変数から来ていることがわかります。また、fooname__をnull以外の値に初期化したことがわかります。この説明を暫定的に却下するにはこれで十分です。 (理論的には、他に何か{変更fooname__}をnullname__にすることができますが、それはここでは起こりません。)

それでは、2番目のシナリオはどうでしょうか。 posname__は1であることがわかります。つまり、foo[1]nullname__でなければなりません。それは可能ですか?

なるほど!そしてそれが問題です。このように初期化すると:

private static String[] foo = new String[2];

2つの要素これらはnullname__に初期化されています _を持つString[]を割り当てます。その後、foo...の内容は変更されていないので、foo[1]nullname__のままになります。

480
Stephen C

nullというオブジェクトにアクセスしようとしているようなものです。以下の例を見てください。

TypeA objA;

現時点では、このオブジェクトは 宣言された だけですが、 初期化またはインスタンス化された はありません。そして、あなたがその中の任意のプロパティまたはメソッドにアクセスしようとするときはいつでも、それは意味をなすNullPointerExceptionを投げます。

下記の例も参照してください。

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
406
Jorge Ferreira

オブジェクトが必要な場合にアプリケーションがnullを使用しようとすると、nullポインタ例外がスローされます。これらが含まれます:

  1. nullオブジェクトのインスタンスメソッドを呼び出します。
  2. nullオブジェクトのフィールドへのアクセスまたは変更。
  3. nullの長さを配列のように考えます。
  4. 配列のようにnullのスロットにアクセスまたは変更します。
  5. Throwable値のようにnullをスローします。

アプリケーションはnullオブジェクトの他の違法な使用を示すためにこのクラスのインスタンスを投げるべきです。

参照: http://docs.Oracle.com/javase/8/docs/api/Java/lang/NullPointerException.html

345
nathan1138

nullポインタはどこも指していないものです。ポインタpを間接参照すると、 "p"に格納されている位置のデータを返す。pnullポインタになると、pに格納されている位置はnowhereとなる。明らかに、これはできないので、null pointer exceptionをスローします。

一般に、何かが正しく初期化されていないためです。

318
MrZebra

それがどのように起こり、どのようにそれを修正するかを説明するために多くの説明が既に存在していますが、NullPointerExceptionを避けるために ベストプラクティス に従うべきです。

以下も参照してください。 ベストプラクティスの良いリスト

非常に重要なこととして、final修飾子をうまく利用することを付け加えます。 Javaで該当する場合はいつでも "final"修飾子を使う

要約:

  1. 良い初期化を強制するためにfinal修飾子を使用してください。
  2. 該当する場合は空のコレクションを返すなど、メソッドにnullを返さないようにします。
  3. アノテーション@NotNull@Nullableを使う
  4. 速く失敗し、nullであるべきでないときにアプリケーション全体でnullオブジェクトが伝播しないようにするには、アサートを使用します。
  5. 最初に既知のオブジェクトと等しいを使用します。if("knownObject".equals(unknownObject)
  6. ToString()よりvalueOf()を優先してください。
  7. NullセーフなStringUtilsメソッドStringUtils.isEmpty(null)を使用してください。
307
L. G.

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");
        }
    }
}
301
javid piprani

Javaでは、すべてのもの(プリミティブ型を除く)はクラスの形式です。

何かオブジェクトを使いたいのなら、2つのフェーズがあります。

  1. 宣言する
  2. 初期化

例:

  • 宣言:Object object;
  • 初期化:object = new Object();

配列の概念についても同じです。

  • 宣言:Item item[] = new Item[5];
  • 初期化:item[0] = new Item();

初期化セクションを指定していない場合は、NullPointerExceptionが発生します。

299
ashish bhatt

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...

これは知っておくべき重要なことです - オブジェクトへの参照がもうない場合(上の例ではreferenceotherReferenceが両方ともnullを指す)、そのオブジェクトは「到達不能」です。私たちがそれを扱うことができる方法がないので、このオブジェクトはガベージコレクションされる準備ができていて、いつか、VMはこのオブジェクトによって使用されていたメモリを解放し、別のものを割り当てます。

295
OscarRyz

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));
}
272
Makoto