JVM初始化阶段简述

1<clinit>()方法

        JVM初始化阶段就是执行类变量构造器方法<clinit>()的过程,对准备阶段产生的默认零值进行赋值。此方法无需定义,是由javac编译器自动收集类中的所有类变量的赋值动作和静态代码块,并将其进行合并。

        只有被static修饰并且赋值的静态属性才会产生<clinit>()方法,不管是直接赋值还是在静态块中赋值,最后都会被合并成一个<clinit>()方法,并按照代码中的顺序依次赋值。

public class JvmDemo1 {
    private static int value1 = 1;
    private static int value2;
    static{
        value2 = 2;
    }
}

        若当前类具有父类,JVM会保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕。

2 static与final搭配初始化时机

        使用 static + final 修饰字段的显式赋值的操作,到底是在哪个阶段进行的赋值?

  • 情况1:在连接阶段的准备环节赋值(ps:连接阶段包括验证、准备和解析)。

        在连接阶段的准备环节赋值情况有以下2种:

        (1)对于基本数据类型的字段来说(int、float等),如果使用static final修饰,通常是 在连接阶段的准备环节进行赋值。

        (2)对于字符串String来说,如果使用字面量的方式赋值(String str = "a"),通常是在连 接阶段的准备环节进行赋值。

  • 情况2:在初始化阶段<clinit>()中赋值(ps:初始化阶段才开始有代码执行)。

        排除上述在准备环节赋值的两种情况,剩余的都是在初始化阶段进行赋值。

        最终结论:使用static+final修饰,且显示赋值中不涉及到方法或构造器调用的基本数据类到或String类型的显式赋值,是在连接阶段的准备环节进行。

// 在连接阶段的准备环节赋值
public static final int INT_CONSTANT = 10;  
 // 在初始化阶段clinit>()中赋值
public static final int NUM1 = new Random().nextInt(10);   
// 在初始化阶段<clinit>()中赋值
public static int a = 1;                                                  
// 在初始化阶段<clinit>()中赋值
public static final Integer INTEGER_CONSTANT1 = Integer.valueOf(100);     
// 在初始化阶段<clinit>()中概值
public static Integer INTEGER_CONSTANT2 = Integer.valueOf(100);     
 // 在连接阶段的准备环节赋值
public static final String s0 = "helloworld0";           
 // 在初始化阶段<clinit>()中赋值
public static final String s1 = new String("helloworld1");      
 // 在初始化阶段<clinit>()中赋值
public static String s2 = "hellowrold2";                                 

3 类的初始化情况

        Java程序对类的使用分为两种:主动使用和被动使用。

3.1 主动使用

        Class只有在必须要首次使用的时候才会被装载,Java虚拟机不会无条件地装载Class。Java虚拟机规定,一个类或接口在初次使用前,必须要进行初始化,这里指的“使用”是主动使用,主动使用只有下列几种情况:

        (1)实例化:使用new关键字,或者通过克隆、反序列化创建一个实例。

/**
 * 订单类
 */
class Order implements Serializable {
    static {
        System.out.println("Order类的初始化");
    }
}

public void test() {
    ObjectOutputStream oos = null;
    ObjectInputStream ois = null;
    try {
        // 序列化
        oos = new ObjectOutputStream(new FileOutputStream("order.dat"));
        oos.writeObject(new Order());
        // 反序列化
        ois = new ObjectInputStream(new FileOutputStream("order.dat"));
        Order order = ois.readObject();
    }
    catch (Exception e){
        e.printStackTrace();
    }
    finally {
        ......
    }
}

        (2)静态方法:调用类的静态方法。

public class ClinitDemo {
    @Test
    public void test(){
        Order.staticMethod();
    }
}

class Order implements Serializable {
    static {
        System.out.println("Order类的初始化");
    }

    public static void staticMethod(){
        System.out.println("静态方法");
    }
}

        (3)静态字段:当使用类、接口的静态字段时(final修饰特殊考虑)。

public class ActiveUse {
    @Test
    public void test() {
        // 执行<clinit>()初始化
        System.out.println(User.num);
        // 不执行<clinit>()初始化
        System.out.println(User.num1);
        // 执行<clinit>()初始化
        System.out.println(User.num2);
    }

    @Test
    public void test2(){
        // 不执行<clinit>()初始化,接口中的变量默认都是final
        System.out.println(CompareA.num);
        // 不执行<clinit>()初始化
        System.out.println(CompareA.num1);
        // 执行<clinit>()初始化
        System.out.println(CompareA.num2);
    }
}

