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

对于每次运行,取消注释 main 方法中的一行。

为什么情况 1 和 2 的输出不同?

4

4 回答 4

6

仅仅因为在第一种情况下,Foo构造了一个对象,而在第二种情况下,Foo构造了两个对象。

您静态地初始化sigleton字段 - 因此当加载类时,Foo始终调用构造函数(如您为字段初始化程序指定的那样)。

现在在案例 1 中,您只是调用一个方法,它返回sigleton已经构造的对象 - 因此不再调用构造函数。在情况 2 中,您显式构造了一个新Foo对象 - 但sigleton仍将构造该对象。因此,在后一种情况下,创建了两个对象,构造函数总共运行了两次,因此count1count2是一个更大的对象。

于 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()将调用构造函数。并且将被初始化为 1 count1count2

执行完singleton = new Foo()初始化器后,会继续执行初始化count2 = 0,并有效设置count2为0。

此时,我们将进入main()函数,并打印出来。如果构造函数被第二次调用,如上所述,非静态代码块将在构造函数之前运行,然后再次调用构造函数以将 和 的值加count11。count2这一步没有什么奇怪的事情发生。

可以尝试编译运行这段代码看看效果:

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 次,一次是在初始化时调用,private static Foo第二次是从 main 方法调用。getInstance()如果使用该方法获取 Foo,则不会发生第二次调用。

于 2012-07-06T08:48:37.797 回答