static修飾子はこのコードにどのように影響しますか?


109

これが私のコードです:

class A {
    static A obj = new A();
    static int num1;
    static int num2=0;

    private A() {
        num1++;
        num2++;
    }
    public static A getInstance() {
        return obj;
    }
}

public class Main{
    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

出力はですが1 0、理解できません。

誰かがそれを私に説明できますか?


10
いい質問だ!これから何を学ぶべきか:しないでください!;)
isnot2bad 2013年

回答:


116

Javaでは2つのフェーズが行われます。1。識別、2。実行

  1. 識別フェーズすべての静的変数が検出され、デフォルト値で初期化。

    したがって、値は次のとおりです。
    A obj=null
    num1=0
    num2=0

  2. 2番目のフェーズであるexecutionは、上から下に始まります。Javaでは、最初の静的メンバーから実行が開始されます。
    ここで最初の静的変数はstatic A obj = new A();なので、最初にその変数のオブジェクトを作成してコンストラクターを呼び出します。したがって、num1およびの値はにnum2なり1ます。
    そして、再びstatic int num2=0;実行されますnum2 = 0;

ここで、コンストラクタが次のようであるとします。

 private A(){
    num1++;
    num2++;
    System.out.println(obj.toString());
 }

これがスローされますNullPointerExceptionようobj、まだの参照を持っていません class A


11
線を移動:私は拡張しますstatic A obj = new A();下にstatic int num2=0;、あなたは1と1を取得する必要があります
トーマス

2
何まだ私を混乱させるするNUM1は、明示的な初期化を持っていないにもかかわらず、それは(暗黙的に)0で初期化されているという事実が本当に明示的および暗黙の初期化に違いはありませんが...
isnot2bad

@ isnot2badの「暗黙的な初期化」は宣言の一部として発生します。宣言は、あなたがそれらをどのような順序で提示しても、割り当ての前A obj = new A(); int num1; int num2 = 0;に行われますA obj; int num1; int num2; obj = new A(); num2 = 0;。Javaはこれを行うのでnum1, num2new A()コンストラクタに到達する時間によって定義されます。
ハンスZ

31

static変数宣言に適用したときの修飾子の意味は、変数がインスタンス変数ではなくクラス変数であることです。つまり... num1変数は1つだけで、変数は1つだけnum2です。

(脇に:静的変数は他のいくつかの言語のグローバル変数にていますが、その名前はどこからでも見えるわけではありません。それがとして宣言されていてpublic staticも、非修飾名は現在のクラスまたはスーパークラスで宣言されている場合にのみ表示されます、または静的インポートを使用してインポートされた場合。それが違いです。真のグローバルは、どこにも修飾なしで表示されます。)

したがって、obj.num1およびを参照する場合、実際の指定がand である静的変数をobj.num2実際に参照しています。同様に、コンストラクタ増分は場合と、それは(それぞれ)同じ変数をインクリメントします。A.num1A.num2num1num2

あなたの例で紛らわしいしわは、クラスの初期化にあります。クラスは、最初にデフォルトですべての静的変数を初期化し、次に宣言された静的初期化子(および静的初期化子ブロック)をクラスに出現する順序で実行して初期化されます。この場合、あなたはこれを持っています:

static A obj = new A();
static int num1;
static int num2=0;

それはこのように起こります:

  1. 静力学は、デフォルトの初期値から始まります。A.objis nullおよびA.num1/ A.num2はゼロです。

  2. 最初の宣言(A.obj)は、のインスタンスA()、およびA増分A.num1とのコンストラクタを作成しますA.num2。宣言が完了したときA.num1A.num2の両方である1、およびA.obj新しく構築されたことをいうAインスタンス。

  3. 2番目の宣言(A.num1)には初期化子A.num1がないため、変更されません。

  4. 3番目の宣言(A.num2)には、ゼロを割り当てる初期化子がありますA.num2

したがって、クラスの初期化の最後に、A.num1is 1A.num2is 0...が表示されます。

この紛らわしい動作は、静的な初期化が完了する前にインスタンスを作成していること、および使用しているコンストラクターが依存しまだ初期化されていないstaticを変更していることが原因です。これは、実際のコードで行うべきではないことです。


16

1,0は正しいです。

クラスがロードされると、すべての静的データが宣言された後に初期化されます。デフォルトでは、intは0です。

  • 最初にAが作成されます。num1とnum2が1と1になる
  • static int num1;何もしないより
  • static int num2=0;これがnum2に0を書き込むより

9

静的初期化子の順序が原因です。クラスの静的式は、トップダウンの順序で評価されます。

呼び出される最初は、コンストラクタでAどのセット、num1及びnum21の両方:

static A obj = new A();

そして、

static int num2=0;

