JDK源码阅读(六):Class和ClassLoader
文章目录
Java中每个类都会对应生成一个Class对象,用来保存类信息,ClassLoader是用来将类加载到JVM的
类的加载机制
类加载机制:虚拟机将.class文件从磁盘或其他地方加载到内存,并同时对文件中的数据进行校验、转换、解析和初始化,最终形成可以被虚拟机直接识别的类型。
类的生命周期:
解析可能会在初始化之后,为了实现动态绑定。
加载(装载):
1、通过一个类的全限定名来获取定义此类的二进制字节流。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
加载器:可以使用系统提供的加载器,也可以用户自定义加载器,只需要继承ClassLoader,再重写loadClass()方法就可以实现自己的一个简单的加载器,当需要加载某个类时,就可以自己调用loadClass方法,参数通常为类的全限定名,再根据全限定名获得文件,得到字节流后即可加载。
绝大部分Java程序都会提供一下3种加载器:
Bootstrap ClassLoader(启动类加载器):负责加载JAVA_HOME/lib/里所有能被JVM识别的类。无法被Java程序直接引用,由C++实现,不是ClassLoader的子类。
Extension ClassLoader(扩展类加载器):负载加载java平台扩展功能的一些java包,包括JAVA_HOME/lib/ext/目录中或java.ext.dirs系统变量指定目录下的所有类库,是ClassLoader的子类,开发者可以直接使用。
Application ClassLoader(应用程序类加载器):负责加载Classpath指定的jar包及java.class.path指定目录下的jar包,class.getSystemClassLoader()返回的就是该加载器,开发者可直接使用。
在JVM中,类的加载采用的是双亲委派模型。
该模型有一个前提:除顶层的类加载器之外,其余的类加载器都应该有自己的父加载器,这里使用的而不是继承而是组合,即Application ClassLoader中应当有Extension ClassLoader的引用。
基本的工作过程:当一个类加载器收到了加载类的请求,它首先不会尝试自己去加载这个类,而是将请求委派给自己的父类加载器去加载,如果不能加载,就委派给父类加载器的父类加载器,如果都不能加载,则到达顶层类加载器。每一层的类加载器会根据请求索要加载的类去自己应该加载的目录中搜索有没有对应的类和查看该类是否已经被加载,如果有,那么层加载器加载并返回,如果到达顶层加载器还不能被加载,那么就由最初接收到这个请求的加载器加载。
优点:双亲委派模型很好的解决了各个类加载器的基础类统一的问题,越基础的类由越上层的类加载器加载,分工、责任明确,解决部分安全问题,如自定义的加载器不能加载根加载器应该加载的类,很好的避免了恶意写入基础类。
例如String类位于rt.jar中,该jar包是由Bootstrap ClassLoader加载的,那么如果我们自己写一个类也叫String,那么加载的时候,就会检查出该类已经被加载,所以不再允许其他类加载器加载,因此我们自己写的String类也就不能用了。
验证:确保上一阶段读进来的二进制流中包含的信息符合虚拟机的规范,并且不会危害虚拟机自身。
准备:为类变量分配内存及设置初始零值(boolean false、int 0、float 0.0f、char ‘/u0000’),这些变量所使用的内存都将在方法区中分配。
解析:将符号引用替换为直接引用。
初始化:开始执行类中定义的Java程序代码,类变量和静态块的代码
对于初始化阶段,虚拟机规范进行了严格的规定,有且只有5种情况必须对类进行初始化。
1、虚拟机启动时,虚拟机会初始化包含main 方法的那个类。
2、当初始化一个子类的时候,发现其父类还没有初始化,就会先初始化其父类。
3、当我们使用反射对类进行调用的使用,如果该类还没有进行初始化,就会加载该类并进行初始化。
例:Class.forName(“com.mysql.jdbc.Driver”)
4、遇到new、getstatic、putstatic、invokestatic这4条指令的时候
5、…
Class
Class类中提供了各种获取类信息的方法,反射机制就是基于这些方法
常用方法:
方法名 | 说明 |
---|---|
static Class forName(String name) | 返回指定类名 name 的 Class 对象 |
Object newInstance() | 调用缺省构造函数,返回该Class对象的一个实例 |
Object newInstance(Object []args) | 调用当前格式构造函数,返回该Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class [] getInterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Class getSuperclass() | 返回表示此Class所表示的实体的超类的Class |
ClassLoader
public class ClassLoaderLearning {
public static void main(String[] args) {
//获取系统类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器:" + classLoader);
//获取扩展类加载器
ClassLoader extendClassLoader = classLoader.getParent();
System.out.println("扩展类加载器:" + extendClassLoader);
//获取根加载器
ClassLoader rootClassLoader = extendClassLoader.getParent();
System.out.println("根加载器:" + rootClassLoader);
//检测当前这个类是哪个加载器加载的
try {
ClassLoader currentClassLoader = Class.forName("huyp.learning.jdk8.lang.ClassLoaderLearning").getClassLoader();
System.out.println("类加载器:" + currentClassLoader);
currentClassLoader = Class.forName("java.lang.String").getClassLoader();
System.out.println("类加载器:" + currentClassLoader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
系统类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
扩展类加载器:sun.misc.Launcher$ExtClassLoader@36baf30c
根加载器:null
类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
类加载器:null
解析:
- 根加载器并不是由Java语言实现的,因此拿不到根加载器对象
- 自定义的类是由系统类加载器加载的,jdk包中的String类是由根加载器加载的