学习类的加载机制

/**
 类的加载机制(这里的类指泛类:普通类,接口,枚举,注解...等一切类)

一 JVM和类
    1 当调用java命令运行java程序时,该命令会启动一个java虚拟机进程,
         不管该java程序多么复杂,该程序启动了多少进程,它们都在该java虚拟机进程里.
    2 JVM进程被终止(jvm进程结束,该进程在内存中的所有数据都丢失了)
        -正常结束
        -运行System.exit)或Runtime.getRuntime().exit()
        -程序出现了错误或未捕捉的异常
        -平台强制结束了jvm进程
 (II) 类的加载机制,3个步骤
    1 类的加载
    2 类的额连接
    3 类的初始化
二 类的加载
    (I)概念: 类的加载是指将类的class文件读取到内存中,并为之创建一个java.lang.Class对象的过程.(class文件->Class对象的过程)
            (当程序使用任何类都是如此,都会为之创建一个java.lang.Class对象,注意此时并没有执行类的初始化)
    (II)类的加载由类的加载器完成(类加载器通常有JVM提供,当然也可以自定义类加载器,比如后面通过自定义类继承ClassLoader基类)
    (III) 从不同来源加载类的二进制数据(通过不同的类加载器可以办到),类的二进制数据如下几种来源
            1 从本地文件系统加载class文件(大多数类加载的方式)
            2 从jar包中加载class文件(也很常见)
            3 通过网络加载class文件
            4 把一个java源文件动态编译,执行并加载
    (VI) 注意: java虚拟机规范允许系统预先记载某些类,无需等到"首次使用"才加载该类
三 类的连接
    (I)概念: 系统将类的加载生成的Class对象,在类的连接阶段将该类的二进制数据合并到JRE
        分为三个阶段: 验证 -> 准备 -> 解析
    1 验证: 验证被加载的类是否有正确的结构,并和其他类协调一致
    2 准备: 为类的类变量分配内存,并设置默认的初始值.
    3 解析:将类的二进制数据中的符号引用替换成直接引用.(虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程)
四 类的初始化(这里的类指一切类,泛类,一个泛类的初始化步骤前总是先初始化其直接父类)
    概念: 类的初始化阶段,主要是执行类的初始化语句(静态代码块和声明类变量显式初始化的语句)
    类的初始化语句(按定义顺序执行)
        1 在声明类变量时指定初始值的语句
        2 静态代码块
     类变量显式初始化的2种方式
            1 声明类变量时指定初始值
            2 静态代码块中为类变量指定初始值
    类的初始化步骤:其实就2步
    1 加载和连接该类(假如这个类没有被加载和连接)
    2 先初始化其直接父类(假如该类的直接父类还没有被初始化)只是一个判断的过程
    3 执行该类初始化语句(假如该类中有初始化语句).
    注意:
        1 如上2只是一个判断的过程,其实还是1和3,真实的执行步骤: 简单13  复杂时111333
        2 一个泛类的初始化步骤前总是先初始化其直接父类(假如该类的直接父类还没有被初始化)
五 类初始化的时机
    如下6中情况将引发类或接口的初始化.
    1 初始化某个类的子类
    2 通过反射创建某个类或接口对应的Class对象
    3 创建该类的实例
    4 访问某个类的类变量
    5 访问静态方法
    6 直接使用java.exe命令运行某个主类(也是访问静态方法)
 如下行为不会导致类的初始化(初始化该类的静态类)和执行"类.class;"语句
     初始化某个类的静态内部类
     初始化某个类的内部枚举
     初始化某个类的内部接口
     初始化某个类的内部注解
     类.class
     ClassLoader的loadClass(String)方法加载某个类仅仅是执行了类的加载阶段和类的连接阶段,并没有执行类的初始化阶段.
     访问某个类的静态宏变量(小心,别忘了)
        解释一下: 访问类的静态变量,如果它是final类型且在编译时就可以确定下来宏变量.(至于什么是宏变量需要复习一下)
  已经成了宏变量,编译器在编译时直接会把这个类变量出现的地方替换成它的值
 注意:

 */
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
class A {
    static { System.out.println("A的静态代码块");}
    static String name1 = "jack";
    final static String name2 = "lucy";
    static void test(){}
    static void test2(BB bb){new  BB();}
    static class InnerA{
        static String innerA = "mary";
       static  {System.out.println("A的静态内部类的静态代码块");}
    }
    enum InnerEnum{
        SPIRNG;
        static  {System.out.println("A的内部枚举的静态代码块");}
    }
    interface InnerInterface{
        String name = "tom";
    }
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface InnerAnnotation  {
         String name() default "jerry";
    }
}
@A.InnerAnnotation
public class 类的加载机制   {
    static { System.out.println("================");  }
    public static void main(String[] args) throws Exception{

//        System.out.println(new A());//创建该类对象
//        System.out.println(A.name1); //静态变量
//        A.test();//静态方法
//        System.out.println(Class.forName("com.china.school.reflect.A"));//反射
//        System.out.println();//使用java.exe命令运行当前主类main方法,初始化这个当前主类
//        System.out.println(new A(){});//初始化某个类的子类,本条语句初始化A的匿名内部类(也是A的子类)

/**
 * 单独测试如上注释的打印语句都可以看到"A的静态代码块"字样在控制台输出.
 * 全部打印如下语句,没有看到"A的静态代码块"字样在控制台输出,说明A类没有执行初始化步骤
 * 总结: 如下行为将导致类的初始化
 * 访问该类的静态变量,静态方法,创建该类的对象,初始化该类的子类,反射该类的Class对象,java.exe运行当前类的main方法.
 * 如下行为不会: 初始化某类的静态类(静态内部类,内部枚举,内部接口,内部注解)和类.class
 *
 */
        System.out.println(A.InnerA.innerA);//初始化某个类的静态内部类
        System.out.println(A.InnerEnum.SPIRNG);//初始化某个类的内部枚举
        System.out.println(A.InnerInterface.name);//初始化某个类的内部接口
        //初始化某个类的内部注解,获取某个类的内部注解实例
        System.out.println(类的加载机制.class.getDeclaredAnnotation(A.InnerAnnotation.class).name());
        System.out.println(A.class);
        System.out.println(A.name2);
        //访问类的静态变量,如果它是final类型且在编译时就可以确定下来宏变量.(至于什么是宏变量需要复习一下)
        // 已经成了宏变量,编译器在编译时直接会把这个类变量出现的地方替换成它的值
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        Class<?> clazz = classLoader.loadClass("A");
        System.out.println(clazz);
       // ClassLoader的loadClass(String)方法加载某个类仅仅是执行了类的加载阶段和类的连接阶段,没有执行类的初始化阶段
    }
}

输出结果如下:

================
A的静态内部类的静态代码块
mary
A的内部枚举的静态代码块
SPIRNG
tom
jerry
class com.china.school.reflect.A
lucy
class com.china.school.reflect.A

posted @   -和时间赛跑-  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
点击右上角即可分享
微信分享提示