Java类的加载机制
类的加载指的是将类的.class文件的二进制数据读入到内存中,==①将类信息保存在方法区内(在jdk1.8之前,在jdk1.8开始就用元空间代替),②然后在堆区创建一个java.lang.Class对象,用来封装类在方法区类的数据结构,可以使用该对象操作方法区中类的所有信息。==类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且提供了访问方法区内的数据结构的接口(简单理解为方法)。
①加载会将类的一些数据的结构信息保存到方法区
②之后会在堆区创建一个对象用于操作获取方法区中对应的类的数据以及信息等,每个类都会在加载的时候创建java.lang.Class类的对象,此时每个类的对应的堆区的这个对象的类型都是Class类型怎么区分呢?这个类是一个泛型类,使用泛型来区分。
- 堆区对应的这个Class类对象提供了很多方法用于操作方法区中类的数据结构。
比如说要加载一个Car类:
1、将类的结构信息加载到方法区中
2在堆中创建一个数据Car类的Class对象,作用是提供方法让我们可以操作方法区中Car数据结构信息
以上说了可以使用类的Class对象操作类的结构信息,你要使用对象应该先取得对象,这个Class对象怎么取得呢? 有三种方式可以取得:
- 第一种方式:使用对象的getClass()方法,类可以有自己的普通对象(new出来的),可以使用这个对象的getClass()方法,这个方法是继承自最大父类Object
- 第二种方式:使用Class类的一个静态方法:forName(“类的名”)
- 第三种方式:可以直接使用“类名.class”直接取得。
这三种方式最终取得的是在加载的时候保存到堆区类对应的Class类对象。取得之后就可以调用Class类对象的方法操作保存在方法区中的类的信息了
DEMO:操作方法区中的类的构造方法
public class Test {
public static void main(String[] args) throws Exception {
Class<?> classObj = Link.class;
classObj.newInstance(); //实例化保存在方法区中的Link类的对象(本质是调用了Link类的构造方法)
}
}
以上我们说了要将类的字节码文件(*.class)转换成二进制形式加载到内存中,这个加载的过程是类加载器(ClassLoader),类加载分为引导类加载器、扩展类加载器、应用程序加载类、自定义加载器。
类加载器
1、类加载器的作用是将.class文件加载到到内存中的组件*
·引导类加载器(bootstrap class loader)
|-Bootstrp加载器(启动类加载器或者叫做引导类加载器)是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib
·扩展类加载器(extensions class loader)
启动类加载在启动的时候会将ExtClassLoader加载进来,并且将ExtClassLoader的父加载器设置为Bootstrploader(不是使用extends的概念实现父子关系),ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext 。
·应用程序加载器(application class loader )
应用程序加载器又叫做系统类加载器, 启动类加载器(Bootstrploader)加载完扩展类加载器(ExtClassLoader)后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader,系统类加载器主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。
|-可以通过继承java.lang.ClassLoader的方式实现一个自定义的加载器,以满足一些特殊的需求。
2、java.class.ClassLoader的作用
ClassLoader主要对类的请求提供服务,当JVM需要某类时,由ClassLoader返回这个类的class对象。 ClassLoader负责载入系统的所有Resources(Class,文件,来自网络的字节流等),通过ClassLoader从而将资源载入JVM
DEMO:观察
package com.sun;
public class Test {
public static void main(String[] args) throws Exception {
//取得系统类加载器
System.out.println("系统类加载器:"+ClassLoader.getSystemClassLoader());
//取得系统类加载器的父加载器
System.out.println("系统类加载器的父加载器:"+ClassLoader.getSystemClassLoader().getParent());
//取得扩展类加载器的加载器
System.out.println("系统类加载器的父加载器:"+ClassLoader.getSystemClassLoader().getParent().getParent());
}
}
双亲委派加载机制
- 当APPClassLoader加载一个class时,先到自己的缓存中查找,如果缓存中不存在他先不会自己去加载这个类,而是把类加载请求委派给父类加载器EXTClassloader去完成。
- 当EXTClassLoader加载一个class时,先到自己的缓存中查找,如果缓存中不存在它先不会去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
- 当BootStrapClassLoader加载一个类时,先到自己的缓存中查找,如果缓存中不存在它先不会去尝试加载这个类,如果加载失败,则会使用EXTClassLoader去尝试加载。
- 若EXTClassLoader也加载失败,则会使用APPClassLoader来加载,如果APPClassLoader也加载失败,则会报出异常ClassNotFundException.