类的初始化过程

类的加载过程.png

  • 加载

    将 Class 文件以二进制的形式加载到内存中

  • 验证

    校验 Class 文件是否安全,是否被正确的修改等

  • 准备

    为类变量申请内存,设置默认值,(初始化变量的默认值,比如int初始化为0,reference初始化为null) 但是达到类的初始化之前都没有初始化为真正的值。

零值.png

  • 解析

    将符号引用转换为直接引用

  • 初始化

    搜集并执行static代码块,以及 方法的执行, 是静态变量以及static 代码块组成

  • 使用

    为新对象申请内存, 为示例变量初始化默认值,为实例对象正确的设置初值;生成 方法

  • 卸载

在运行的时候加上虚拟机参数 +XX:+TraceClassLoading 可以详细的看到类加载的信息,同样的要看类卸载的信息,可以使用 -XX:TraceClassUnloading

主动引用与被动引用

主动引用

  • 遇到new , getstatic , putstatic,invokestatic 字节码指令的时候,如果没有初始化,进行性初始化
  • 反射的时候,比如: System.load("xxxx.xxxx.xx");
  • 初始化一个类,但是这个类的父类没有初始化的时候(一个接口初始化的时候并不要求其父接口全部初始化)
  • JVM 需要执行的主类
  • 遇到动态语言支持的时候

被动引用

  • 通过子类引用父类的 静态变量 或者 静态方法,并不会初始化父类。 通过子类引用父类的静态属性,表示对父类的主动使用,而非对子类的主动使用
  • 通过构造类型的数组,不会初始化此类
  • 直接引用某个类的常亮的类型的时候,并不会对该对初始化

示例代码

class SuperClass {
public static String msg = "Hello,World"; static {
System.out.println("SuperClass.static initializer");
}
} class SubClass extends SuperClass { public static String msg2 = "Hello,World"; static {
System.out.println("SubClass.static initializer");
}
}
  • 验证通过子类引用父类的常量 不会初始化子类
   // 验证通过子类引用父类的常量并不会初始化子类
System.out.println(SubClass.msg);
  • 验证初始化子类的同时一定会初始化父类
    System.out.println(SubClass.msg2);
  • 验证初始类型数组的时候并不初始化该类型

