类初始化是类加载过程的最后一个阶段,到初始化阶段,才真正开始执行类中的Java程序代码。虚拟机规范严格规定了有且只有四种情况必须立即对类进行初始化:

  • 遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果类还没有进行过初始化,则需要先触发其初始化。生成这四条指令最常见的Java代码场景是:使用new关键字实例化对象时、读取或设置一个类的静态字段(static)时(被static修饰又被final修饰的,已在编译期把结果放入常量池的静态字段除外)、以及调用一个类的静态方法时。
  • 使用Java.lang.refect包的方法对类进行反射调用时,如果类还没有进行过初始化,则需要先触发其初始化。
  • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
  • 当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先执行该主类。

虚拟机规定只有这四种情况才会触发类的初始化,称为对一个类进行主动引用,除此之外所有引用类的方式都不会触发其初始化,称为被动引用。下面举一些例子来说明被动引用。

1、通过子类引用父类中的静态字段,这时对子类的引用为被动引用,因此不会初始化子类,只会初始化父类

 class Father{
public static int m = 33;
static{
System.out.println("父类被初始化");
}
} class Child extends Father{
static{
System.out.println("子类被初始化");
}
} public class StaticTest{
public static void main(String[] args){
System.out.println(Child.m);
}
}

执行后输出的结果如下:

父类被初始化
    33

对于静态字段,只有直接定义这个字段的类才会被初始化,因此,通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。

2、常量在编译阶段会存入调用它的类的常量池中,本质上没有直接引用到定义该常量的类,因此不会触发定义常量的类的初始化

 class Const{
public static final String NAME = "我是常量";
static{
System.out.println("初始化Const类");
}
} public class FinalTest{
public static void main(String[] args){
System.out.println(Const.NAME);
}
}

执行后输出的结果如下:

我是常量

虽然程序中引用了const类的常量NAME,但是在编译阶段将此常量的值“我是常量”存储到了调用它的类FinalTest的常量池中,对常量Const.NAME的引用实际上转化为了FinalTest类对自身常量池的引用。也就是说,实际上FinalTest的Class文件之中并没有Const类的符号引用入口,这两个类在编译成Class文件后就不存在任何联系了。

3、通过数组定义来引用类,不会触发类的初始化

 class Const{
static{
System.out.println("初始化Const类");
}
} public class ArrayTest{
public static void main(String[] args){
Const[] con = new Const[5];
}
}

执行后不输出任何信息,说明Const类并没有被初始化。

但这段代码里触发了另一个名为“LLConst”的类的初始化,它是一个由虚拟机自动生成的、直接继承于java.lang.Object的子类,创建动作由字节码指令newarray触发,很明显,这是一个对数组引用类型的初初始化,而该数组中的元素仅仅包含一个对Const类的引用,并没有对其进行初始化。如果我们加入对con数组中各个Const类元素的实例化代码,便会触发Const类的初始化,如下:

 class Const{
static{
System.out.println("初始化Const类");
}
} public class ArrayTest{
public static void main(String[] args){
Const[] con = new Const[5];
for(Const a:con)
a = new Const();
}
}

这样便会得到如下输出结果:

初始化Const类
    根据四条规则的第一条,这里的new触发了Const类。

最后看一下接口的初始化过程与类初始化过程的不同。

接口也有初始化过程,上面的代码中我们都是用静态语句块来输出初始化信息的,而在接口中不能使用“static{}”语句块,但编译器仍然会为接口生成<clinit>类构造器,用于初始化接口中定义的成员变量(实际上是static
final修饰的全局常量)。

二者在初始化时最主要的区别是:当一个类在初始化时,要求其父类全部已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量),才会初始化该父接口。这点也与类初始化的情况很不同,回过头来看第2个例子就知道,调用类中的static
final常量时并不会 触发该类的初始化,但是调用接口中的static final常量时便会触发该接口的初始化。

转自:http://blog.csdn.net/ns_code/article/details/17845821

