前言
- 类加载器是用来加载类的。
- 数组类是由JVM在运行时创建的。
ExtensionClassLoader和AppClassLoader是由谁加载的?
- ExtensionClassLoader和AppClassLoader是由Java代码实现的,也就是说它们也是类,也需要被加载。
- 这时就由BootstrapClassLoader加载,但是BootstrapClassLoader由谁加载?
- BootstrapClassLoader是由C/C++实现的,它和VM是相关联的,VM启动时它也就启动了。
启动类加载器
- 内建于JVM中的启动类加载器会加载java.lang.ClassLoader以及其他的Java平台类,当JVM启动时,一块特殊的机器码会运行,它会加载扩展类加载器与系统类加载器,这块特殊的机器码叫做启动类加载器(Bootstrap) 。
- 启动类加载器并不是Java类,而其他的加载器则都是Java类
- 启动类加载器是特定于平台的机器指令,它负责开启整个加载过程。所有类加载器(除了启动类加载器)都被实现为Java类。不过,总归要有一个组件来加载第一个Java类加载器,从而让整个加载过程能够顺利进行下去,加载第一个纯Java类加载器就是启动类加载器的职责。
- 启动类加载器还会负责加载供JRE正常运行所需要的基本组件,这包括java.util与java.lang包中的类等等。
验证
-
首先ExtensionClassLoader和AppClassLoader在Launcher类中定义的内部类。
-
也就是说加载了Launcher类也就加载了那两个类加载器,只要看Launcher类是谁加载的,就知道 ExtensionClassLoader和AppClassLoader是谁加载的
-
代码
public class BootStrapTest { public static void main(String[] args) { System.out.println(Launcher.class.getClassLoader()); } } -
结果
-
前面说过
null表示BootStrapClassLoader。 -
Launcher类里面有有关ExtensionClassLoader和AppClassLoader如何创建的代码,但是Oracle公司并未开源,需要用OpenJDK,
以后有时间再读去读jdk源码。
Class.forName
jdk源码阅读
- 一般在连接数据库加载驱动的时候都有使用过这个方法,参数就传一个有关驱动名字的字符串,其实它有3个参数。
- 翻译
- 使用给定的类加载器返回与具有给定字符串名称的类或接口关联的Class对象。 给定类或接口的完全限定名称(与getName返回的格式相同),此方法尝试定位、加载和链接类或接口。 指定的类加载器用于加载类或接口。 如果参数loader为空,则通过(BootstrapClassLoader)引导类加载器加载该类。 仅当initialize参数为true并且之前未初始化该类时,
才会初始化该类。 - 如果name表示原始数据类型或 void,将尝试在名称为name的未命名包中定位用户定义的类。因此,此方法不能用于获取任何表示原始数据类型或 void 的Class对象。
- 如果name表示数组类,则加载数组类的组件类型但未初始化。毕竟数组数组是VM在运行期创建的
- 参数:
- name - 所需类的完全限定名称。
- initialize - 如果为true ,类将被初始化。
- loader – 必须从中加载类的类加载器。
- 返回值:
- 表示所需类的类对象
题外话(指定系统类加载器)
System属性
public class BootStrapTest {
public static void main(String[] args) {
//System.out.println(Launcher.class.getClassLoader());
System.out.println(System.getProperty ("sun.boot.class.path"));//BootStrapClassLoader默认加载路径
System.out.println(System.getProperty("java.ext.dirs"));//ExtensionClassLoader默认加载路径
System.out.println(System.getProperty("java.class.path"));//AppClassLoader默认加载路径
System.out.println( "--------");
System.out.println(System.getProperty ("java.system.class.loader")) ;//系统类加载器
System.out.println(ClassLoader.getSystemClassLoader()) ;//加载系统的类加载器
}
}
- 结果
-
看看ClassLoader.getSystemClassLoader()方法源码
-
返回用于委托的系统类加载器。 这是新ClassLoader实例的默认委托父级,通常是用于启动应用程序的类加载器。 此方法首先在运行时启动顺序的早期调用,此时它会创建系统类加载器并将其设置为调用Thread的上下文类加载器。
-
默认的系统类加载器是此类的依赖于实现的实例。
-
如果首次调用此方法时定义了系统属性“
java.system.class.loader”,则该属性的值将被视为将作为系统类加载器返回的类的名称。 该类是使用默认系统类加载器加载的,并且必须定义一个公共构造函数,该构造函数是含有一个ClassLoader类型的参数,该参数用作委托父级。 然后使用此构造函数以默认系统类加载器作为参数创建一个实例。 生成的类加载器被定义为系统类加载器。 -
如果安全管理器存在,并且调用者的类加载器不为空并调用者的类加载器是不一样的或系统类加载器的祖先,则此方法调用安全管理器的checkPermission方法用RuntimePermission("getClassLoader")权限来验证对系统类加载器的访问。 如果没有,将抛出SecurityException 。
-
返回值:
- 用于委托的系统类加载器,如果没有,则为null 抛出:
-
SecurityException – 如果安全管理器存在并且其checkPermission方法不允许访问系统类加载器。
-
IllegalStateException – 如果在构造由“ java.system.class.loader ”属性指定的类加载器期间递归调用。
-
Error – 如果定义了系统属性“ java.system.class.loader ”但无法加载命名类,则提供者类未定义所需的构造函数,或者该构造函数在调用时抛出异常。 可以通过Throwable.getCause()方法检索错误的根本原因。
也就是说?
- 可以自己可以替换系统类加载器。
- 并且必须定义一个公共构造函数,该构造函数是含有一个ClassLoader类型的参数
public UserClassLoader(ClassLoader parent) {//自定义类构造器 super(parent);//显式定义该类加载器的父加载器 } - 只要设置系统属性
java.system.class.loader。
代码
public class BootStrapTest {
public static void main(String[] args) {
//System.out.println(Launcher.class.getClassLoader());
System.out.println(System.getProperty ("sun.boot.class.path"));//BootStrapClassLoader默认加载路径
System.out.println(System.getProperty("java.ext.dirs"));//ExtensionClassLoader默认加载路径
System.out.println(System.getProperty("java.class.path"));//AppClassLoader默认加载路径
System.out.println( "--------");
System.out.println(System.getProperty("java.system.class.loader")) ;//系统类加载器
//System.out.println(ClassLoader.getSystemClassLoader()) ;//加载系统的类加载器
System.out.println(ClassLoader.getSystemClassLoader().getClass()) ;//加载系统的类加载器
}
}
- 需要使用 -D<名称>=<值> 设置系统属性
- 对于本人自定义的类加载器来说命令如下:
java -D"java.system.class.loader"="com.jvmstudy.classloading.UserClassLoader" com.jvmstudy.classloading.BootStrapTest - 结果