科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件解析Java类和对象的初始化过程(2)

解析Java类和对象的初始化过程(2)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文主要对类和对象初始化全过程进行分析,通过一个实际问题引入,将源代码转换成JVM字节码后,对JVM执行过程的关键点进行全面解析。

作者:天新网 来源:天新网 2007年9月3日

关键字:

  • 评论
  • 分享微博
  • 分享邮件

看到这里,如果是你在调试该程序,你此刻一定觉得很奇怪,难道是我的Jvm有问题吗?非也!如果不是,那我的程序是怎么了?这绝对不是我想要的结果。可事实上无论怎么修改initEnum()方法都无济于事,起码我最初是一定不会怀疑到问题可能出在创建CachingEnumResolver实例这一环节上。正是因为我太相信我创建CachingEnumResolver实例的方法,加之对Java类初始化与对象实例化底层原理理解有所偏差,使我为此付出了三、四个小时--约半个工作日的大好青春。

那么问题究竟出在哪里呢?为什么会出现这样的怪事呢?在解决这个问题之前,先让我们来了解一下JVM的类和对象初始化的底层机制。

类的生命周期

 



上图展示的是类生命周期流向;在本文里,我只打算谈谈类的“初始化”以及“对象实例化”两个阶段。

类初始化

类“初始化”阶段,它是一个类或接口被首次使用的前阶段中的最后一项工作,本阶段负责为类变量赋予正确的初始值。

Java编译器把所有的类变量初始化语句和类型的静态初始化器通通收集到<clinit>方法内,该方法只能被Jvm调用,专门承担初始化工作。

除接口以外,初始化一个类之前必须保证其直接超类已被初始化,并且该初始化过程是由Jvm保证线程安全的。另外,并非所有的类都会拥有一个<clinit>()方法,在以下条件中该类不会拥有<clinit>()方法:

该类既没有声明任何类变量,也没有静态初始化语句;该类声明了类变量,但没有明确使用类变量初始化语句或静态初始化语句初始化;该类仅包含静态final变量的类变量初始化语句,并且类变量初始化语句是编译时常量表达式。

对象初始化

在类被装载、连接和初始化时,这个类就随时都可能使用了。对象实例化和初始化就是对象生命的起始阶段的活动,在这里我们主要讨论对象的初始化工作的相关特点。

Java 编译器在编译每个类时都会为该类至少生成一个实例初始化方法--即“<init>()”方法。此方法与源代码中的每个构造方法相对应,如果类没有明确地声明任何构造方法,编译器则为该类生成一个默认的无参构造方法,这个默认的构造器仅仅调用父类的无参构造器,与此同时也会生成一个与默认构造方法对应的“<init>()”方法。

通常来说,<init>()方法内包括的代码内容大概为:调用另一个 <init>()方法;对实例变量初始化;与其对应的构造方法内的代码。如果构造方法是明确地从调用同一个类中的另一个构造方法开始,那它对应的<init>()方法体内包括的内容为:一个对本类的<init>()方法的调用;对应用构造方法内的所有字节码。

如果构造方法不是通过调用自身类的其它构造方法开始,并且该对象不是Object对象,那<init>()方法内则包括的内容为:一个对父类<init>()方法的调用;对实例变量初始化方法的字节码;最后是对应构造子的方法体字节码。

如果这个类是Object,那么它的<init>()方法则不包括对父类<init>()方法的调用。

类的初始化时机本文到目前为止,我们已经大概有了解到了类生命周期中都经历了哪些阶段,但这个类的生命周期的开始阶段--类装载又是在什么时候被触发呢?类又是何时被初始化的呢?让我们带着这三个疑问继续去寻找答案。

Java 虚拟机规范为类的初始化时机做了严格定义:“initialize on first active use”--“在首次主动使用时初始化”。这个规则直接影响着类装载、连接和初始化类的机制--因为在类型被初始化之前它必须已经被连接,然而在连接之前又必须保证它已经被装载了。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章