当前位置: 首页 > 知识库问答 >
问题:

静态数据是如何初始化的?

太叔永新
2023-03-14

对于“何时”有很多很好的答案,比如在这个线程中——静态类初始化何时发生?现在我的问题是“如何”。这是斯蒂芬C.的回答中的一段话

类静态初始化通常在第一次发生以下事件之前立即发生:

  • 将创建该类的一个实例,
  • 调用该类的静态方法,
  • 已分配类的静态字段,
  • 使用非恒定静态场,或
  • 对于顶级类,执行词汇嵌套在类中的assert语句

那么它是如何在内部完成的呢?每个可能触发初始化的指令都用if包装?任何工作的细节:-)我都可以执行。

我用“Java”来标记这个问题,但如果我没有弄错的话,C#和Swift也会根据需要初始化静态数据。

共有2个答案

金珂
2023-03-14

对于静态常量(最终)字段,. class文件直接定义常量值,因此JVM可以在类加载时分配它。

对于非常量静态字段,编译器将合并任何初始化器和自定义静态初始化器块,以生成单个静态初始化器代码块,JVM可以在类加载时执行该代码块。

例子:

public final class Test {
    public static double x = Math.random();
    static {
        x *= 2;
    }
    public static final double y = myInit();
    public static final double z = 3.14;
    private static double myInit() {
        return Math.random();
    }
}

字段z是一个常量,而xy是运行时值,将与静态初始值设定项块合并(x*=2)。

如果使用javap-c-p-constants测试反汇编字节码。类,您将获得以下内容。我添加了空行来分隔静态初始值设定项块的合并部分(static{})。

Compiled from "Test.java"
public final class test.Test {
  public static double x;

  public static final double y;

  public static final double z = 3.14d;

  static {};
    Code:
       0: invokestatic  #15                 // Method java/lang/Math.random:()D
       3: putstatic     #21                 // Field x:D

       6: getstatic     #21                 // Field x:D
       9: ldc2_w        #23                 // double 2.0d
      12: dmul
      13: putstatic     #21                 // Field x:D

      16: invokestatic  #25                 // Method myInit:()D
      19: putstatic     #28                 // Field y:D

      22: return

  public test.Test();
    Code:
       0: aload_0
       1: invokespecial #33                 // Method java/lang/Object."<init>":()V
       4: return

  private static double myInit();
    Code:
       0: invokestatic  #15                 // Method java/lang/Math.random:()D
       3: dreturn
}

请注意,这还表明默认构造函数是由编译器创建的,并且构造函数调用超类(对象)默认构造函数。

使现代化

如果您将-v(详细)参数添加到javap,您将看到常量池,其中存储了定义上面列出的引用的值,例如,对于Math.random()调用,列出了以上为#15,相关常量为:

#15 = Methodref          #16.#18        // java/lang/Math.random:()D
#16 = Class              #17            // java/lang/Math
#17 = Utf8               java/lang/Math
#18 = NameAndType        #19:#20        // random:()D
#19 = Utf8               random
#20 = Utf8               ()D

如您所见,Math类有一个类常量(#16),它被定义为字符串“java/lang/Math”

第一次使用引用#16(这发生在执行invokestatic#15时),JVM将把它解析为一个实际的类。如果该类已经加载,它将只使用加载的类。

如果类尚未加载,则调用ClassLoader加载类(loadClass()),然后调用defineClass()方法,将字节码作为参数。在此加载过程中,通过自动分配常量值并执行先前标识的静态初始值设定项代码块,初始化该类。

正是JVM执行的这个类引用解析过程触发了静态字段的初始化。这本质上就是发生的事情,但这个过程的确切机制是JVM实现特有的,例如通过JIT(对机器代码的即时编译)。

邢杰
2023-03-14

正如在评论中提到的,这类事情可以通过segfaults来完成,但是对于Java来说,这并不是真正必要的。

请记住,Java字节码不是由机器直接执行的——在JIT编译成真正的机器指令之前,它被解释和分析以确定何时编译它,这已经涉及到为每个字节码执行大量的机器指令字节码指令。在这段时间内检查静态初始化的所有条件是没有问题的。

字节码也可以编译成带有检查的机器码,在第一次执行检查后重写或修补。这种事情的发生还有很多其他原因,比如自动内联和转义分析,所以像这样做静态初始化检查并没有什么大问题。

简而言之,有很多html" target="_blank">方法,但关键点是,当你运行一个Java程序时,除了你实际编写的代码之外,还有很多事情在进行。

 类似资料:
  • 问题内容: 我有以下代码: 这给了我以下错误: 解析错误:语法错误,在第19行的/home/user/Sites/site/registration/inc/registration.class.inc中出现意外的’(’,期待’)’ 所以,我想我做错了什么…但是如果不那样做怎么办?如果我用常规字符串更改mktime内容,它将起作用。所以,我知道我能做到这一点 的那种 像.. 有人有指针吗? 问题答

  • 问题内容: 这是一段Java代码: 它如何编译?初始化后已执行变量“ ture”的声明。据我所知,静态块和字段已经按照它们出现的顺序执行了。 现在,为什么实例块中的值9已被打印3次?顺便说一句,该类的实例已创建了3次。那不是功课,我正在学习Java进行认证。 问题答案: 关于第一个问题,静态块确实按照它们出现的顺序进行处理,但是在处理静态块之前,先处理声明。声明作为类 准备工作 的一部分(JLS§

  • 问题内容: 哪两个代码片段正确创建并初始化了一个int元素的静态数组?(选择两个。) 一个。 B. C。 D. 答案:A,B 在这里即使D看起来是真实的,谁能让我知道为什么D是错误的。 问题答案: 正确答案是1和2(或者用您的符号表示A和B),而且正确的解决方案是: 解决方案D不会自动初始化数组,因为运行时会加载该类。它只是定义了一个静态方法(init),您必须在使用数组字段之前调用该方法。

  • 问题内容: 你将如何在Java中初始化静态变量? 方法一:静态初始化器 方法二:实例初始化器(匿名子类)或其他方法? 各自的优缺点是什么? 这是说明两种方法的示例: 问题答案: 在这种情况下,实例初始化器只是语法糖,对吗?我不明白为什么你只需要一个额外的匿名类即可进行初始化。如果正在创建的类是最终的,那将是行不通的。 你也可以使用静态初始化程序创建不可变映射:

  • 我正在开发我的第一个 Swing 应用程序,现在提出了一个难题:在静态初始化期间或开始实际执行后执行引导和资源初始化。我是什么意思...我有单例: 因此,方法如下所示 或者,也许我在启动后手动初始化资源,然后运行它。逻辑上正确的方式是什么?

  • 问题内容: 我想知道为什么默认情况下C,C ++和Java中的确切静态变量初始化为零?为什么对局部变量不是这样? 问题答案: 为什么要对静态变量进行确定性初始化而对局部变量不进行初始化? 了解如何实现静态变量。 它们的内存在链接时分配,并且它们的初始值也在链接时提供。 没有运行时开销。 另一方面,用于局部变量的内存是在运行时分配的。堆栈必须增长。你不知道以前在那里。如果需要,可以清除该内存(将其清