Java 类的初始化

作者: 小鱼爱小虾 | 来源:发表于2018-10-24 00:16 被阅读7次

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

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

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

  1. 当虚拟机启动时,初始化用户指定的主类
  2. 当遇到调用 静态方法 的指令时,初始化该静态方法所在的类
  3. 当遇到访问 静态字段 的指令时,初始化该静态字段所在的类
  4. 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类
  5. 子类的初始化会触发父类的初始化
  6. 如果一个接口定义了 default 方法,那么直接或者间接实现该接口的类的初始化,会触发该接口的初识化
  7. 使用反射 API 对某个类进行反射调用时,初始化这个类
  8. 当初次调用 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

相关文章

  • 关于java子父类关系的小坑

    学过JavaSE的都知道java类在初始化的时候,如果存在直接父类,是先初始化父类,然后才初始化子类。 ...

  • Java初始化理解与总结

    Java的初始化可以分为两个部分:(a)类的初始化(b)对象的创建(a)类的初始化 **一、概念介绍: ** 一个...

  • java 动态字节码技术

    aop在java中有几种实现方式? java proxy基于接口的实现,构建目标类的实现类(全新的类),初始化的时...

  • Java类加载与反射

    Java类加载与反射 类加载、连接、初始化 JVM和类 当运行某个Java程序时,将会启动一个Java虚拟机进程,...

  • java构造器初始化与清理

    java构造器初始化先后顺序: 若此类继承于某基类,则先初始化基类,以此类推,找到最基本的父类,先对基类进行初始化...

  • 类什么情况下会执行初始化

    初始化一个类,包括执行这个类的静态初始化和初始化在这个类中声明的静态字段。根据Java语言规范,在首次发生下列任意...

  • Java子类初始化顺序

    Print.java Person.java Child.java 输出结果 ->很显然,子类初始化的过程: 父类...

  • Java类的初始化顺序

    Java类的初始化顺序 (静态变量、静态代码块)> 类里的 main()(如果有的话) > (变量、初始化块) >...

  • 构造器内部的多态方法的行为

    上一篇 :Java类初始化顺序 上一节我们简单总结了类的初始化顺序。父类(静态变量、静态初始化块)>子类(静态变量...

  • Java类加载机制

    Java类加载机制 类的生命周期是:加载->验证->准备->解析->初始化->使用->卸载,只有在准备阶段和初始化...

网友评论

    本文标题:Java 类的初始化

    本文链接:https://www.haomeiwen.com/subject/opkyzftx.html