0

次のコードを見てください。

public class Foo {
    private static Foo sigleton=new Foo();
    private static int count1;
    private static int count2=0;

    private Foo (){
        count1++;
        count2++;
    }

    public static Foo getInstance(){
        return sigleton;
    }

    public static void main(String[] args) {
        //Foo f= Foo.getInstance(); // case 1
        //Foo f= new Foo(); // case 2
        System.out.println(f.count1);
        System.out.println(f.count2);
    } 
}

実行ごとに、メイン メソッドの行の 1 つをコメント解除します。

ケース 1 とケース 2 の出力が異なるのはなぜですか?

4

4 に答える 4

6

最初のケースでは 1 つのFooオブジェクトが構築されますが、2 番目のケースでは 2 つのFooオブジェクトが構築されるためです。

フィールドを静的に初期化するsigletonため、クラスがロードされると、Fooコンストラクターが常に呼び出されます (フィールド初期化子に指定したとおり)。

ケース 1 では、すでに構築されているオブジェクトを返すmethodを呼び出すだけなので、それ以上のコンストラクターは呼び出されません。sigletonケース 2 では、新しいFooオブジェクトを明示的に構築しますが、sigletonは引き続き構築されます。したがって、この後者の場合、2 つのオブジェクトが作成され、コンストラクターが合計 2 回実行されるためcount1count2と が 1 つ大きくなります。

于 2012-07-06T08:40:05.103 に答える
3

あなたの質問よりもさらに興味深いのは、count1 == count2 + 1.

その理由は、静的宣言がコンパイラによって検出された順序で実行されるため、Foo への最初の呼び出しで次のステートメントが呼び出されるためです。

private static Foo sigleton = null; //default value for objects
private static int count1 = 0; //default value for int
private static int count2 = 0; //default value for int

sigleton = new Foo(); //first static initializer => count1 = 1 AND count2 = 1
count2 = 0; //second static initializer

その時点から、count1 と count2 は一緒にインクリメントされ、常にcount1 == count2 + 1.

結論: そのようなコードを書かないでください!

于 2012-07-06T08:48:10.853 に答える
2

Javaは、最初に静的変数をデフォルト値(null、0、および同等のもの)に初期化します。これは、初期値が指定されているかどうかに関係なく行われます。

次に、クラスの最初から最後まで、コードのすべての静的ブロック(静的メソッドではありません!)を実行します。初期化もコードのブロックと見なされ、ファイルで指定された順序で実行されます。したがって、そのようなコード:

// static block of code in front

static Type variable = initialValue;

// static block of code in-between

static OtherType anotherVariable = someInitialValue;

// static block of code below

ほぼ同等です(構文的に同等ではないため、ほぼ同等です)

static Type variable;
static OtherType anotherVariable;

// static block of code in front

static {
    variable = initialValue;
}

// static block of code in-between

static {
    anotherVariable = someInitialValue;
}

// static block of code below

(コンストラクターを呼び出す直前に、コンストラクターが呼び出される前にすべての非静的コードブロックが実行されます。ただし、OPのコードにはあまり関係ありません。)

上記のスキームから、Foo()コンストラクターが呼び出されます。そしてcount1count21に初期化されます。

イニシャライザを実行した後、singleton = new Foo()イニシャライザを実行しcount2 = 0、実質的count2に0に設定します。

この時点で、main()関数に入り、印刷します。上記のように、コンストラクターが2回目に呼び出された場合、非静的なコードブロックがコンストラクターの前に実行され、コンストラクターが再度呼び出されて、値count1count2それぞれが1ずつインクリメントされます。このステップで奇妙なことは何も起こりません。

このコードをコンパイルして実行し、効果を確認できます。

class Foo {
    static {
      System.out.println("static 0: " + Foo.sigleton + " " + Foo.sigleton.count1 + " " + Foo.sigleton.count2);
    }

    private static Foo sigleton=new Foo();

    static {
      System.out.println("static 1: " + sigleton + " " + sigleton.count1 + " " + sigleton.count2);
    }

    private static int count1;

    static {
      System.out.println("static 2: " + sigleton + " " + sigleton.count1 + " " + sigleton.count2);
    }

    private static int count2=0;

    static {
      System.out.println("static 3: " + sigleton + " " + count1 + " " + count2);
    }

    {
      System.out.println("non-static 1: " + sigleton + " " + count1 + " " + count2);
    }

    private Foo (){
        count1++;
        count2++;

        System.out.println(count1 + " " + count2);
    }

    {
      System.out.println("non-static 2: " + sigleton + " " + count1 + " " + count2);
    }

    public static Foo getInstance(){
        return sigleton;
    }

    public static void main(String[] args) {
        Foo f= Foo.getInstance(); // case 1
        System.out.println(f.count1);
        System.out.println(f.count2);
        Foo t= new Foo(); // case 2
        System.out.println(t.count1);
        System.out.println(t.count2);
    } 
}
于 2012-07-06T09:29:49.547 に答える
1

ケース 2 では、コンストラクターFoo()が 2 回呼び出されます。1 回目は初期化時に、2 回private static Foo目はメイン メソッドから呼び出されます。getInstance()メソッドを使用して Foo を取得する場合、この 2 番目の呼び出しは発生しません。

于 2012-07-06T08:48:37.797 に答える