java 类的初始化_Java 类的初始化

在 Java 代码中,如果要初始化一个静态字段,可以在声明时直接赋值,也可以在静态代码块中对其赋值。如果直接赋值的静态字段被 final 所修饰,并且它的类型是基本类型或字符串时,那么该字段便会被 Java 编译器标记为常量值(ConstantValue),其初始化直接由 Java 虚拟机完成。除此之外的直接赋值操作,以及所有静态代码块中的代码,则会被 Java 编译器置于同一方法中,并把它命名为 。

类加载的最后一步是初始化,便是为标记为常量值的字段赋值,以及执行 方法的过程。 Java 虚拟机会通过加锁来确保 方法仅被执行一次。 只有当初始化完成之后,类才正式成为可执行的状态。

JVM规范枚举了下述几种情况:

当虚拟机启动时,初始化用户指定的主类

当遇到调用 静态方法 的指令时,初始化该静态方法所在的类

当遇到访问 静态字段 的指令时,初始化该静态字段所在的类

当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类

子类的初始化会触发父类的初始化

如果一个接口定义了 default 方法,那么直接或者间接实现该接口的类的初始化,会触发该接口的初识化

使用反射 API 对某个类进行反射调用时,初始化这个类

当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类

下面的示例代码逐个演示上述几种情况:

import java.lang.invoke.MethodHandle;

import java.lang.invoke.MethodHandles;

import java.lang.invoke.MethodType;

import java.lang.reflect.Method;

public class Main {

static {

System.out.println("1. 当虚拟机启动时,初始化用户指定的主类");

}

public static void main(String[] args) throws Throwable {

// Scenario 1,2,3,4

Scenario2.getInstance();

// Scenario 5

new Scenario5();

// Scenario 6

new Scenario6Impl();

// Scenario 7

Class> clazz = Class.forName("Scenario7");

Method method = clazz.getMethod("doSomething", String.class);

method.invoke(clazz.newInstance(), "somevalue");

// Scenario 8

MethodType mt = MethodType.methodType(void.class, int.class);

MethodHandle handle = MethodHandles.lookup().findStatic(Scenario8.class,"println", mt);

handle.invoke(1);

}

}

class Scenario2 {

static {

System.out.println("2. 当遇到调用 静态方法 的指令时,初始化该静态方法所在的类");

}

private static class Scenario3 {

static {

System.out.println("3. 当遇到访问 静态字段 的指令时,初始化该静态字段所在的类");

}

static final Scenario4 INSTANCE = new Scenario4();

}

public static Scenario4 getInstance() {

return Scenario3.INSTANCE;

}

}

class Scenario4 {

static {

System.out.println("4. 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类");

}

}

class Scenario5Parent {

static {

System.out.println("5. 1) 子类的初始化会触发父类的初始化");

}

}

class Scenario5 extends Scenario5Parent {

static {

System.out.println(" 2) 子类的初始化");

}

}

interface Scenario6 {

Scenario6Field field = new Scenario6Field();

default void doSomething() {

}

}

class Scenario6Field {

static {

// 如果删除接口中的default方法,则不会出发Scenario6Field的初始化,就不会打印下面这条语句

System.out.println("6. 1) 如果一个接口定义了 default 方法,那么直接或者间接实现该接口的类的初始化,会触发该接口的初识化 (如果删除接口中的default方法,则不会出发Scenario6Field的初始化,就不会打印这条语句)");

}

}

class Scenario6Impl implements Scenario6 {

static {

System.out.println(" 2) 初始化接口实现类");

}

}

class Scenario7 {

static {

System.out.println("7. 使用反射 API 对某个类进行反射调用时,初始化这个类");

}

public void doSomething(String param) {

}

}

class Scenario8 {

static {

System.out.println("8. 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类");

}

public static void println(int param) {

System.out.println(" print: " + param);

}

}

输出结果:

1. 当虚拟机启动时,初始化用户指定的主类

2. 当遇到调用 静态方法 的指令时,初始化该静态方法所在的类

3. 当遇到访问 静态字段 的指令时,初始化该静态字段所在的类

4. 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类

5. 1) 子类的初始化会触发父类的初始化

2) 子类的初始化

6. 1) 如果一个接口定义了 default 方法,那么直接或者间接实现该接口的类的初始化,会触发该接口的初识化 (如果删除接口中的default方法,则不会出发Scenario6Field的初始化,就不会打印这条语句)

2) 初始化接口实现类

7. 使用反射 API 对某个类进行反射调用时,初始化这个类

8. 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类

print: 1

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值