静态修饰符如何影响这个代码?

这是我的代码:

class A { static A obj = new A(); static int num1; static int num2=0; private A() { num1++; num2++; } public static A getInstance() { return obj; } } public class Main{ public static void main(String[] arg) { A obj = A.getInstance(); System.out.println(obj.num1); System.out.println(obj.num2); } } 

输出是1 0 ,但我不明白。

有人可以给我解释吗?

在Java中有两个阶段:1.识别,2.执行

  1. 识别阶段,所有的静态variables都被检测到并用默认值初始化。

    所以现在的价值是:
    A obj=null
    num1=0
    num2=0

  2. 第二阶段, 执行 ,从上到下。 在Java中,执行从第一个静态成员开始。
    这里你的第一个静态variables是static A obj = new A(); ,所以首先创build该variables的对象并调用构造函数,因此num1num2的值为1
    然后,再次, static int num2=0; 将被执行,这使得num2 = 0;

现在,假设你的构造函数是这样的:

  private A(){ num1++; num2++; System.out.println(obj.toString()); } 

这将抛出一个NullPointerException因为obj仍然没有得到class A的引用。

static修饰符在应用于variables声明时意味着variables是类variables而不是实例variables。 换句话说……只有一个num1variables,而且只有一个num2variables。

(除此之外,静态variables在一些其他语言中就像是一个全局variables,除了它的名字在任何地方都是不可见的,即使它被声明为public static ,只有在当前类中声明了非限定名或一个超类,或者如果它是使用静态导入导入的,那就是区别,一个真正的全局在任何地方都是可见的。

所以当你引用obj.num1obj.num2 ,实际上是指实际指定为A.num1A.num2 静态variables。 同样,当构造函数递增num1num2 ,它将分别递增相同的variables。

在你的例子中令人困惑的皱纹是在类初始化。 一个类通过初始化默认初始化所有静态variables,然后按照它们出现在类中的顺序执行声明的静态初始化程序(和静态初始化块)来初始化。 在这种情况下,你有这样的:

 static A obj = new A(); static int num1; static int num2=0; 

它是这样发生的:

  1. 静态从他们的默认初始值开始; A.objnullA.num1 / A.num2为零。

  2. 第一个声明( A.obj )创buildA()的实例, A()的构造函数增加A.num1A.num2 。 当声明完成时, A.num1A.num2都是1A.obj是指新构build的A实例。

  3. 第二个声明( A.num1 )没有初始值设定项,所以A.num1不会改变。

  4. 第三个声明( A.num2 )有一个初始值设定项,赋值为A.num2

因此,在类初始化结束时, A.num11A.num20 …这就是你的打印语句显示的内容。

这个令人困惑的行为实际上是由于你在静态初始化完成之前创build了一个实例,而你正在使用的构造函数依赖于并修改了一个尚未被初始化的静态。 这个东西,你应该避免在真正的代码做。

1,0是正确的。

当类加载时,所有的静态数据在oder中被初始化,它们被声明。 默认情况下,int是0。

  • 第一个A被创build。 num1和num2变成1和1
  • static int num1; 什么也没做
  • static int num2=0; 这写0到num2

这是由于静态初始化器的顺序。 类中的静态expression式按照自上而下的顺序进行评估。

第一个被调用的是A的构造函数,它将num1num2都设置为1:

static A obj = new A();

然后,

 static int num2=0; 

被调用并且再次设置num2 = 0。

这就是为什么num1是1而num2是0的原因。

作为一个方面说明,构造函数不应该修改静态variables,这是非常糟糕的devise。 相反,尝试一种不同的方法来实现Java中的Singleton 。

JLS中的一个部分可以find: §12.4.2 。

详细的初始化过程:

接下来,按照文本顺序执行类的类variables初始值设定项和静态初始值设定项或接口的字段初始值设定项, 就像它们是一个单独的块一样,除了最后的类variables和值为编译的接口的字段时间常量首先被初始化

所以这三个静态variables将按照文本顺序逐一进行初始化。

所以

 static A obj = new A(); //num1 = 1, num2 = 1; static int num1; //this is initilized first, see below. static int num2=0; //num1 = 1, num2 = 0; 

如果我将订单更改为:

 static int num1; static int num2=0; static A obj = new A(); 

结果将是1,1

请注意, static int num1; 不是一个variables初始值设定项,因为( §8.3.2 ):

如果一个字段声明符包含一个variables初始值设定项,那么它对所声明的variables具有一个赋值语义(§15.26),并且:如果声明符是一个类variables(即,一个静态字段),那么variables初始值设定项是评估和分配执行一次,当这个类被初始化

这个类variables在类创build时被初始化。 这首先发生( §4.12.5 )。

程序中的每个variables在使用其值之前都必须有一个值:创build时,每个类variables,实例variables或数组组件都使用默认值进行初始化(第15.9节,第15.10节):对于字节types,默认值是零,也就是(字节)0的值。 对于shorttypes,默认值为零,也就是(short)0的值。 对于inttypes,默认值为零,即0。对于longtypes,默认值为零,即0L。 对于浮点types,默认值为正零,即0.0f。 对于doubletypes,默认值为正零,即0.0d。 对于chartypes,默认值是空字符,即'\ u0000'。 对于布尔types,默认值为false。 对于所有的引用types(§4.3),默认值为null。

也许这将有助于以这种方式来思考。

类是对象的蓝图。

对象在实例化时可以有variables。

类也可以有variables。 这些被声明为静态的。 所以它们被设置在类而不是对象实例上。

你只能在应用程序中拥有任何一个类,所以它就像专门用于该类的全局存储一样。 这些静态variables当然可以在你的应用程序的任何地方被访问和修改(假设它们是公开的)。

下面是一个“Dog”类的示例,它使用静态variables来跟踪它创build的实例的数量。

“狗”类是云,而橙色框是“狗”实例。

狗类

阅读更多

希望这可以帮助!

如果你觉得有些琐事,这个想法是柏拉图最初提出的

Java中使用static关键字主要用于内存pipe理。 我们可以将static关键字应用于variables,方法,块和嵌套类。 静态关键字属于类而不是类的实例。有关静态关键字的简要说明:

http://www.javatpoint.com/static-keyword-in-java

上面的许多答案是正确的。 但真的要说明发生了什么,我已经做了一些小的修改如下。

正如上面多次提到的那样,发生的事情是在A类完全加载之前正在创build类A的一个实例。 所以没有观察到正常的“行为”。 这与从可以被覆盖的构造函数中调用方法不太相似。 在这种情况下,实例variables可能不是直观的状态。 在这个例子中,类variables不是直观的状态。

 class A { static A obj = new A(); static int num1; static int num2; static { System.out.println("Setting num2 to 0"); num2 = 0; } private A() { System.out.println("Constructing singleton instance of A"); num1++; num2++; } public static A getInstance() { return obj; } } public class Main { public static void main(String[] arg) { A obj = A.getInstance(); System.out.println(obj.num1); System.out.println(obj.num2); } } 

输出是

 Constructing singleton instance of A Setting num2 to 0 1 0 

java不会初始化任何静态或非静态数据成员的值,直到它不被调用,但它创build它。

所以在这里,当num1和num2将在main中被调用时,它将被初始化为值

num1 = 0 + 1; 和

NUM2 = 0;