class User {
    static {
        System.out.println("User类的初始化");
    }
    public static int num = 1;
    public static final int num1 = 1;
    public static final int num2 = new Random().nextInt(10);
}

interface CompareA{
    public static final Thread t = new Thread(){
        {
            System.out.println("CompareA的初始代码块");
        }
    };

    public static int num = 1;
    public static final int num1 = 1;
    public static final int num2 = new Random().nextInt(10);
}

        (4)反射:当使用java.lang.reflect包中的反射创建类实例。

public class ActiveUse1 {
    @Test
    public void test(){
        try {
            Class.forName("com.lr.gc.Order");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class Order implements Serializable {
    static {
        System.out.println("Order类的初始化");
    }
}

        (5)继承:当初始化子类时,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化,但是这条规则并不适用于接口。

  • 在初始化一个类时,并不会先初始化它所实现的接口。
  • 在初始化一个接口时,并不会先初始化它的父接口。
public class ActiveUse {
    @Test
    public void test(){
        // 先初始化Father,后初始化Child,并不会先初始化它所实现的接口
        System.out.println(Child.num);
    }

    @Test
    public void test1(){
        // 在初始化一个接口时,并不会先初始化它的父接口
        System.out.println(CompareC.t);
    }
}

class Father{
    static {
        System.out.println("Father类的初始化过程");
    }
}

class Child extends Father implements CompareB{
    static {
        System.out.println("child类的初始化过程");
    }
    public static int num = 1;
}

/**
 * 在初始化一个类时,并不会先初始化它所实现的接口
 */
interface CompareB{
    public static final Thread t = new Thread(){
        {
            System.out.println("CompareB的初始代码块");
        }
    };
}

/**
 * 在初始化一个接口时,并不会先初始化它的父接口
 */
interface CompareC extends CompareB{
    public static final Thread t = new Thread(){
        {
            System.out.println("CompareC的初始代码块");
        }
    };
    public static int num = 1;
}

        (6)default方法:如果一个接口定义了default方法,那么直接或间接实现该接口的类在进行初始化前,该接口要在其之前被初始化。

public class ActiveUse1 {
    @Test
    public void test(){
        // 先初始化Father,后初始化Child,并不会先初始化它所实现的接口
        System.out.println(Child.num);
    }
}

class Father{
    static {
        System.out.println("Father类的初始化过程");
    }
}

class Child extends Father implements CompareB{
    static {
        System.out.println("child类的初始化过程");
    }
    public static int num = 1;
}

/**
 * 在初始化一个类时,并不会先初始化它所实现的接口
 */
interface CompareB{
    public static final Thread t = new Thread(){
        {
            System.out.println("CompareB的初始代码块");
        }
    };
    public default void method(){
        System.out.println("初始化调用default method");
    }
}

3.2 被动使用

        被动使用不会引起类的初始化。也就是说,并不是在代码中出现的类,就一定会被加载或者初始化。如果不符合主动使用的条件,类就不会初始化。

        (1)静态字段:通过子类引用父类的静态变量,不会导致子类初始化。

public class PassiveUse {
 	@Test
    public void test() {
        System.out.println(Child.num);
    }
}

class Child extends Parent {
    static {
        System.out.println("Child类的初始化");
    }
}

class Parent {
    static {
        System.out.println("Parent类的初始化");
    }
    
    public static int num = 1;
}

        (2)数组定义:通过数组定义类引用,不会触发此类的初始化

Parent[] parents= new Parent[10];
// 不会对Parent进行初始化
System.out.println(parents.getClass()); 
// new的话才会初始化
parents[0] = new Parent();

        (3)引用常量:引用常量不会触发类或接口的初始化,因为常量在连接阶段就已经被显式赋值了。

public class PassiveUse {
    public static void main(String[] args) {
        // 接口中static默认为常量
        System.out.println(Serival.num);
        // 但引用其他类的话还是会初始化
        System.out.println(Serival.num2);
    }
}

interface Serival {
    public static final Thread t = new Thread() {
        {
            System.out.println("Serival初始化");
        }
    };

    public static int num = 10; 
    public static final int num2 = new Random().nextInt(10);
}

        (4)loadClass方法:调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。

Class clazz = ClassLoader.getSystemClassLoader().loadClass("com.test.java.Person");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大局观的小老虎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值