今天听了B站的某教育平台的jvm课,记下笔记
看这段代码,输出是什么呢?
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.str);
}
}
class MyParent2{
public static String str = "hello world";
static{
System.out.println("MyParent2 static block");
}
};
有开发经验的朋友基本都能知道结果。结果如下。
MyParent2 static block
hello world
原因是MyTest2类的main方法调用了Myparent2的静态变量str,从而导致Myparent2被初始化,类初始化过程中静态语句块先执行,所以程序打印了第一行 “MyParent2 static block”,接着打印了变量str “hello world“。
接着我们稍微改下代码,将MyParent2类的静态变量str,加上fianl修饰。
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.str);
}
}
class MyParent2{
public final static String str = "hello world";
static{
System.out.println("MyParent2 static block");
}
};
打印结果是:
hello world
是的,没看错,只有一行hello world。并没有执行类MyParent2的静态语句块。这是为什么呢?
关键是final这个java关键字。大家知道被final修饰的变量,就是常量。而
在编译阶段,jvm发现某个变量被fianl修饰,即认定这个是常量,且这个常量在编译时就能确认其值。则把这个常量放到调用这个常量的方法的所在类的常量池中。本质上,并没有引用这个常量所在的类,因此并不会触发常量所在类的初始化,所以也不会打印这个类的静态语句块。
结合我们上面那个例子来理解:在程序编译阶段,jvm发现main方法中Myparent2.str是常量,则把这个常量也就是“hello world”放到自己也就是Mytest2的常量池中,运行阶段执行main方法的打印时,直接从自己的常量池中取出来就可以了,并不会涉及到Myparent2的初始化,所以Myparent2的静态语句块也就不会执行。
继续来看代码,稍做改动。将代码改为 public final static String str = System.currentTimeMillis()+"";
public class MyTest2 {
public static void main(String[] args) {
System.out.println(MyParent2.str);
}
}
class MyParent2{
public final static String str = System.currentTimeMillis()+"";
static{
System.out.println("MyParent2 static block");
}
};
打印结果是:
MyParent2 static block
1581682607094
没错静态语句块又出现了,为什么?细心的同学肯定猜到了,看上面红色字体这句话
在编译阶段,jvm发现某个变量被fianl修饰,即认定这个是常量,且这个常量在编译时就能确认其值。。。
在比较下第二个例子和第三个例子不同的代码:
- 第二个例子是public final static String str = "hello world";
- 第三个例子是public final static String str = System.currentTimeMillis()+""
第二个例子中编译时就可以确定str肯定就是“hello world”,而第三个例子只有在运行期才能确定,所以肯定要将Myparent2初始化,静态语句块肯定就打印出来了