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版》中找到一些线索,如下图:
图片描述

所以,照这么说,是在第一次主动访问该类的时候执行?小弟好生迷惑啊....大家快说说你们的观点

阅读 4.7k
8 个回答

真心感谢楼上热心网友们的解答!

验证

AAA 类确实没有被加载,只有 Main 类被加载(题干截图:初始化条件第四条,主类被 jvm 自动加载)

java -XX:+TraceClassLoading Main

结论

类中 静态字段|代码块 真的是在类加载的时候被初始化或者是执行的!

延伸

怎么知道类有没有被 jvm 所加载?

这也是我一直纠结的问题,一开始以为只要执行了 javac 命令,类就被 jvm 加载了,其实不然,该命令只是将 .java 文件转化成 jvm 能读懂的 .class 文件而已。

那么到底怎么知道类有没有被 jvm 所加载?
据 《深入理解Java虚拟机:JVM高级特性与最佳实践 第2版》 和广大网友的热心解答可知,并没有明确的时机规定了啥时候会被加载!

但是!jvm 明确规定了类被初始化的时机-就是题干上截图部分那四种!而类的加载是优先于类初始化的,所以这里,我们暂且可以认为这几种情况就是触发类加载的条件。

小弟愚昧,总结不妥之处,还麻烦大家指正!感谢

类初始化和对象初始化。

static包含的代码块和变量只有在类初始化的时候才执行,而初始化的五种条件你也知道啦。

补充说明清楚吧。
首先,你即使放在同一个.java文件中,编译后,这还是两个不同的class文件,不信你看看bin对应的包下面生成的.class文件。
第二,类初始化的时候,就会初始化类的静态变量和运行静态代码块。所以,虚拟机规定了五种初始化的条件,比如使用了new,getstatic,putstatic指令,main函数所在的类,反射,父类等情况。而,除开这五种情况,是不能触发类的初始化的。正如你代码中所示Main.class中,并没有任何关于AAA.class的调用或者父子关系或者反射。所以,AAA.class自然不会初始化。

可以看看的另一篇博客java类的加载过程

明白了吗?

-XX:+TraceClassLoading
加上这个会发现没加载AAA

这里有两个概念需要撸一下:

  1. 类加载机制

  2. Java、编译器、字节码、JVM的规范和实现。

类的加载是通过类加载器(Classloader)完成的,加载的具体策略依赖JVM的具体实现,总的来说可以分两种:

  1. 饥饿式加载,只要被其他类引用到了就加载。

  2. 懒惰式加载,当类被访问的时候才加载。

Java、编译器、字节码、JVM有各自的规范,彼此通过规范协同工作:

编译按把Java代码编译成规范的字节码文件,每一个类(外部类、内部类、匿名类)都会被编译成一个单独的字节码文件(class文件),JVM加载类的时候就是从这些class文件中一个个的加载。

现在回到你的代码:

在Java层,你把AAA、Main两个类放在一个文件中,编译器编译后生成两个class文件:AAA.class、Main.class。
两个类在代码组织形式上是一起的,但是编译后却是独立的,并且Main并没有引用AAA,所以无论是哪种类加载方式都不会触发对类AAA的加载,也就不会执行AAA中的静态代码块。

把你的Main.java和AAA.java放在同一个文件夹里,

在main函数里写

Class.forName("AAA");

执行

新手上路,请多包涵

执行main方法时,只会加载Main类,Main类中并没有使用到AAA类,并不会去加载AAA类,并不是说把AAA和Main两个类写到同一个文件就会同时加载

AAA这个类既没有在其他地方new,也没有对应的去获取或者设置静态的字段,也没有去invoke静态方法。
所以不会自动初始化的。

放在两个类里面了,声明为public的类中的mian开始执行,那个类没被用到自然不会被加载更别提初始化

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