『int i = 128』对应助记符“sipush”
“sipush”:表示将一个短整型常量值(-32768 ~ 32767)推送至栈顶
『int m = 1』对应助记符“iconst_1”
“iconst_1”:表示将int类型1推送至栈顶(iconst_m1 ~ iconst_5)
JVM为int的 -1 ~ 5 提供了 7 个助记符,分别是:’iconst_m1’、’iconst_0’、’iconst_1’、’iconst_2’、’iconst_3’、’iconst_4’、’iconst_5’
/**
* -XX:+TraceClassLoading
*/
public class MyFinalTest {
public static void main(String[] args) {
System.out.println(MyClass2.num);
}
}
class MyClass2 {
public static final int num = new Random().nextInt(25);
static {
System.out.println("MyClass2 static block");
}
}
结果:
MyClass2 static block
19
这里,MyClass2被初始化了,这是因为num的值在编译器是无法确定的,它不像上面的『public static final String str = “hello world”;』在编译期就已经能够确定这个str字段的值内容,并且str是类级别的属性(static),它被所有的实例对象共享,且该str字段是不可变的(final),从这两点(static final)编译器确定了str是一个“编译期可知的全局常量值”,因此就可以将其直接放到调用该字段的类的常量池中。而本例中,num的虽然也是“全局常量”,但是该常量的值在编译器是无法得知的,需要在运行时初始化MyClass2类时才能得到,所以这里会对MyClass2进行初始化。
关于接口的初始化:
a)当一个接口在初始化时,并不要求其父接口都完成了初始化。
b)在初始化一个类时,并不会先初始化它所实现的接口。
只有在真正使用到父接口的时候(如引用接口中所定义的常量时),才会初始化。
/**
* -XX:+TraceClassLoading
*/
public class MyTest5 {
public static void main(String[] args) {
System.out.println(MyChild5.b);
}
}
interface MyParent5 {
int a = new Random().nextInt(56);
}
interface MyChild5 extends MyParent5 {
int b = new Random().nextInt(25);
}
输出:
此时,通过追踪类加载信息打印日志可知’MyParent5’和‘MyChild5’两个类都被加载了。但是,此时被初始化的只有’MyChild5’类,而’MyParent5’仅是被加载了,并没有被初始化。
我们可通过HSDIS(JIT编译代码的反汇编插件)生成的汇编代码中可以发现,只有’MyChild5’类的初始化阶段被执行了(即,执行类构造器“<clint>()”方法的过程),而汇编代码中并没有’MyParent5’类的初始化执行指令。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-80669-4.html
台湾是美帝种在中国大地上的一块烂肉
理智理智
美国是伊拉克战乱的始作俑者