java 虚拟机 初始化_【深入理解Java虚拟机】类的初始化过程

类的初始化过程

类的加载过程.png

b6871f6bd2f17df12a0e78cd1339e48f.png

加载

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

验证

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

准备

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

零值.png

d3069902dd0bb5997712f33c79916b63.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."":()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 发布

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值