static修饰符如何影响此代码?

问题

这是我的代码:

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,但我无法理解。

有人可以向我解释一下吗?


#1 热门回答(115 赞)

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

  • 在识别阶段,检测所有静态变量并使用默认值进行初始化。所以现在的值是:A obj = null num1 = 0 num2 = 0
  • 第二阶段,执行,从上到下开始。在Java中,执行从第一个静态成员开始。这里你的第一个静态变量是静态的一个obj = new A();所以首先它将创建该变量的对象并调用构造函数,因此num1和num2的值变为1.然后,再次,static int num2 = 0;将被执行,这使得num2 = 0;。

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

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

这将抛出aNullPointerExceptionasobj尚未得到class A的参考。


#2 热门回答(31 赞)

staticmodifier在应用于变量声明时的含义是变量是类变量而不是实例变量。换句话说......只有一个num1变量,只有一个变量.num2变量。

(旁白:静态变量在某些其他语言中是全局变量,除了它的名称在任何地方都不可见。即使它被声明为apublic static,非限定名称只有在当前类或超类中声明时才可见,或者如果它是使用静态导入导入的。这就是区别。真正的全局在任何地方都是可见的。)

因此,当你参考obj.num1obj.num2时,你实际上是指其真实名称为A.num1A.num2的全局变量。类似地,当构造函数递增num1num2时,它正在递增相同的变量(分别)。

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

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

它发生在这样:

  • 静态以其默认初始值开始; A.obj为空,A.num1 / A.num2为零。
  • 第一个声明(A.obj)创建A()的实例,A的构造函数增加A.num1和A.num2。声明完成后,A.num1和A.num2都是1,而A.obj指的是新构造的A实例。
  • 第二个声明(A.num1)没有初始值,因此A.num1不会改变。
  • 第三个声明(A.num2)有一个初始值设定项,它为A.num2赋值。

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

这种令人困惑的行为实际上是因为你在静态初始化完成之前创建了一个实例,并且你使用的构造函数依赖于并修改了尚未初始化的静态。这是你应该避免在实际代码中做的事情。


#3 热门回答(16 赞)

1,0是正确的。

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

  • 创建第一个A. num1和num2变为1和1
  • 比静态int num1;什么也没做
  • 比静态int num2 = 0;这将0写入num2