一、.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里找到了,就开始装载。如果没找到,抛出各种异常
- 当一个ClassLoader装载一个类时,会先委托他的父装载器寻找目标类,找不到的情况下由子装载器从自己的路径下查找并装载
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对象