Java反射 - 基础知识 - Class类、.class文件、ClassLoader类装载器

203 阅读4分钟

一、.class文件和Class是什么

1、概述.class文件

  • 每个类都写在一个.java文件中,.java文件编译后会形成一个.class文件。所以可以这么理解:一个类 对应 一个.class文件

2、Class类是什么

  • 在Java中,“类”是具备某些共同特征的实体的集合。比如 学生 ,所有的学生都具有以下属性:姓名、年龄。那么我们就可以抽象出来一个Student类,它有两个属性 name 、age
public class Student {
    // 每个学生都有姓名
    String name;
    // 每个学生都有年龄
    String age;
}
  • 如果把所有的 抽象一下呢?所有的类都有类名、所有的类都有方法。这么一抽象,就得到了Class类(下面代码仅为了方便理解,并非源码)
public class Class {
    // 每个类都有类名
    String className;
    // 每个类都有方法,可能是多个
    List<Method> methods;
    // 每个类都有成员变量,可能是多个
    List<Field> fields;
}
  • 就像Student类的每个对象都代表一个具体的学生一样,Class类的每个对象,都代表一个具体的.class文件,或者说一个具体的类。比如说 Student类/Student.calss文件 就可以看作是Class的一个对象。

3、获取Class类的对象

  • 类的对象可以理解为某个抽象概念中的一个具体事物,比如Student类可以new一个对象张三zhangSan,也可以new一个对象叫李四liSi,张三/李四都是具体的学生,是Student的两个对象
Student zhangSan = new Student();
Student liSi = new Student();
  • Class类的构造函数是私有的,所以我们不能直接使用Class claz = new Class()来创建Class对象。JVM将某个.class文件从硬盘上加载到内存中的时候,会根据当前这个.class文件创建出Class的一个对象
  • 要知道,像Student这种普通的类,我们可以直接new并拿到一个对象zhangSan,可是Class类的对象都是由JVM创建的,开发者在代码中如何拿到想要的Class对象呢?有以下三种方式,看代码
public class Test {

    public static void main(String[] args) throws ClassNotFoundException {
        // 1、  对象.getClass() 获取
        Student student = new Student();
        Class stuClass01 = student.getClass();

        // 2、  类.Class 获取
        Class stuClass02 = Student.class;

        // 3、  Class.forName(全路径类名)
        Class stuClass03 = Class.forName("com.example.xxx.reflect.Student");
    }

}

二、ClassLoader — 类加载器

1、类加载器的工作机制

  • 类加载器负责寻找类的.class文件并构造出类在JVM内部表现对象

  • 在Java中,类加载器把一个类装载到JVM中需要经过以下几步

    • 装载:查找和导入.class文件
    • 校验:检查载入.class文件数据的正确性
    • 准备:给类的静态变量分配存储空间
    • 解析(可选择):将符号引用转为直接引用
    • 初始化:对类的静态变量、静态代码块执行初始化操作
  • 类装载工作是由ClassLoader及其子类完成的,JVM运行时会产生3个类装载器。 每个装载器都有其父装载器,不是父类!!

    • 根装载器BootstrapClassLoader:负责装载JRE中的核心类库,C++写的,Java中看不到
    • 扩展装载器ExtClassLoader:负责装载JRE扩展目录ext中的JAR类包
    • 应用装载器AppClassLoader:负责装载classPath路径下的类包
    public class Test {
        public static void main(String[] args) throws Exception {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            // 当前类的装载器是AppClassLoader
            System.out.println("AppClassLoad应用装载器:" + classLoader);
            // AppClassLoader其父是ExtClassLoader
            System.out.println("ExtClassLoad扩展装载器:" + classLoader.getParent());
            // ExtClassLoader其父是根装载器,由于是C++编写,故Java中获取不到它的引用
            System.out.println("根装载器:" + classLoader.getParent().getParent());
        }
    }
    
    控制台打印信息如下:
       AppClassLoad应用装载器:sun.misc.Launcher$AppClassLoader@18b4aac2
       ExtClassLoad扩展装载器:sun.misc.Launcher$ExtClassLoader@404b9385
       根装载器:null
    
  • 全盘负责机制

    • 当一个ClassLoader装载一个类时,除非显式地指定另一个ClassLoader,否则这个类所依赖和引用的类也由这个ClassLoader载入
  • 双亲委托机制

    • 当一个ClassLoader装载一个类时,会先委托他的父装载器寻找目标类,找不到的情况下由子装载器从自己的路径下查找并装载
      • 比如:AppClassLoader想要装载一个类Student,查缓存,没有的话就去委托父装载器ExtClassLoader装载
      • ExtClassLoader想要装载Student类,查缓存,没有的话去委托父装载器BootstrapClassLoader装载
      • BootstrapClassLoader想要装载Student类,查缓存,没有的话自己找一圈,没找到则让ExtClassLoader装载
      • ExtClassLoader找了一圈也没找到,让AppClassLoader装载
      • AppClassLoader如果在classPath里找到了,就开始装载。如果没找到,抛出各种异常

2、classLoader的重要方法

  • loadClass(String name):参数name是全限定类名,如com.xxx.Student这种
    // name:全限定类名
    public Class<?> loadClass(String name) throws ClassNotFoundException{
        return loadClass(name, false);
    }
    
    // name:全限定类名   resolve:是否需要解析该类
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
        ...
    }
  • getParent():获取当前装载器的父装载器(不是父类)
public final ClassLoader getParent(){
    ...
}
  • 除了上面提到的3个类装载器,用户还可以自定义类装载器,需要继承抽象类ClassLoader
  • 类文件被装载和解析后,在JVM中将拥有一个对应的java.lang.Class对象 image.pngq