扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:天新网 来源:天新网 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领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者