java - Static 标识的字段或者是代码块,真的是在类加载的时候初始化的吗?
问题描述
class AAA { static {System.out.println('class AAA static block println'); // 并没有打印此句 }}public class Main { public static void main(String[] args) {System.out.println('hello world!'); }}
一直以来都以为 static 标识的代码块或者是字段,都是在类加载的时候就被执行或者赋值了,但是这么一看....感觉自己的世界观都要被刷新了。
所以此处是类没有被加载吗?还是说我们一直以来认为的,静态代码块、字段都在类加载的时候被初始化的,这个观点是错误的?
在《深入理解Java虚拟机:JVM高级特性与最佳实践 第2版》中找到一些线索,如下图:
所以,照这么说,是在第一次主动访问该类的时候执行?小弟好生迷惑啊....大家快说说你们的观点
问题解答
回答1:类初始化和对象初始化。
static包含的代码块和变量只有在类初始化的时候才执行,而初始化的五种条件你也知道啦。
补充说明清楚吧。首先,你即使放在同一个.java文件中,编译后,这还是两个不同的class文件,不信你看看bin对应的包下面生成的.class文件。第二,类初始化的时候,就会初始化类的静态变量和运行静态代码块。所以,虚拟机规定了五种初始化的条件,比如使用了new,getstatic,putstatic指令,main函数所在的类,反射,父类等情况。而,除开这五种情况,是不能触发类的初始化的。正如你代码中所示Main.class中,并没有任何关于AAA.class的调用或者父子关系或者反射。所以,AAA.class自然不会初始化。
可以看看的另一篇博客java类的加载过程
明白了吗?
回答2:-XX:+TraceClassLoading加上这个会发现没加载AAA
回答3:这里有两个概念需要撸一下:
类加载机制
Java、编译器、字节码、JVM的规范和实现。
类的加载是通过类加载器(Classloader)完成的,加载的具体策略依赖JVM的具体实现,总的来说可以分两种:
饥饿式加载,只要被其他类引用到了就加载。
懒惰式加载,当类被访问的时候才加载。
Java、编译器、字节码、JVM有各自的规范,彼此通过规范协同工作:
编译按把Java代码编译成规范的字节码文件,每一个类(外部类、内部类、匿名类)都会被编译成一个单独的字节码文件(class文件),JVM加载类的时候就是从这些class文件中一个个的加载。
现在回到你的代码:
在Java层,你把AAA、Main两个类放在一个文件中,编译器编译后生成两个class文件:AAA.class、Main.class。两个类在代码组织形式上是一起的,但是编译后却是独立的,并且Main并没有引用AAA,所以无论是哪种类加载方式都不会触发对类AAA的加载,也就不会执行AAA中的静态代码块。
回答4:真心感谢楼上热心网友们的解答!
验证AAA 类确实没有被加载,只有 Main 类被加载(题干截图:初始化条件第四条,主类被 jvm 自动加载)
java -XX:+TraceClassLoading Main结论
类中 静态字段|代码块 真的是在类加载的时候被初始化或者是执行的!
延伸怎么知道类有没有被 jvm 所加载?这也是我一直纠结的问题,一开始以为只要执行了 javac 命令,类就被 jvm 加载了,其实不然,该命令只是将 .java 文件转化成 jvm 能读懂的 .class 文件而已。
那么到底怎么知道类有没有被 jvm 所加载?据 《深入理解Java虚拟机:JVM高级特性与最佳实践 第2版》 和广大网友的热心解答可知,并没有明确的时机规定了啥时候会被加载!
但是!jvm 明确规定了类被初始化的时机-就是题干上截图部分那四种!而类的加载是优先于类初始化的,所以这里,我们暂且可以认为这几种情况就是触发类加载的条件。
小弟愚昧,总结不妥之处,还麻烦大家指正!感谢
回答5:把你的Main.java和AAA.java放在同一个文件夹里,
在main函数里写
Class.forName('AAA');
执行
回答6:执行main方法时,只会加载Main类,Main类中并没有使用到AAA类,并不会去加载AAA类,并不是说把AAA和Main两个类写到同一个文件就会同时加载
回答7:AAA这个类既没有在其他地方new,也没有对应的去获取或者设置静态的字段,也没有去invoke静态方法。所以不会自动初始化的。
回答8:放在两个类里面了,声明为public的类中的mian开始执行,那个类没被用到自然不会被加载更别提初始化