が呼び出され、num2 = 0が再度設定されます。

これがnum1が1でnum20である理由です。

補足として、コンストラクターは静的変数を変更しないでください。これは非常に悪い設計です。代わりに、Javaでシングルトン実装する別の方法を試してください。


6

JLSのセクションを見つけることができます:§12.4.2

詳細な初期化手順:

9.次に、クラスのクラス変数イニシャライザと静的イニシャライザ、またはインターフェイスのフィールド初期化子のいずれかを、それらが単一のブロックであるかのようにテキスト順に実行します。ただし、最終的なクラス変数と、値がコンパイルされるインターフェイスのフィールドは除きます。 -時定数が最初に初期化されます

したがって、3つの静的変数はテキスト順に1つずつ初期化されます。

そう

static A obj = new A();
//num1 = 1, num2 = 1;
static int num1;
//this is initilized first, see below.
static int num2=0;
//num1 = 1, num2 = 0;

注文を次のように変更した場合:

static int num1;
static int num2=0;
static A obj = new A();

結果はになります1,1

static int num1;§8.3.2)は変数初期化子ではないことに注意してください:

フィールド宣言子が変数初期化子を含む場合、宣言された変数への割り当て(15.26)のセマンティクスを持ち、宣言子がクラス変数(つまり、静的フィールド)の場合、変数初期化子はクラスが初期化されるときに評価され、割り当てが正確に1回実行される

そして、このクラス変数は、クラスが作成されるときに初期化されます。これが最初に起こります(§4.12.5)。

プログラム内のすべての変数には、その値を使用する前に値が必要です。各クラス変数、インスタンス変数、または配列コンポーネントは、作成時にデフォルト値で初期化されます(§15.9、§15.10):バイト型の場合、デフォルト値ゼロ、つまり(byte)0の値。タイプshortの場合、デフォルト値はゼロ、つまり(short)0の値です。int型の場合、デフォルト値はゼロ、つまり0です。long型の場合、デフォルト値はゼロ、つまり0Lです。float型の場合、デフォルト値は正のゼロ、つまり0.0fです。タイプdoubleの場合、デフォルト値は正のゼロ、つまり0.0dです。char型の場合、デフォルト値はnull文字、つまり「\ u0000」です。ブール型の場合、デフォルト値はfalseです。すべての参照タイプ(4.3)では、デフォルト値はnullです。


2

多分それはこのようにそれを考えるのを助けるでしょう。

クラスはオブジェクトの青写真です。

オブジェクトは、インスタンス化されるときに変数を持つことができます。

クラスは変数を持つこともできます。これらは静的として宣言されています。したがって、オブジェクトインスタンスではなくクラスに設定されます。

アプリケーション内のクラスは1つしか持つことができないため、そのクラス専用のグローバルストレージのようなものです。もちろん、これらの静的変数は、アプリケーションのどこからでもアクセスおよび変更できます(それらがパブリックである場合)。

以下は、静的変数を使用して、作成したインスタンスの数を追跡する「Dog」クラスの例です。

「犬」クラスはクラウドであり、オレンジ色のボックスは「犬」インスタンスです。

犬のクラス

続きを読む

お役に立てれば!

雑学のように感じた場合、このアイデアはプラトンによって最初に導入されました


1

staticキーワードは、主にメモリ管理のためにJavaで使用されます。変数、メソッド、ブロック、ネストされたクラスで静的キーワードを適用できます。staticキーワードは、クラスのインスタンスよりもクラスに属します。staticキーワードについて簡単に説明します。

http://www.javatpoint.com/static-keyword-in-java


0

上記の答えの多くは正しいです。しかし、実際に何が起こっているのかを説明するために、以下にいくつかの小さな変更を加えました。

上記で何度も言及したように、クラスAが完全にロードされる前にクラスAのインスタンスが作成されています。したがって、通常の「動作」と見なされるものは観察されません。これは、オーバーライド可能なコンストラクターからメソッドを呼び出すこととそれほど違いはありません。その場合、インスタンス変数は直感的な状態にならない場合があります。この例では、クラス変数は直感的な状態ではありません。

class A {
    static A obj = new A();
    static int num1;
    static int num2;
    static {
        System.out.println("Setting num2 to 0");
        num2 = 0;
    }

    private A() {
        System.out.println("Constructing singleton instance of A");
        num1++;
        num2++;
    }

    public static A getInstance() {
        return obj;
    }
}

public class Main {

    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

出力は

Constructing singleton instance of A
Setting num2 to 0
1
0

0

javaは、呼び出されずに作成されるまで、静的または非静的データメンバーの値を初期化しません。

したがって、ここでnum1およびnum2がメインで呼び出されると、値で初期化されます。

num1 = 0 + 1; そして

num2 = 0;

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.