一 点睛
为了保证兼容性,JDK9 没有从根本上改变三层类加载器架构和双亲委派模型,但为了模块化系统的顺利运行,仍然发生了一些值得被注意的变动。
1 扩展机制被移除,扩展类加载器由于向后兼容性的原因被保留,不过被重命名为平台类加载器(platform class loader)。可以通过 classLoader 的新方法 getPlatformClassLoader() 来获取。
JDK9 是基于模块化进行构建(原来的 rt.jar 和 tools.jar 被拆分成数十个 JMOD 文件),其中的 Java 类库就已天然地满足了可扩展的需求,那自然无须再保留 <JAVA_HOME>\lib\ext 目录,此前使用这个目录或者 java.ext.dirs 系统变量来扩展 JDK 功能的机制已经没有继续存在的价值了。
2 平台类加载器和应用程序类加载器都不再继承自java.net.URLClassLoader。
现在启动类加载器、平台类加载器、应用程序类加载器全都继承于jdk.internal.loader.BuiltinClassLoader。
如果有程序直接依赖了这种继承关系,或者依赖了 URLClassLoader 类的特定方法,那代码很可能会在 JDK9 及更高版本的 JDK 中崩溃。
3 在 Java9 中,类加载器有了名称。该名称在构造方法中指定,可以通过 getName() 方法来获取。平台类加载器的名称是 platform,应用类加载器的名称是 app。类加载器的名称在调试与类加载器相关的问题时会非常有用。
4 启动类加载器现在是在 jvm 内部和 java 类库共同协作实现的类加载器(以前是C++实现),但为了与之前代码兼容,在获取启动类加载器的场景中仍然会返回 null,而不会得到 BootClassLoader 实例。
5 类加载的委派关系也发生了变动。当平台及应用程序类加载器收到类加载请求,在委派给父加载器加载前,要先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载。
二 实战——测试 JDK9 关于类加载器的新特性
1 代码
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println(ClassLoaderTest.class.getClassLoader());
System.out.println(ClassLoaderTest.class.getClassLoader().getParent());
System.out.println(ClassLoaderTest.class.getClassLoader().getParent().getParent());
// 获取系统类加载器
System.out.println(ClassLoader.getSystemClassLoader());
// 获取平台类加载器
System.out.println(ClassLoader.getPlatformClassLoader());
// 获取类的加载器的名称
System.out.println(ClassLoaderTest.class.getClassLoader().getName());
System.out.println(ClassLoaderTest.class.getClassLoader().getParent().getName());
}
}
2 测试
jdk.internal.loader.ClassLoaders$AppClassLoader@726f3b58
jdk.internal.loader.ClassLoaders$PlatformClassLoader@368239c8
null
jdk.internal.loader.ClassLoaders$AppClassLoader@726f3b58
jdk.internal.loader.ClassLoaders$PlatformClassLoader@368239c8
app
platform