Java 静态修饰符如何影响此代码?

Java 静态修饰符如何影响此代码?,java,static,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 v

这是我的密码:

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);
    }
}
输出是
10
,但我无法理解

谁能给我解释一下吗?

1,0是正确的

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

  • 首先创建一个。num1和num2变为1和1
  • 大于
    静态int num1什么也不做
  • 小于
    static int num2=0这会将0写入num2

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

    • 识别阶段检测所有静态变量并用默认值初始化

      现在的值是:
      A obj=null

      num1=0

      num2=0

    • 第二个阶段,执行,从上到下开始。在Java中,执行从第一个静态成员开始。
      这里您的第一个静态变量是
      static A obj=new A(),因此首先它将创建该变量的对象并调用构造函数,因此
      num1
      num2
      的值变为
      1

      然后,再一次,
      static int num2=0,使
      num2=0

    • 现在,假设您的构造函数如下所示:

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

      这将抛出一个
      NullPointerException
      ,因为
      obj
      仍然没有获得
      a类的引用

      ,这是由于静态初始值设定项的顺序。类中的静态表达式按自上而下的顺序求值

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

      static A obj=new A()

      那么

      调用并再次将num2设置为0

      这就是为什么
      num1
      是1,
      num2
      是0


      作为旁注,构造函数不应该修改静态变量,这是非常糟糕的设计。相反,请尝试另一种方法。

      静态关键字在java中主要用于内存管理。我们可以将静态关键字应用于变量、方法、块和嵌套类。static关键字属于类而不是类的实例。有关static关键字的简要说明:


      当应用于变量声明时,
      静态
      修饰符的意思是,变量是类变量而不是实例变量。换句话说。。。只有一个
      num1
      变量,只有一个
      num2
      变量

      (旁白:在某些其他语言中,静态变量与全局变量类似,只是它的名称不在任何地方都可见。即使它声明为
      公共静态
      ,但只有在当前类或超类中声明,或者使用静态导入时,非限定名称才可见。这就是区别rue global在任何地方都是可见的,没有任何限制。)

      因此,当你提到
      obj.num1
      obj.num2
      时,你实际上指的是静态变量,它们的实际名称是
      A.num1
      A.num2
      。同样,当构造函数增加
      num1
      num2
      时,它分别增加相同的变量

      在您的示例中,令人困惑的问题在于类初始化。默认情况下,初始化一个类首先初始化所有静态变量,然后按照声明的静态初始值设定项(和静态初始值设定项块)在类中出现的顺序执行。在这种情况下,您有:

      static A obj = new A();
      static int num1;
      static int num2=0;
      
      事情是这样的:

       private A(){
          num1++;
          num2++;
          System.out.println(obj.toString());
       }
      
    • 静态以其默认初始值开始;
      A.obj
      null
      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.num1
      1
      A.num2
      0
      …这就是打印语句显示的内容


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

      java不会初始化任何静态或非静态数据成员,直到它未被调用但创建为止

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

      num1=0+1;以及


      num2=0;

      可以在JLS中找到一个部分:

      详细初始化过程:

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

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

      所以

      如果我将订单更改为:

      static int num1;
      static int num2=0;
      static A obj = new A();
      
      结果将是
      1,1

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

      如果字段声明符包含变量初始值设定项,则
      static int num1;
      static int num2=0;
      static A obj = new A();
      
      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