16

重複の可能性:
Java の null 参照の静的フィールド

静的メソッドはクラス レベルであることを理解しています。したがって、静的メソッドを呼び出すためにインスタンスを作成する必要がないことを認識しています。しかし、静的メソッドをインスタンス メソッドのように呼び出すことができることも認識しています。NullPointerExceptionnullオブジェクトから静的メソッドを呼び出す間(インスタンスメソッドの呼び出しのように)を期待していたので、これは私が混乱している場所です。ここで期待するのが間違っていた理由について、いくつか説明していただければ幸いNullPointerExceptionです。

サンプルコードは次のとおりです。

public class SampleClass {

    public static int getSumStatic(int x, int y){
        return x+y;
    }

    public int getDifferenceInstance(int x, int y){
        return x-y;
    }
}

public class TestClass {

    public static void main (String[] args){        
    SampleClass sc=null;

    System.out.println(SampleClass.getSumStatic(2, 2)); //as expected

    //I was expecting NullPointerException in the next line, since I am accessing null object
    System.out.println(sc.getSumStatic(4,5)); //static method , executes perfectly  

    System.out.println(sc.getDifferenceInstance(6,4));//throws NullPointerException
    }
}
4

6 に答える 6

14

インスタンスを介して静的メソッドを呼び出す場合、インスタンスがそこにある必要はありません。コンパイラが変数の型を判別できる限り、式を評価しscて結果を破棄した後、同等の呼び出しを静的に行います。

System.out.println(SampleClass.getSumStatic(4,5));

Java言語仕様から:

セクション15.12.1

❖ フォームが の場合Primary.NonWildTypeArgumentsopt Identifier、メソッドの名前は識別子です。T を Primary 式の型とします。検索されるクラスまたはインターフェースは、T がクラスまたはインターフェース型の場合は T であり、T が型変数の場合は T の上限です。

セクション 15.12.4.1:

  1. Primary を含む MethodInvocation の 2 番目のプロダクションが関係している場合は、次の 2 つのサブケースがあります。

❖ 呼び出しモードが静的である場合、ターゲット参照はありません。Primary 式は評価されますが、結果は破棄されます。

于 2012-09-30T21:58:34.357 に答える
3

これは、Java 設計者によるある種の設計ミスです。class でstatic メソッドを呼び出す必要があります。これは、オブジェクトではなくクラスに属しているためです。

この問題については、Why-isnt-calling-a-static-method-by-way-of-an-instance-an-error-for-the-Java-coで少し確認できます。

面白いことに、初期化されていないオブジェクト変数で静的メソッドを呼び出すことはできません。ただし、オブジェクトがnullで初期化されている場合は、すべて問題ありません。

この変数で格納されたオブジェクトは、割り当てを通じて型情報を提供するため、これが機能すると思います。

SampleClass sampleObject;
sampleObject.getSumStatic(2, 2) 

オブジェクトが初期化されていないためコンパイルされません。そのため、Java コンパイラの構文ツリーに型情報が設定されていません

于 2012-09-30T22:06:30.140 に答える
2

Java では、参照が であっても、単に参照に基づいて静的メソッドにアクセスできますnull。参照の型だけが重要です。

通常、クラス名を使用して静的メソッドを呼び出す必要があります。

SampleClass.getSumStatic(2, 2);
于 2012-09-30T21:59:46.150 に答える
2

dasblinkenlight の (絶対に正しい) 応答に対する追加のメモとして、生成される Java バイトコードの違いを確認できます (これは で読みやすく表示されますjavap -c)。

次の(より単純な)クラスを検討してください。

public class Example {
  public static void staticMethod() {}
  public void virtualMethod() {}
}

そして、それを使用するアプリケーション:

public class ExampleApplication {
  public static void main(String[] args) {
    Example ex = null;
    Example.staticMethod();
    ex.staticMethod();
    ex.virtualMethod();
  }
}

用に生成されたバイトコードを見てみましょうExampleApplication.main(String[]):

public static void main(java.lang.String[]);
  Code:
   0:   aconst_null
   1:   astore_1
   2:   invokestatic    #2; //Method Example.staticMethod:()V
   5:   aload_1
   6:   pop
   7:   invokestatic    #2; //Method Example.staticMethod:()V
   10:  aload_1
   11:  invokevirtual   #3; //Method Example.virtualMethod:()V
   14:  return

これをステップスルーします(上記の出力の数値列であるオフセットによる):

オフセット 0 と 1 の命令がロードnullされ、ローカル変数 1 に格納されます ( ex)。

オフセット 2 の命令は、従来の静的呼び出しを行います。これは、invokestaticを呼び出す命令Example.staticMethod()です。ご想像のとおり、これにはインスタンス変数は含まれません。

次は、インスタンスの静的メソッドの呼び出しです。オフセット 5 の命令はスタックにロードexされますが (これは null であることに注意してください)、popオフセット 6 の命令はこれをすぐに元に戻します。そのためinvokestatic、オフセット 7 の動作はオフセット 2 の動作とまったく同じです。null 値は VM スタック上になく、呼び出されるメソッドはjavacの値に関係なくによってコンパイルされexます。

対照的に、仮想 (つまり、非静的) メソッドは、インスタンスをスタック (オフセット 10) にプッシュし、スタック上にある間に、実行するメソッドを見つけるためにインスタンスをinvokevirtualルックアップする命令を実行します。virtualMethod()これは、NullPointerExceptionルックアップを続行できないため、 a がスローされるステップです。(静的な場合、この手順は不要であることに注意してください。これが、単純な VM で静的メソッド呼び出しが高速になる理由でもあります。)

于 2012-09-30T22:20:13.083 に答える
1

に強く型付けされた変数を介して静的メソッドにアクセスしていますSampleClass。コンパイル中に、メソッドはクラス定義で直接呼び出されるものとして解決されるため (したがって、静的メソッドです)、実際に解決されます。

暗黙的なthisinpublic static int getSumStaticがないため、null ポインターへのアクセスはありません。

于 2012-09-30T21:58:54.293 に答える
0

クラスインスタンスインスタンスの静的関数は、基本的にコンパイル時に静的クラス情報に置き換えられると思います(インスタンスメソッドのような継承でも機能しません)。

インスタンス メソッドは null のメソッドにアクセスしようとし、NullPointerException をスローします。

于 2012-09-30T22:02:13.887 に答える