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