Java 类初始化到底何时触发?别把加载、连接和 static 执行混在一起

很多人把"类加载"说成一个瞬间:用到了类,JVM 就加载,static 就跑了。

面试官通常不会在这里放过你。他想听的其实是:加载、连接、初始化是不同阶段;真正执行 static 字段赋值和 static 代码块,是初始化阶段。

我觉得最容易讲清楚的方式,不是先背一长串生命周期,而是先拿一段能跑的代码。

java 复制代码
class Parent {
    static {
        System.out.println("Parent <clinit>");
    }

    static int value = 42;
}

class Child extends Parent {
    static {
        System.out.println("Child <clinit>");
    }
}

class Demo {
    public static void main(String[] args) {
        System.out.println(Child.value);
    }
}

输出只有:

xml 复制代码
Parent <clinit>
42

Child.value 看起来是通过子类读的,但 value 实际声明在 Parent。按 Java 语言规范,只有直接声明这个静态字段的类会因此初始化,所以这里不该出现 Child <clinit>

这就是这题最常见的坑:不要按"代码里写了哪个类名"判断,要看字段到底定义在哪个类里。

哪些情况会真的触发初始化

先记住几个够用的主动使用场景:

  • new Config()
  • 调用类的静态方法,例如 Config.load()
  • 读取或写入不是编译期常量的静态字段
  • 反射主动使用该类
  • 启动类的 main 执行前

前面几个最终对应到 newgetstaticputstaticinvokestatic 等使用点。父类需要先于子类完成初始化,所以第一次 new Child() 时,输出顺序会先是父类,再是子类。

反过来,有些写法只是在拿类型信息,别急着说"static 一定执行"。

java 复制代码
class Config {
    static {
        System.out.println("Config <clinit>");
    }

    static final int PORT = 8080;
    static final Integer TIMEOUT = 3000;
}

class Demo {
    public static void main(String[] args) {
        System.out.println(Config.PORT);
        System.out.println(Config.TIMEOUT);
        System.out.println(Config.class.getName());
    }
}

PORT 是编译期常量,调用方编译后可能直接把 8080 写进自己的常量池,不需要初始化 Config。但 TIMEOUTInteger 对象,不属于编译期常量,读取它会触发 Config 初始化。

这块别只盯着有没有 final。判断关键是"是不是编译期常量表达式",不是"字段看上去像个常量"。

Config.class 也不会执行 static 代码块。它拿到的是类字面量,不等于主动初始化。

不是构造方法

继续被追问时,很多人会说"static 代码块在构造函数之前执行"。这句话不算错,但太粗。

编译器会把静态字段的赋值和 static 代码块,按源码出现的顺序收进一个叫 <clinit> 的类初始化方法。它由 JVM 在初始化阶段调用,普通 Java 代码不能直接调。

实例初始化走的是 <init>,也就是构造器相关的那条路径。一个管类,一个管对象。把这两个名字拆开,后面的单例、静态资源初始化、父子类顺序就不容易混。

现场怎么答

我会先用一句话定边界:

"加载、连接和初始化不是一回事。static 真正执行在初始化阶段。new、调用静态方法、读写非常量静态字段会触发它;子类名访问父类字段时,只初始化字段的声明类。"

然后补上 static final int PORT = 8080static final Integer TIMEOUT = 3000 这个对照。面试官一般会接着问父子类顺序,或者让你解释一段输出。

相关推荐
小羊没烦恼!1 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
俊昭喜喜里1 天前
java中的继承和多态的区别
java
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕1 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码1 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖1 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时1 天前
01-06-A-JVM排查实战详解
java·jvm
vipxieliang1 天前
ValidX 在 DDD 领域驱动设计中的实践
java·spring boot
ba_pi1 天前
mysql 查询所有表名并授权
java
ProcessOn官方账号1 天前
java函数式编程--入门基础
java·编程·函数式编程