原文:https://blog.saymagic.cn/2017/07/01/class-common-question.html

类的初始化顺序是怎样的?

我们尝试从class文件中找到答案。来看这样的一段代码:

public class InitialOrderTest {
public static String staticField = " StaticField";
public String fieldFromMethod = getStrFromMethod();
public String fieldFromInit = " InitField";
static {
System.out.println( "Call Init Static Code" );
System.out.println( staticField );
}
{
System.out.println( "Call Init Block Code" );
System.out.println( fieldFromInit );
System.out.println( fieldFromMethod );
}
public InitialOrderTest()
{
System.out.println( "Call Constructor" );
}
public String getStrFromMethod(){
System.out.println("Call getStrFromMethod Method");
return " MethodField" ;
}
public static void main( String[] args )
{
new InitialOrderTest();
}
}

结果:

我们来一一来看一下它的class文件中的内容,首先是有一个static方法区:

static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: ldc #14 // String StaticField
2: putstatic #15 // Field staticField:Ljava/lang/String;
5: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #16 // String Call Init Static Code
10: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
16: getstatic #15 // Field staticField:Ljava/lang/String;
19: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
22: return

Java编译器在编译阶段会将所有static的代码块收集到一起,形成一个特殊的方法,这个方法的名字叫做clinit, 这个名字容易让我们联想到构造函数的名称叫做init,但与构造函数不同,这个方法在Java层中是调用不到的,并且,这个函数是在这个类被加载时,由虚拟机进行调用。注意的是,是类被加载,而不是类被初始化成实例。所以,静态代码块的加载优先于普通的代码块,也优先于构造函数。这属于虚拟机规定的范畴,我们不做更深入的探讨。

在Class文件中,是没有为普通方法区开辟类似于clinit这种方法的,而是将所有普通方法区的代码都合并到了构造函数中,我们直接来看构造函数:

public InitialOrderTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: aload_0
6: invokevirtual #2 // Method getStr:()Ljava/lang/String;
9: putfield #3 // Field field:Ljava/lang/String;
12: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
15: aload_0
16: getfield #3 // Field field:Ljava/lang/String;
19: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
22: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
25: ldc #6 // String Init Block
27: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
33: ldc #7 // String Constructor
35: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
38: return

通过分析构造函数,我们就可以对一个实例初始化的顺序一清二楚,首先,0,1在构造函数中调用了父类的构造函数,接着,4、5、6、9为成员变量进行赋值,25、27在执行实例的代码块,最后,33、35才是执行我们Java文件中编写的构造函数的代码。这样,一个普通类的初始化顺序大致如下:

静态代码按照顺序初始化 -> 父类构造函数 -> 变量初始化 -> 实例代码块 -> 自身构造函数

最新文章

  1. Winform的&quot;透明&quot;
  2. JS网页顶部进度条demo
  3. [SQL Server]如何快速查找使用了某个字段的所有存储过程
  4. iOS开发 火星坐标转百度坐标
  5. windows 程序设计自学:窗口正中显示Hello,World
  6. 一个C++程序员学习C#语言
  7. 把CSV文件导入到SQL Server表中
  8. 如何在macos下创建文件或者文件夹的快捷方式
  9. C#中禁止程序多开
  10. EventBus 最简易的使用方式
  11. 前端动态菜单-bootstrap-treeview
  12. linux常用命令及使用技巧(二)
  13. 将ASP.NET网站部署到服务器IIS上
  14. Oracle分析函数-keep(dense_rank first/last)
  15. JavaScript中使用function作为对象键值
  16. Mysql-表的基本操作
  17. J15W-J45W全铜质截止阀厂家,J15W-J45W全铜质截止阀价格 - 专题栏目 - 无极资讯网
  18. hadoop学习笔记——用python写wordcount程序
  19. 2015/9/18 Python基础(14):函数式编程
  20. redis 按空间范围查询点位

热门文章

  1. linuxmint17.3提示flash不是最新版本解决方法(copy)
  2. ubuntu 12.04 samba 配置(转载)
  3. ES6躬行记(23)——Promise的静态方法和应用
  4. bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】
  5. centos 7 添加普通用户
  6. jQuery笔记之animate中的queue
  7. java启动参数一
  8. 转-iOS开发系列--地图与定位
  9. jsp错误处理
  10. 【学习笔记】SIFT尺度不变特征 (配合UCF-CRCV课程视频)