对于“何时”有很多很好的答案,比如在这个线程中——静态类初始化何时发生?现在我的问题是“如何”。这是斯蒂芬C.的回答中的一段话
类静态初始化通常在第一次发生以下事件之前立即发生:
那么它是如何在内部完成的呢?每个可能触发初始化的指令都用if
包装?任何工作的细节:-)我都可以执行。
我用“Java”来标记这个问题,但如果我没有弄错的话,C#和Swift也会根据需要初始化静态数据。
对于静态常量(最终)字段,. 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
是一个常量,而x
和y
是运行时值,将与静态初始值设定项块合并(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(对机器代码的即时编译)。
正如在评论中提到的,这类事情可以通过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中的确切静态变量初始化为零?为什么对局部变量不是这样? 问题答案: 为什么要对静态变量进行确定性初始化而对局部变量不进行初始化? 了解如何实现静态变量。 它们的内存在链接时分配,并且它们的初始值也在链接时提供。 没有运行时开销。 另一方面,用于局部变量的内存是在运行时分配的。堆栈必须增长。你不知道以前在那里。如果需要,可以清除该内存(将其清