【深入Java虚拟机】之三:类初始化的更多相关文章

  1. 深入java虚拟机学习 -- 类的加载机制

    当看到"类的加载机制",肯定很多人都在想我平时也不接触啊,工作中无非就是写代码,不会了可以百度,至于类,jvm是怎么加载的我一点也不需要关心.在我刚开始工作的时候也觉得这些底层的内 ...

  2. 深入java虚拟机学习 -- 类的加载机制(续)

    昨晚写 深入java虚拟机学习 -- 类的加载机制 都到1点半了,由于第二天还要工作,没有将上篇文章中的demo讲解写出来,今天抽时间补上昨晚的例子讲解. 这里我先把昨天的两份代码贴过来,重新看下: ...

  3. 深入java虚拟机学习 -- 类的卸载

    类的生命周期 在开始本节之前让我们再来回顾下类的生命周期 没看过前6个过程的同学建议从头看下<深入java虚拟机学习 -- 类的加载机制>,这里就不再过多介绍了,着重说下类的卸载 类的卸载 ...

  4. Java虚拟机的类载入机制

    Java虚拟机类载入过程是把Class类文件载入到内存.并对Class文件里的数据进行校验.转换解析和初始化,终于形成能够被虚拟机直接使用的java类型的过程. 在载入阶段,java虚拟机须要完毕下面 ...

  5. 实战Java虚拟机之三“G1的新生代GC”

    今天开始实战Java虚拟机之三:“G1的新生代GC”. 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟机之三“G1的新生代GC” 实 ...

  6. Java虚拟机,类文件结构深度解析

    Java类文件结构 Java虚拟机不和包括Java在内的任何语言绑定,只与 "Class文件" 这种特定的二进制文件所关联, Class文件中包含了Java虚拟机指令集合符号表以及 ...

  7. 深入理解Java虚拟机(类文件结构)

    深入理解Java虚拟机(类文件结构) 欢迎关注微信公众号:BaronTalk,获取更多精彩好文! 之前在阅读 ASM 文档时,对于已编译类的结构.方法描述符.访问标志.ACC_PUBLIC.ACC_P ...

  8. 《深入理解Java虚拟机》类文件结构

    上节学习回顾 在上一节当中,主要以自己的工作环境简单地介绍了一下自身的一些调优或者说是故障处理经验.所谓百变不离其宗,这个宗就是我们解决问题的思路了. 本节学习重点 在前面几章,我们宏观地了解了虚拟机 ...

  9. 深入java虚拟机学习 -- 类的加载机制(三)

    类的初始化时机 在上篇文章中讲到了类的六种主动使用方式,反射是其中的一种(Class.forName("com.jack.test")),这里需要注意一点:当调用ClasLoade ...

  10. java类加载及类初始化

    1.前言 java是跨平台语言,主要是因为它的java虚拟机的存在,java有事编译语言,所以需要将编写的java文件编译成jvm可运用的class字节码文件.在java中一切皆对象.对于Java虚拟 ...

随机推荐

  1. Python学习记录1

    交互式解释器 模块 python序列 索引提取 序列运算 空列表 成员资格 长度最大值最小值函数 列表 list和join函数 交互式解释器 ' >>> '为提示符. 语句是用来告诉 ...

  2. 如何用纯 CSS 创作牛奶文字变换效果

    效果预览 在线演示 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/MGNWOm 可交互视频教 ...

  3. mcu读写调式

    拿仿真SPIS为例: 对于其他外设(UART.SPIM.I2S.I2C...)都是一个道理. 当MCU写时:主要对一个寄存器进行写,此寄存器是外设的入口(基本都会做并转串逻辑). spis_tx_da ...

  4. 【php】 get 和 post 比较

    来源 php.net 评论区 -- nucc1 worth clarifying: POST is not more secure than GET. The reasons for choosing ...

  5. Python9-day11-作业

    # 1.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),# 要求登录成功一次,后续的函数都无需再输入用户名和密码 FLAG = False def login(func): def ...

  6. python基础学习笔记——collections模块

    在内置数据类型(dict.list.set.tuple)的基础上,collections模块还提供了几个额外的数据类型:Counter.deque.defaultdict.namedtuple和Ord ...

  7. Knockout v3.4.0 中文版教程-14-控制文本内容和外观-style绑定

    5. style绑定 目的 style绑定用来给关联的DOM元素添加或移除一个或多个样式值.在如下情况很有用,比如,当某些值为负时,高亮显示,或者设置容器元素的宽度来匹配数值的改变. (注意:如果你不 ...

  8. Configure Red Hat Enterprise Linux shared disk cluster for SQL Server

    下面一步一步介绍一下如何在Red Hat Enterprise Linux系统上为SQL Server配置共享磁盘集群(Shared Disk Cluster)及其相关使用(仅供测试学习之用,基础篇) ...

  9. iOS8 WebKit库之——WKWebView篇

    iOS8 WebKit库之--WKWebView篇 webkit使用WKWebView来代替IOS的UIWebView和OSX的WebView,并且使用Nitro JavaScript引擎,这意味着所 ...

  10. Sequence Models

    Sequence Models This is the fifth and final course of the deep learning specialization at Coursera w ...