Java 初始化构造函数中的静态变量

Java 初始化构造函数中的静态变量,java,singleton,Java,Singleton,请看以下代码: 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

请看以下代码:

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中的输出不同?

原因很简单,在第一种情况下,构造了一个
Foo
对象,但在第二种情况下,构造了两个
Foo
对象

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


现在在案例1中,您只需调用一个方法,该方法返回已构造的
sigleton
对象,因此不再调用其他构造函数。在案例2中,您显式地构造了一个新的
Foo
对象,但是仍然会构造
sigleton
。因此,在后一种情况下,将创建两个对象,构造函数总共运行两次,因此
count1
count2
将比您的问题更有趣的是
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


底线:不要写那种代码

在案例2中,构造函数
Foo()
被调用两次,一次是在初始化
私有静态Foo
时调用,另一次是从main方法调用。如果使用
getInstance()
方法获取Foo,则不会发生第二次调用。

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
大致相当于(因为它们在语法上不等价)

(在调用构造函数之前,所有非静态的代码块都将在调用构造函数之前运行。不过,这与OP的代码块关系不大。)

从上面的方案中,将调用
Foo()
构造函数。和
count1
count2
将被初始化为1

执行
singleton=new Foo()
初始值设定项后,它将继续执行初始化
count2=0
,并有效地将
count2
设置为0

此时,我们将进入
main()
函数并打印输出。如前所述,如果第二次调用构造函数,则非静态代码块将在构造函数之前运行,并且再次调用构造函数以将
count1
count2
的值分别递增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);
    } 
}

这不是一个有效的代码!在初始化f并调用t.count1时不能声明f??更有趣的问题是为什么count1和count2不同。
sigleton
的正确名称是
singleton
。在构造函数中设置静态字段是个坏主意。这通常会导致混淆和编程错误。如果您有一个单例,请明确说明,而不是使用静态/非静态混合对象。这段代码揭示了Java中
静态
和非静态代码块的执行顺序
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);
    } 
}