Java类初始化阶段深度解析:执行顺序与线程安全
myzbx 2025-09-06 08:31 68 浏览
一、初始化阶段核心机制
二、分步详解与代码验证
1. 初始化触发条件
主动使用场景:
public class InitTrigger {
static {
System.out.println("类初始化开始");
}
public static void main(String[] args) {
// 触发初始化的6种情况
new InitTrigger(); // 1.创建实例
System.out.println(VALUE); // 2.访问静态变量
staticMethod(); // 3.调用静态方法
Class<?> clazz = InitTrigger.class; // 4.反射调用
InitTrigger[] arr = new InitTrigger[10]; // 5.数组声明(不触发)
ClassLoader.getSystemClassLoader().loadClass("InitTrigger"); // 6.加载但不初始化
}
static int VALUE = 100;
static void staticMethod() {}
}执行结果:
类初始化开始 # 只打印一次2. 类构造器生成规则
代码转换案例:
// 原始代码
public class ClinitDemo {
static int a = initA();
static int b = 20;
static {
b = 30;
}
static int initA() {
return 10;
}
}
// 编译器生成的<clinit>方法等价代码:
static void <clinit>() {
a = initA(); // 10
b = 20; // 被后续代码覆盖
b = 30; // 最终值
}
字节码验证:
javap -v ClinitDemo.class
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
0: invokestatic #2 // Method initA:()I
3: putstatic #3 // Field a:I
6: bipush 20
8: putstatic #4 // Field b:I
11: bipush 30
13: putstatic #4 // Field b:I
16: return
3. 多线程初始化安全
public class ThreadSafeInit {
static class Resource {
static {
System.out.println(Thread.currentThread().getName() + "初始化资源");
try { Thread.sleep(3000); } catch (InterruptedException e) {}
}
}
public static void main(String[] args) {
Runnable task = () -> { System.out.println(Resource.class); };
// 同时启动10个线程
for (int i = 0; i < 10; i++) {
new Thread(task).start();
}
}
}
输出结果:
Thread-0初始化资源 # 只有一个线程执行初始化三、初始化顺序原理剖析
1. 类继承体系初始化
代码验证:
public class InheritanceInit {
static class GrandParent {
static { System.out.println("GrandParent初始化"); }
}
static class Parent extends GrandParent {
static { System.out.println("Parent初始化"); }
}
static class Child extends Parent {
static { System.out.println("Child初始化"); }
}
public static void main(String[] args) {
new Child();
}
}输出结果:
GrandParent初始化
Parent初始化
Child初始化2. 接口初始化特性
public class InterfaceInit {
interface MyInterface {
int VALUE = new Random().nextInt();
class Inner {
static { System.out.println("接口内部类初始化"); }
}
}
public static void main(String[] args) {
System.out.println(MyInterface.VALUE); // 触发接口初始化
new MyInterface.Inner(); // 不触发接口初始化
}
}执行结果:
接口内部类初始化 # 仅内部类初始化
3. 初始化异常处理
public class InitException {
static {
if (true) {
throw new RuntimeException("模拟初始化失败");
}
}
public static void main(String[] args) {
try {
new InitException();
} catch (Throwable t) {
System.out.println("捕获异常: " + t.getClass());
System.out.println("根本原因: " + t.getCause().getMessage());
}
}
}输出结果:
捕获异常: class java.lang.ExceptionInInitializerError
根本原因: 模拟初始化失败四、高级应用场景
1. 延迟初始化模式
双重检查锁定:
public class LazyInit {
private static volatile LazyInit instance;
// 单例模式实现
public static LazyInit getInstance() {
if (instance == null) {
synchronized (LazyInit.class) {
if (instance == null) {
instance = new LazyInit();
}
}
}
return instance;
}
private LazyInit() {
System.out.println("实例初始化完成");
}
public static void main(String[] args) {
IntStream.range(0, 5).parallel().forEach(i -> getInstance());
}
}输出结果:
实例初始化完成 # 仅打印一次
2. 类卸载与再初始化
public class Reinitialization {
static class Temp {
static {
System.out.println("初始化完成");
}
}
public static void main(String[] args) throws Exception {
ClassLoader loader = new CustomLoader();
Class<?> clazz = loader.loadClass("Temp");
clazz.newInstance(); // 第一次初始化
loader = new CustomLoader(); // 创建新类加载器
clazz = loader.loadClass("Temp");
clazz.newInstance(); // 再次初始化
}
static class CustomLoader extends ClassLoader {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.equals("Temp")) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
return super.loadClass(name);
}
private byte[] loadClassData(String name) {
// 从指定路径加载类字节码
}
}
}输出结果:
初始化完成
初始化完成 # 不同类加载器允许重复初始化
3. 静态变量重置技巧
public class StaticReset {
static class Counter {
static int count;
static {
count = 100;
}
}
public static void main(String[] args) throws Exception {
System.out.println("初始值: " + Counter.count); // 100
Field field = Counter.class.getDeclaredField("count");
field.setAccessible(true);
field.setInt(null, 200);
System.out.println("修改后: " + Counter.count); // 200
// 通过反射触发重新初始化
Field init = Class.class.getDeclaredField("init");
init.setAccessible(true);
init.set(Counter.class, false);
Counter.count = 300; // 无效,实际会执行<clinit>
System.out.println("强制重置后: " + Counter.count);
}
}警告:此代码涉及JVM内部实现,不同版本可能行为不同
五、最佳实践与性能优化
- 静态代码块规范:
// 不良实践:复杂逻辑放在静态块
static {
try (Connection conn = DriverManager.getConnection(url)) {
// 初始化数据库连接
} catch (SQLException e) {
throw new ExceptionInInitializerError(e);
}
}
// 改进方案:延迟初始化
private static Connection conn;
public static Connection getConnection() {
if (conn == null) {
synchronized (MyClass.class) {
if (conn == null) {
// 初始化连接
}
}
}
return conn;
}
- 类初始化耗时监控:
public class InitMonitor {
static {
long start = System.nanoTime();
// 初始化代码...
long duration = System.nanoTime() - start;
System.out.println("初始化耗时: " + duration/1e6 + "ms");
}
}
- 静态变量内存管理:
public class StaticMemory {
// 错误示例:大对象静态初始化
static byte[] buffer = new byte[1024 * 1024 * 100]; // 立即占用100MB
// 正确做法:按需加载
private static class Holder {
static final byte[] BUFFER = new byte[1024 * 1024 * 100];
}
public static byte[] getBuffer() {
return Holder.BUFFER;
}
}
六、总结
关键要点总结:
- 每个类/接口的方法只会执行一次
- 静态变量的赋值顺序决定最终值
- 初始化失败会导致无法恢复的Error
- 合理设计静态初始化逻辑影响启动性能
推荐调试方法:
# 查看类初始化过程
java -XX:+TraceClassInitialization YourClass
# 打印初始化时间
java -XX:+PrintCompilation -XX:+PrintInlining相关推荐
- 如何设计一个优秀的电子商务产品详情页
-
加入人人都是产品经理【起点学院】产品经理实战训练营,BAT产品总监手把手带你学产品电子商务网站的产品详情页面无疑是设计师和开发人员关注的最重要的网页之一。产品详情页面是客户作出“加入购物车”决定的页面...
- 怎么在JS中使用Ajax进行异步请求?
-
大家好,今天我来分享一项JavaScript的实战技巧,即如何在JS中使用Ajax进行异步请求,让你的网页速度瞬间提升。Ajax是一种在不刷新整个网页的情况下与服务器进行数据交互的技术,可以实现异步加...
- 中小企业如何组建,管理团队_中小企业应当如何开展组织结构设计变革
-
前言写了太多关于产品的东西觉得应该换换口味.从码农到架构师,从前端到平面再到UI、UE,最后走向了产品这条不归路,其实以前一直再给你们讲.产品经理跟项目经理区别没有特别大,两个岗位之间有很...
- 前端监控 SDK 开发分享_前端监控系统 开源
-
一、前言随着前端的发展和被重视,慢慢的行业内对于前端监控系统的重视程度也在增加。这里不对为什么需要监控再做解释。那我们先直接说说需求。对于中小型公司来说,可以直接使用三方的监控,比如自己搭建一套免费的...
- Ajax 会被 fetch 取代吗?Axios 怎么办?
-
大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!今天给大家带来的主题是ajax、fetch...
- 前端面试题《AJAX》_前端面试ajax考点汇总
-
1.什么是ajax?ajax作用是什么?AJAX=异步JavaScript和XML。AJAX是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX可以使网页实...
- Ajax 详细介绍_ajax
-
1、ajax是什么?asynchronousjavascriptandxml:异步的javascript和xml。ajax是用来改善用户体验的一种技术,其本质是利用浏览器内置的一个特殊的...
- 6款可替代dreamweaver的工具_替代powerdesigner的工具
-
dreamweaver对一个web前端工作者来说,再熟悉不过了,像我07年接触web前端开发就是用的dreamweaver,一直用到现在,身边的朋友有跟我推荐过各种更好用的可替代dreamweaver...
- 我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊
-
接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...
- 福斯《死侍》发布新剧照 "小贱贱"韦德被改造前造型曝光
-
时光网讯福斯出品的科幻片《死侍》今天发布新剧照,其中一张是较为罕见的死侍在被改造之前的剧照,其余两张剧照都是死侍在执行任务中的状态。据外媒推测,片方此时发布剧照,预计是为了给不久之后影片发布首款正式预...
- 2021年超详细的java学习路线总结—纯干货分享
-
本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础重点知识点:数据类型、核心语法、面向对象...
- 不用海淘,真黑五来到你身边:亚马逊15件热卖爆款推荐!
-
Fujifilm富士instaxMini8小黄人拍立得相机(黄色/蓝色)扫二维码进入购物页面黑五是入手一个轻巧可爱的拍立得相机的好时机,此款是mini8的小黄人特别版,除了颜色涂装成小黄人...
- 2025 年 Python 爬虫四大前沿技术:从异步到 AI
-
作为互联网大厂的后端Python爬虫开发,你是否也曾遇到过这些痛点:面对海量目标URL,单线程爬虫爬取一周还没完成任务;动态渲染的SPA页面,requests库返回的全是空白代码;好不容易...
- 最贱超级英雄《死侍》来了!_死侍超燃
-
死侍Deadpool(2016)导演:蒂姆·米勒编剧:略特·里斯/保罗·沃尼克主演:瑞恩·雷诺兹/莫蕾娜·巴卡林/吉娜·卡拉诺/艾德·斯克林/T·J·米勒类型:动作/...
- 停止javascript的ajax请求,取消axios请求,取消reactfetch请求
-
一、Ajax原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。停止javascript的ajax请求...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 简介 (30)
- HTML 响应式设计 (31)
- HTML URL 编码 (32)
- HTML Web 服务器 (31)
- HTML 表单属性 (32)
- HTML 音频 (31)
- HTML5 支持 (33)
- HTML API (36)
- HTML 总结 (32)
- HTML 全局属性 (32)
- HTML 事件 (31)
- HTML 画布 (32)
- HTTP 方法 (30)
- 键盘快捷键 (30)
- CSS 语法 (35)
- CSS 轮廓宽度 (31)
- CSS 谷歌字体 (33)
- CSS 链接 (31)
- CSS 定位 (31)
- CSS 图片库 (32)
- CSS 图像精灵 (31)
- SVG 文本 (32)
- 时钟启动 (33)
- HTML 游戏 (34)
- JS Loop For (32)
