32

このことを考慮:

public class TestClass {

    private String a;
    private String b;

    public TestClass()
    {
    a = "initialized";
    }

    public void doSomething()
    {
    String c;

        a.notify(); // This is fine
    b.notify(); // This is fine - but will end in an exception
    c.notify(); // "Local variable c may not have been initialised"
    }

}

理解できません。"b" は初期化されませんが、コンパイル時エラーである "c" と同じ実行時エラーが発生します。ローカル変数とメンバーの違いはなぜですか?

編集:メンバーを非公開にすることは私の当初の意図であり、疑問はまだ残っています...

4

7 に答える 7

44

言語はそれをこのように定義します。

オブジェクトタイプのインスタンス変数は、デフォルトでnullに初期化されます。オブジェクトタイプのローカル変数はデフォルトでは初期化されておらず、未定義の変数にアクセスするのはコンパイル時エラーです。

SE7についてはセクション4.12.5を参照してください(SE14と同じセクション) http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

于 2008-11-06T14:23:40.027 に答える
24

これが取引です。電話すると

TestClass tc = new TestClass();

このnewコマンドは、次の 4 つの重要なタスクを実行します。

  1. 新しいオブジェクトのヒープにメモリを割り当てます。
  2. クラス フィールドをデフォルト値に戻します (数値を 0 に、ブール値を にfalse、オブジェクトを にnull)。
  3. コンストラクターを呼び出します (フィールドを再初期化する場合としない場合があります)。
  4. 新しいオブジェクトへの参照を返します。

したがって、フィールド「a」と「b」は両方とも開始されnull、「a」はコンストラクターで再開始されます。このプロセスはメソッド呼び出しには関係がないため、ローカル変数 'c​​' は初期化されません

HTH

PS: 重度の不眠症の方は、こちらをお読みください。

于 2008-11-06T14:41:21.253 に答える
14

明確な割り当てのルールは非常に難しいです (JLS 第 3 版の第 16 章を読んでください)。フィールドに明確な代入を強制することは実用的ではありません。現状では、初期化される前に final フィールドを観察することさえ可能です。

于 2008-11-06T15:59:18.367 に答える
3

コンパイラーは、cが設定されないことを理解できます。b変数は、コンストラクターが呼び出された後、doSomething()の前に他の誰かによって設定される可能性があります。bをプライベートにすると、コンパイラが役立つ場合があります。

于 2008-11-06T14:16:52.113 に答える
3

コンパイラーは、doSomething()のコードから、cがそこで宣言されており、初期化されていないことを知ることができます。ローカルであるため、他の場所で初期化される可能性はありません。

doSomething()をいつどこで呼び出すかはわかりません。bはパブリックメンバーです。メソッドを呼び出す前に、他のコードで初期化することは完全に可能です。

于 2008-11-06T14:18:19.053 に答える
2

メンバー変数は、null に初期化されるか、プリミティブの場合は既定のプリミティブ値に初期化されます。

ローカル変数は UNDEFINED であり、初期化されません。初期値の設定はユーザーの責任です。コンパイラは、それらを使用できないようにします。

したがって、c が未定義である間にクラス TestClass がインスタンス化されると、b が初期化されます。

注: null は undefined とは異なります。

于 2012-04-14T06:59:25.967 に答える
1

一般に、実行時ではなく編集/コンパイル時にエラーを見つけようとする Java のシステムの大きな穴の 1 つを実際に特定しました。

この問題を回避するには、いくつかのパターンがあります。1つ目は「デフォルトで最終」です。メンバーが最終的なものである場合、コンストラクターでそれらを埋める必要があります-そして、パス分析を使用して、すべての可能なパスが最終的に満たされるようにします(目的を無効にする「Null」を割り当てることもできますが、少なくとも、意図的にやっていることを認識せざるを得ないでしょう)。

2 番目のアプローチは、厳密な null チェックです。プロジェクトまたはデフォルトのプロパティのいずれかで、Eclipse設定でオンにすることができます。呼び出す前に、 b.notify() をヌルチェックする必要があると思います。これはすぐに手に負えなくなる可能性があるため、物事を簡単にするために一連の注釈を使用する傾向があります。

注釈の名前は異なる場合がありますが、概念的には、厳密な null チェックをオンにすると、注釈の変数の型は「nullable」および「NotNull」になります。Nullable を非 null 変数に配置しようとする場合は、最初に null をチェックする必要があります。パラメーターと戻り値の型にも注釈が付けられるため、null 以外の変数に代入するたびに null をチェックする必要はありません。

「NotNullByDefault」パッケージ レベルの注釈もあり、Nullable のタグを付けない限り、どの変数も null 値を持つことができないとエディターに想定させます。

これらの注釈は主にエディター レベルで適用されます。Eclipse やおそらく他のエディター内でオンにすることができます。これが、必ずしも標準化されていない理由です。(少なくとも最後に確認したとき、Java 8にはまだ見つかっていない注釈がいくつかある可能性があります)

于 2017-01-26T19:21:20.293 に答える