请查看以下代码:
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的输出不同?
答案 0 :(得分:6)
仅仅因为在第一种情况下,构造了一个Foo
对象,但在第二种情况下,构造了两个Foo
个对象。
您静态初始化sigleton
字段 - 因此在加载类时,始终会调用Foo
构造函数(正如您为字段初始化程序指定的那样)。
现在在案例1中,您只是调用方法,它返回已经构造的sigleton
对象 - 因此不再调用其他构造函数。在案例2中,您显式构造了一个新的Foo
对象 - 但仍将构造sigleton
。因此,在后一种情况下,会创建两个对象,构造函数总共运行两次,因此count1
和count2
将更大一个。
答案 1 :(得分: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
。
底线:不要写那种代码!
答案 2 :(得分: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()
构造函数。 count1
和count2
将初始化为1。
执行singleton = new Foo()
初始化程序后,它将继续执行初始化count2 = 0
,并有效地将count2
设置为0。
此时,我们将输入main()
功能,然后打印出来。如果第二次调用构造函数,如上所述,将在构造函数之前运行非静态代码块,并且将再次调用构造函数以增加count1
和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);
}
}
答案 3 :(得分:1)
在情况2中,构造函数Foo()
被调用2次,一次是在初始化private static Foo
时,第二次是从main方法调用。如果使用getInstance()
方法获取Foo,则不会发生第二次调用。