类加载机制

一、详解类加载机制

    1.类加载机制:将javac编译后生成的Class字节码文件(一串二进制的字节流)加载到内存中,并对数据进行验证准备解析初始化,然后在堆区创建一个java.lang.Class对象,可被JVM直接使用。

    2.类的生命周期:加载  --->  验证-->准备-->解析  --->  初始化  --->  使用  --->  卸载

    3.加载通过类加载器,将class字节码文件加载到内存。(class字节码文件可来自各种途径)
             加载过程: a.通过类的全名获取对应的二进制字节流;  b.将二进制字节流的静态存储结构转化为方法区的运行时数据结构;  c.在堆中生成一个java.lang.Class对象。
             结果:方法区内多了类的二进制数据,堆中增加一个Class对象(反射对象)。

    4.链接:将二进制字节码合并到JVM的运行状态中。
             验证:确保字节码信息符合JVM规范,保证安全;
             准备:为static变量分配内存、附默认值(不会直接赋值),给常量直接赋值;
             解析:将各种符号引用替换为直接引用。1.类或接口的解析. 2.字段解析. 3.类方法解析. 4.接口方法解析.

    5.初始化:执行类构造器<clinit>()方法的过程。类构造器由static变量和static代码块组成。初始化的本质就是执行了静态变量和静态代码块;初始化子类时,先执行父类的类构造器。    对象的初始化,是先初始化成员变量,再执行构造方法

    6.类的主动引用和被动引用:是否进行类初始化。
             主动引用: new对象;  反射调用;  调用静态属性/方法(final除外);  main()所在的类;  子类被调时先初始化父类 
             被动引用: 子类引用父类静态数据,子类不初始化;  MyObj[] arr = new MyObj[1];数组定义某个类,不初始化;   引用final常量不初始化(链接中的准备/解析两步,已经把常量存到常量池中并直接引用)

    7.初始化代码执行顺序: 1.父类静态方法/静态代码块(跟代码顺序一致)   2.子类static   3.父类成员变量初始化,再构造函数   4.子类成员变量初始化,再构造函数。

    8.使用:对类进行实例化。  卸载:该类的所有实例对象都不被引用时,类在方法区中的数据就会被卸载(类的卸载取决于它的实例能活多久).

二、有哪些类加载器,分别加载哪些文件

    1.引导类加载器(Bootstrap ClassLoader):C/C++实现,加载原生代码 JAVA_HOME/jre/lib/rt.jar (java基础类库)
    2.扩展类加载器(Extention ClassLoader):加载 JAVA_HOME/jre/ext/*.jar
    3.应用程序类加载器(APP ClassLoader):根据应用的类路径(classpath)加载,一般java应用类都由它加载,是默认的classLoader
    4.自定义类加载器:继承java.lang.ClassLoader,加载指定类。一般覆盖findClass()方法,而不要直接改写loadClass()方法。自定义的CL一般用来加载其他途径的class字节码文件,默认parent是APP CL。

    5.java.lang.ClassLoader抽象类,后3个CL均继承自它。作用:加载二进制字节码到方法区,在堆中生成Class反射对象(类声明周期中加载的步骤)。类加载器之间的关系不是继承,而是组合: 成员变量 private final ClassLoader parent;

    6.ClassLoader抽象类中的抽象方法:
         1.loadClass(String s); 加载指定名字的类。此方法先从已加载的类中找,如果找到了直接返回,没找到则双亲委派;最后调用findClass方法找。
         2.findClass:此方法直接返回ClassNotFoundException,自定义CL时重写此方法。
         3.findLoadedClass:从当前CL中(metaspace区域)找该类是否已被加载。
         4.resolveClass:完成Class对象的链接。

    7.双亲委派机制:先给最上面的CL加载,如果不行再依此向下,自己也加载不了则报错。委托是从下向上,具体查找过程是自上至下。  作用: 保证java核心库的安全(自己写的java.lang.String不可能被加载)。
    8.同一个ClassLoader加载的同一个类,JVM才认为是相同的类;不同CL加载同一个类,类不一样。
    9.反射时,调用此类默认的那个类加载器,保证各种途径加载的是同一个类。

三、手写类加载demo

// 双亲委派 大致源码
Class<?> loadClass(String name, boolean resolve) {
    Class<?> c = findLoadedClass(name);    //先从已经加载过的类中去找
    if (c == null) {
        try {
            if (parent != null) {
                c = parent.loadClass(name, false);    //用上游的CL加载
            } else {        //扩展类加载器的parent是null,所以有else
                c = findBootstrapClassOrNull(name);    //用引导类加载器试试,native方法
            }
        } catch (ClassNotFoundException e) {
        }
        if (c == null) {
            c = findClass(name);    //上游都不行,自己来
        }
    }
    return c;
}

四、Class.forName("java.lang.String") 和 String.class.getClassLoader().loadClass("java.lang.String")   区别是什么?

    1.Class.forName("java.lang.String"):反射调用,是主动引用,会进行类的: 加载、链接(解释类)、初始化(执行static)
        数据库驱动加载就是使用Class.froName(“com.mysql.jdbc.Driver”):在这个类里面有一个static代码块,里面写的初始化操作

    2.ClassLoader.loadClass("java.lang.String"):只做类的加载操作(将字节码文件加载到JVM:方法区中添加类的字节码数据、堆中生成一个反射对象)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值