public class MyTest1 {
public static void main(String[] args){
System.out.println(MyChild1.str);
}
}
class MyParent1{
public static String str = "hello world";
static{
System.out.println("MyParent1 static block");
}
}
class MyChild1 extends MyParent1{
public static String str2 = "Welcome";
static {
System.out.println("MyChild1 static block");
}
}
子类不会初始化,有没有加载,Java规范里没有规定,可以通过命名来查看是否加载
-XX:TraceClassLoading,用于追踪类的加载信息并打印出来
配置之后运行
结果如下:
首先是加载所有类的父类Object
即便没有对MyChild1初始化,JVM对MyChild1进行了加载
JVM的参数使用:
所有的参数都是-XX:开始的,有以下三种情况
-XX:+<option>表示开启option选项
-XX:-<option> 表示关闭option选项
-XX:<option>=<value> 表示将option选项的值设置为value
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.str);
}
}
class MyParent2{
// 定义为final 不会执行静态代码块
public static final String str = "hello world";
static {
System.out.println("MyParent2 static block");
}
}
str 定义为 final 不会执行静态代码块,在编译阶段str这个常量会存入调用这个常量的方法所在的类的常量池中,也就是会被放入MyTest2的常量池中,本质上调用类并没有直接引用到定义常量的类,因此并不会触发定义常量的类的初始化。
注意:
这里指的是将常量存放到了MyTest2的常量池中,之后MyTest2与MyParent2就没有任何关系了,甚至,我们可以将MyParent2的class文件删除
反编译MyTest2
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String hello world 说明已经是一个确定的值
ldc 表示将int,float或是String类型的常量值从常量池中推送至栈顶
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
将代码里的常量String,替换为short
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.s);
}
}
class MyParent2{
public static final String str = "hello world";
public static final short s = 7;
static {
System.out.println("MyParent2 static block");
}
}
反编译结果:
ldc变成了bipush
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: bipush 7
bipush表示将单字节(-128 ~ 127)的常量值推送至栈顶
5: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
8: return
改为 public static final int s = 128;
会变成sipush 表示将一个短整型常量值(-32768 ~ 32767)推送至栈顶
public static final int s = 1;
会变成iconst_1表示将int类型1推送至栈顶(jvm 单独对1到5做了特殊的表示,iconst_m1(-1) 到 iconst_5,大于5就会使用sipush)