字节码与类的加载篇--3 类的加载过程详解 -B站尚硅谷JVM课程学习

概述

类的生命周期
在这里插入图片描述
类的使用过程
在这里插入图片描述

加载(loading)阶段

加载完成的操作
加载:将Java类的字节码文件加载到机器内存中,并在内存中构建出Java类的原型–类模板对象。

加载完成的操作:查找并加载类的二进制数据,生成Class的实例

  • 通过类的全名,获取类的二进制数据流
  • 解析类的二进制数据流为方法区内的数据结构
  • 创建Class类的实例,表示该类型。作为方法区这个类的各种数据的访问入口

获取二进制流的方式:只要所读取的字节码符合JVM规范即可,获取之后转换为一个Java.lang.Class实例
类模型的位置:类结构存储在方法区
Class实例的位置:在中创建一个java.lang.Class对象,用来封装类位于方法区内的数据结构
在这里插入图片描述

package chapter203;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class LoadingTest {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("java.lang.String");
            //            获取当前运行时类声明的所有方法
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method m : declaredMethods){
//                获取方法修饰符
                String mod = Modifier.toString(m.getModifiers());
                System.out.print(mod + " " );
//                获取方法返回值类型
                String returnType = m.getReturnType().getSimpleName();
                System.out.print(returnType + "");
//                获取方法名
                System.out.print(m.getName() + "(");
//                获取方法的参数列表
                Class<?>[] ps = m.getParameterTypes();
                if (ps.length == 0) System.out.print(")");
                for (int i = 0; i < ps.length; i++){
                    char end = (i == ps.length - 1) ? ')' : ',';
//                    获取参数类型
                    System.out.print(ps[i].getSimpleName() + end);
                }
                System.out.println();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

数组类的加载

  • 如果是引用类型,就遵循定义的加载过程递归加载和创建数组A的元素类型
  • JVM使用指定的元素类型和数组维度来创建新的数组类

链接(Linking)阶段

验证(Verification):保证字节码是合法、合理并符合规范的,(格式检查和加载阶段同步进行)
在这里插入图片描述

  • 语义检查(是否都有父类,是否定义的final方法被继承或重写,非抽象类实现抽象类方法,是否不兼容方法)
  • 字节码验证(最复杂,是否跳转到一条不存在指令,函数调用传递了正确的类型参数,变量赋值是否正确)
  • 符号引用验证(解析环节执行,方法或类是否存在)

准备(Preparation):为类中静态变量分配内存,初始化默认值
在这里插入图片描述

  • 不包括基本数据类型被static final修饰的,编译时已分配,在常量池中

解析(Resolution):将类、接口、字段和方法的符号引用转换为直接引用

初始化(Initialization)阶段

  • 静态变量赋正确的初始值,真正开始执行类中定义的Java程序代码
  • 执行()方法,由Java编译器生成,由JVM调用,由类静态成员的赋值语句static语句块合并产生的。
  • 由父及子
package chapter203;

import java.util.Random;

/**
 * 使用static final 修饰的字段显示赋值的操作,到底是哪个阶段进行赋值?
 *
 * 情况1:链接阶段的准备环节赋值
 * 情况2:初始化阶段<clinit>赋值
 *
 * 结论:
 * 链接阶段的准备环节赋值情况:
 * 1. 对于基本数据类型字段来说,如果使用 static + final修饰并显示赋值(直接赋值常量而非调用方法)
 * 2. 对于String来说,如果使用字面量的方式赋值,使用static + final修饰
 *
 * 初始化阶段<clinit>赋值情况: 排除上述在准备环节以外都是
 * 
 * 最终结论:
 * 使用static + final 修饰,且显示赋值中不涉及到方法或者构造器调用的基本数据类型或String类型是在链接的准备阶段赋值
 */

public class InitializationTest2 {
//    初始化阶段<clinit>赋值
    public static int a = 1;
//    链接阶段的准备环节赋值
    public static final int INT_CONSTANT = 10;
//    初始化阶段<clinit>赋值
    public static final Integer INTEGER_CONSTANT  = Integer.valueOf(100);
//    初始化阶段<clinit>赋值
    public static Integer INTEGER_CONSTANT2  = Integer.valueOf(1000);
    //    链接阶段的准备环节赋值
    public static final String s0 = "hello";
    //    初始化阶段<clinit>赋值
    public static final String s1 = new String("hello");
    //    初始化阶段<clinit>赋值
    public static final int NUM1 = new Random().nextInt(2);

}

()方法的线程安全性,带有隐式锁,保证线程安全
类的主动使用和被动使用
主动使用:

package chapter203;

import java.io.*;

/**
 * 测试类的主动使用:意味着调用类的<clinit>(),即执行了类的初始化阶段
 * 1. new对象,序列化机制
 * 2. 调用类的静态方法
 * 3. 使用类、接口的静态字段(final修饰特殊考虑)
 */
public class ActiveUseTest1 {
    public static void main(String[] args) {
        Order order = new Order();
        order.method();
    }

//    序列化过程
    public void test1(){
        ObjectOutputStream oos = null;

        try {
            oos = new ObjectOutputStream(new FileOutputStream("order.dat"));
            oos.writeObject(new Order());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(oos != null){
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
//    反序列化过程(验证)
    public void test2(){
        ObjectInputStream ois = null;
        try {
           ois  = new ObjectInputStream(new FileInputStream("order.dat"));
            try {
                Order order = (Order) ois.readObject();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

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

package chapter203;

import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;

import java.util.Random;

/**
 * 3. 使用类、接口的静态字段(final修饰特殊考虑)
 */
public class ActiveUseTest2 {
    public static void main(String[] args) {
        System.out.println(User.num1);//不会
        System.out.println(User.num2);//会
        System.out.println(CompareA.NUM1); //不会
        System.out.println(CompareA.NUM2); //会
    }
}

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

}
interface CompareA {
    public static final Thread t = new Thread() {
        {
            System.out.println("CompareA的初始化。。");
        }
    };
    public static final int NUM1 = 1;
    public static final int NUM2 = new Random().nextInt(22);
}

package chapter203;

import java.util.Random;

/**
 * 4. 使用java.lang.reflect包中的方法反射类的方法时。Class.forName()
 * 5. 初始化子类时,要先初始化父类 -XX:+TraceClassLoading 加载类的trace
 * 补充:初始化类时不会初始化接口
 *      初始化一个接口时不会初始化父接口
 *
 *  6.如果接口中定义default方法,直接或者间接实现该接口的类的初始化,该接口要在其之前初始化
 *  7.虚拟机初始化时,会先初始化带有main方法的类
 */
public class ActiveUseTest3 {

    static {
        System.out.println("ActiveUseTest3 的初始化");
    }

    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("chapter203.Order");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println(Son.num);
        System.out.println(CompareC.NUM);
    }
}

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

interface CompareB{
    public static final Thread t = new Thread() {
        {
            System.out.println("CompareB的初始化。。");
        }
    };
    public default void method1(){
        System.out.println("hello");
    }
}

interface CompareC extends CompareB{
    public static final Thread t = new Thread() {
        {
            System.out.println("CompareC的初始化。。");
        }
    };
    public static final int NUM = new Random().nextInt(22);
}

被动使用

package chapter203;

/**
 * 类的被动使用,即不会进行类的初始化操作
 *
 * 1. 子类引用父类的静态变量,不会导致子类初始化
 * 2. 数组定义类的引用,不会触发类的初始化
 */
public class PassiveUseTest1 {
    public static void main(String[] args) {
//        System.out.println(Child.num);
        Parent[] parents = new Parent[20];
    }
}

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

}
class Child extends Parent{
    static {
        System.out.println("Child 的初始化过程");
    }
}
package chapter203;

/**
 * 3. 引用常量不会触发类或者接口的初始化,因为已经在链接阶段被显示赋值了
 * 4. 调用ClassLoader类的loadClass方法记载类,不是对类的主动使用,不会导致类的初始化
 */
public class PassiveUseTest2 {
    public static void main(String[] args) {
        System.out.println(Person.NUM);
        try {
            Class clazz = ClassLoader.getSystemClassLoader().loadClass("chapter203.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

class Person{
    static {
        System.out.println("Person 的初始化");
    }

    public static final int NUM = 1;
}

类的使用(Using)

静态类成员信息和new关键字

类的卸载(Unloading)

类、类的加载器、类的实例之间的关系
在这里插入图片描述
一个类是否属于不在使用的类的条件:

  • 该类所有实例被回收
  • 加载该类的类加载器被回收
  • Class对象没有任何使用

类的卸载:几乎不卸载

  • 启动类加载器加载的类型在整个运行期间不可能被卸载
  • 被系统类加载器和扩展类加载器加载的类型在运行期间也不太可能被卸载
  • 被开发者自定义的类加载器实例的类型在很简单的上下文环境中才能被卸载
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值