对于数组类型,其类型是JVM运行期间动态生成的,类型为[Lxxxx.xxxx.xxxx.xxxx.SubClasss; 其父类为Object,同理二维数组类型为[[Lxxx.xxx.xxx.SubClass; 其父类型为Object;

    SubClass[] subClasses = new SubClass[1];

常量池的引用

  • 源码
public class ReferenceExample002 {

  public static void main(String[] args) {
// 实际运行的是 System.out.println(Hello, World);
// 对于只有运行期才能确定的值,仍然会初始化类
System.out.println(ExampleClass.msg);
}
} class ExampleClass { public static final String msg = "Hello, World"; static {
System.out.println("ExampleClass.static initializer");
}
}
  • 代码反编译后的信息中移除了对ExampleClass 的直接引用

public class ReferenceExample002 {
public ReferenceExample002() {
} public static void main(String[] args) {
System.out.println("Hello, World");
}
}
  • 反编译ReferenceExample002得到的助记符信息如下:

反编译命令如: javap -c xxx.xxx.xxx

public class com.zhoutao.example.ReferenceExample002 {
public com.zhoutao.example.ReferenceExample002();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String Hello, World
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return

编译期间不确定的常量值所在的类将被初始化

package com.zhoutao.classload;

import java.util.UUID;

public class ReferenceExample003 {

  public static void main(String[] args) {

    System.out.println(ExampleClass.uuid);
} static class ExampleClass { // 此处引用的并非真正的常量值
public static final String uuid = UUID.randomUUID().toString(); static {
System.out.println("ExampleClass.static initializer");
}
}
}

ExampleClass 的静态代码块将会被执行: uuid 的值在编译器并不能确定,所以仍然会初始化对应的类,注意和编译期间确定的常量值进行区分。

接口中的常量

​ 当一个接口初始化时候,并不要求其父接口都完成初始化,然后类的初始化的时候要求父类完成初始化,而类的初始化的时候要求父类完成初始化

public class ReferenceExample005 {

  public static void main(String[] args) {
System.out.println(SubInterface.b);
} static interface ParentInterface {
public static int a = 1;
} static interface SubInterface extends ParentInterface {
public static int b = 2;
}
}

JVM 中父接口并不会因为子接口或者其实现类的初始化而初始化,其仅仅在使用该接口的静态变量的时候才会进行初始化

初始化过程的方法

Java 会为每个类生成 方法 ,对于静态变量会生出 方法

public class ReferenceExample006 {

  public static void main(String[] args) {
ExampleClass exampleClass = ExampleClass.getInstance();
System.out.println("a = " + ExampleClass.a);
System.out.println("b = " + ExampleClass.b);
} static class ExampleClass {
public static int a; public static int b = 0; private static ExampleClass exampleClass = new ExampleClass(); private ExampleClass() {
a++;
b++;
} public static ExampleClass getInstance() {
return exampleClass;
}
}
}

对于上面的代码,可以很简单的知道,其输出值为:

a = 1
b = 1

如果将 ExampleClass 中的定义 b 放置于ExampleClass 的私有构造方法之后,那么其输出的值将为:

a = 1
b = 0

这是因为在 方法的搜集static定义及以及代码块的时候,是按照顺序执行的,在私有构造方法时候,将会对b 进行赋值为1,然后在下一步 public static int b = 0; 又重新的将 b 定义为1

本文由博客群发一文多发等运营工具平台 OpenWrite 发布

【深入理解Java虚拟机】类的初始化过程的更多相关文章

  1. 深入理解java虚拟机(4)类加载的过程

    类加载的过程 ------------------------------------------------------- 0.如下图所示JVM类加载机制分为5个部分:加载.验证.准备.解析.初始化 ...

  2. 深入理解java虚拟机---对象的创建过程(八)

    1.对象的创建过程 由于类的加载是一个很复杂的过程,所以这里暂时略过,后面会详细讲解,默认为是已加载过的类.着重强调对象的创建过程. 注意: 最后一步的init方法是代码块和构造方法. 以上是总图,下 ...

  3. 《深入理解java虚拟机》:类的初始化

    深入理解java虚拟机>:类的初始化 类从被载入到虚拟机内存中開始.到卸载出内存为止,它的整个生命周期包含:载入.验证.准备.解析.初始化.使用和卸载七个阶段.当中验证.准备.解析3个部分统称为 ...

  4. 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)

    目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...

  5. 深入理解java虚拟机(八)类加载过程详解

    类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(In ...

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

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

  7. 《深入理解 java虚拟机》学习笔记

    java内存区域详解 以下内容参考自<深入理解 java虚拟机 JVM高级特性与最佳实践>,其中图片大多取自网络与本书,以供学习和参考.

  8. (1) 深入理解Java虚拟机到底是什么?

    好文转载:http://blog.csdn.net/zhangjg_blog/article/details/20380971 什么是Java虚拟机   作为一个Java程序员,我们每天都在写Java ...

  9. 深入理解java虚拟机(5)---字节码执行引擎

    字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...

随机推荐

  1. pandas(一)

    pandas.io 1.概述,主要从txt,json,pkl,csv,excel中读取数据,读取的数据最终转化为pandas.core.frame.DataFrame类型的df 先来看总的api fr ...

  2. 【转】Linux虚拟终端命令Screen用法详解

    转自 http://www.linuxidc.com/Linux/2013-07/87415.htm 在使用ssh或者telnet登录远程主机后,执行一些耗时的命令,如果此时ssh或者telnet中断 ...

  3. Python 语言基础

    Python 语言基础 Python 开发环境 计算机组成 编程语言(计算机语言)是人们为了控制计算机,而设计的一种符号和文字的组合,从而实现向计算机发出指令. 形式是符号和文字的组合 目的是为了控制 ...

  4. EternalBlue永恒之蓝漏洞复现

    EternalBlue漏洞复现 1.    实训目的 永恒之蓝(EternalBlue)是由美国国家安全局开发的漏洞利用程序,对应微软漏洞编号ms17-010.该漏洞利用工具由一个名为”影子经济人”( ...

  5. PAT甲级——1011 World Cup Betting

    PATA1011 World Cup Betting With the 2010 FIFA World Cup running, football fans the world over were b ...

  6. unittest如何在循环遍历一条用例时生成多个测试结果

    引用自:http://blog.csdn.net/kaku21/article/details/42124593 参考网址:http://programmaticallyspeaking.com/te ...

  7. Java发送邮箱

    smtp与pop3 SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议 SMTP 服务器就是遵循 SMTP 协议的发送邮件服务器 POP3是Post Office ...

  8. Ubuntu 12.04 搭建TFTP服务器

    吐槽先:在Ubuntu上搭建TFTP服务器,网上搜到一堆资料,可惜基本都是部分能用,至于哪些部分能用还要自己摸索着试出来,郁闷之情仅次于找不到任何资料…… ---------------------- ...

  9. 成为一名PHP专家其实并不难

    本文作者Bruno Skvorc是一名资深的Web开发者.在这篇文章里主要是讲述成为一名专业的PHP专家所要经历的过程,以及在这个过程里要如何学习掌握技巧和对工具的舍取.(以下为编译内容) 当阅读各种 ...

  10. 吴裕雄--天生自然python学习笔记:python用OpenCV 读取和显示图形

    Open CV 是一个开源.跨平台的计算机视觉库,它可 以在商业和研究领域中免费使用,目前已广泛应用于人机 互动.人脸识别.动作识别.运动跟踪等领域. 要识别特定的图像,最重要的是要有识别对象的特征 ...