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

lir*_*rui 109 java 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出是1 0,但我无法理解.

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

Sho*_*ate 115

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

  1. 识别阶段,检测所有静态变量并使用默认值进行初始化.

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

  2. 第二阶段,执行,从上到下开始.在Java中,执行从第一个静态成员开始.
    在这里你的第一个静态变量是static A obj = new A();,所以首先它会创建一个变量的对象,并调用构造函数,因此的价值num1num21.
    然后,再次,static int num2=0;将执行,这使num2 = 0;.

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

 private A(){
    num1++;
    num2++;
    System.out.println(obj.toString());
 }
Run Code Online (Sandbox Code Playgroud)

这将抛出一个NullPointerException作为obj仍然还没有得到的参考 class A.

  • 我将扩展:移动行`static A obj = new A();`下面`static int num2 = 0;`你应该得到1和1. (11认同)
  • 令我困惑的是,尽管num1没有显式初始化,但它(隐含地)用0初始化.显式和隐式初始化之间应该没有区别...... (2认同)

Ste*_*n C 31

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

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

因此,当您引用obj.num1和时obj.num2,您实际上是指其真实名称为和静态变量.同样,当构造增量和,它是(分别)增加相同的变量.A.num1A.num2num1num2

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

static A obj = new A();
static int num1;
static int num2=0;
Run Code Online (Sandbox Code Playgroud)

它发生如下:

  1. 静态开始时使用默认的初始值; A.objnullA.num1/ A.num2是零.

  2. 第一个declaration(A.obj)创建一个实例A(),以及A增量A.num1和的构造函数A.num2.当申报完成,A.num1并且A.num2都是1,并且A.obj是指新建的A实例.

  3. 第二个声明(A.num1)没有初始值设定项,因此A.num1不会更改.

  4. 第三个声明(A.num2)有一个初始化器,它指定零A.num2.

因此,在类初始化结束时,A.num1is 1A.num2is 0...这就是你的print语句所显示的内容.

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


Leo*_*dos 16

1,0是正确的.

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

  • 第一个A被创建.num1和num2变为1和1
  • static int num1;什么都没做
  • static int num2=0;这写0到num2


Dom*_*omi 9

这是由于静态初始化器的顺序.类中的静态表达式以自上而下的顺序进行计算.

第一个被调用的是构造函数A,它设置为num1num21:

static A obj = new A();

然后,

static int num2=0;
Run Code Online (Sandbox Code Playgroud)

被调用并再次设置num2 = 0.

这就是为什么num1是1并且num2是0.

作为旁注,构造函数不应该修改静态变量,这是非常糟糕的设计.相反,尝试使用不同的方法在Java中实现Singleton.


Sta*_*kER 6

可以找到JLS中的一节:§12.4.2.

详细的初始化程序:

9.接下来,按文本顺序执行类的类变量初始值设定项和静态初始值设定项,或接口的字段初始值设定项,就像它们是单个块一样,除了最终的类变量和值为compile的接口字段-time常量首先初始化

因此,三个静态变量将按文本顺序逐个初始化.

所以

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;
Run Code Online (Sandbox Code Playgroud)

如果我将订单更改为:

static int num1;
static int num2=0;
static A obj = new A();
Run Code Online (Sandbox Code Playgroud)

结果将是1,1.

请注意,static int num1;它不是变量初始值设定项,因为(§8.3.2):

如果字段声明符包含变量初始值设定项,则它具有声明变量的赋值(第15.26节)的语义,并且:如果声明符用于类变量(即静态字段),则变量初始值设定项为在初始化类时,评估和分配只执行一次

并且在创建类时初始化此类变量.这首先发生(§4.12.5).

程序中的每个变量在使用其值之前必须具有一个值:每个类变量,实例变量或数组组件在创建时都使用默认值进行初始化(第15.9节,第15.10节):对于类型字节,默认值为零,即(byte)0的值.对于short类型,默认值为零,即(short)0的值.对于int类型,默认值为零,即0.对于long类型,默认值为零,即0L.对于float类型,默认值为正零,即0.0f.对于double类型,默认值为正零,即0.0d.对于char类型,默认值为空字符,即'\ u0000'.对于boolean类型,默认值为false.对于所有引用类型(第4.3节),默认值为null.