在下面的代码中,为什么x和y分别输出0和1?为什么当我写这段代码时:private static Singleton instance = new Singleton();
从位置①起在位置②,输出为1,1?
private static Singleton instance = new Singleton();
//①
private static int x = 0;
private static int y;
//②
private Singleton()
{
System.out.println("Starting add");
x++;
y++;
}
public static Singleton getInstance()
{
return instance;
}
public static void main(String[] args)
{
System.out.println("Starting Singleton");
Singleton singleton = Singleton.getInstance();
//Singleton singleton = new Singleton();
System.out.println(singleton.x);
System.out.println(singleton.y);
}
1条答案
按热度按时间mmvthczy1#
不要写这样的代码。正如你所发现的,这非常令人困惑。它是**,然而,根据规范。虽然很奇怪让我们通过相当多的步骤来解释这一点。这是关于复杂的,通常不相关的细节的个别细节,这些细节合并解释了这种行为。
CTC和初始化器的区别
想象一下这段代码:
显然,
MARK
的值不能在编译时确定-这被解释为“时间戳,因为它是在该字段初始化时”,这归结为“当这个类被加载时”。这意味着任何地方的代码第一次引用Foo
时,必须执行一些代码。在这个执行过程中,上下文必须存在-这样的代码在理论上可以引用MARK
。因此,
MARK
从0
开始,代码编译如下:那是法律的的Java代码(试试看;编译它)-这是一个静态初始化器。运行
javap -c -v Foo
查看这些内容。这一点:
非常不同。如果你在上面运行
javap -c -v Foo
,你会注意到根本没有静态初始化器。相反,5是所谓的CTC:编译时间常数。编译器本身解析它,并将此值直接放入类文件中。在第一种情况下,MARK
的存在包含0,然后在初始化过程中设置其值。但是,FOO
会立即以5的值存在。仅由于CTC的定义方式,在您的代码中,
x
和y
不符合条件;具体来说,x
* 没有 * 一个常量值,这是因为要符合条件,该字段必须是static
和final
。显然,singleton
也不是。因此,您的代码与以下代码相同:现在应该很明显为什么你得到0/1而不是预期的1/1了。首先,构造函数运行,
x
和y
都为零。构造函数将它们设置为1/1。然后,将x设置为0。但是...忘记这一切
真实的的问题是,这个代码很愚蠢。你不做单例和静态字段,这根本没有意义。构造函数不应该作为
static
上下文的初始化器。构造函数从根本上与示例相关。因此,永远不应该编写这些代码,因为它们本身是不一致的,您需要从头到尾了解JLS,以便了解正在发生的事情。单例的一般方法是拥有所有非静态内容,* 除了 * 包含单例及其访问器的字段:
上面的工作很好。
或者,如果你有静态的东西需要初始化,写一个实际的静态初始化器。如果你有这个,单例通常是没有意义的-只是让一切都是静态的,在这一点上单例将是毫无意义的:
工作也很好(x和y在这两个片段中都是1和1)。