都知道JDK9之后将扩展类加载器(ExtensionClassLoader)重命名为平台类加载器(PlatformClassLoader),难道只是简单的重命名吗?都有什么变化?

带来的变化

1.双亲委派模型的改变

JDK8:双亲委派是严格的“向上查找”:自定义——>App——>Ext——>Boot

JDK9+:为了适配模块化,当类加载器收到加载请求时,会先判断该类是否属于某个具体的模块

如果该类已经归属于某个模块,Java 会直接将请求委派给负责该模块的类加载器,而不再一层层往上托举

什么?谁判断的该类是否属于某个具体的模块?is引入的BuiltinClassLoader判断的

package jdk.internal.loader;
  
protected Class<?> loadClassOrNull(String cn, boolean resolve) {
        synchronized (getClassLoadingLock(cn)) {
            // 检查这个类是否已经被加载过
            Class<?> c = findLoadedClass(cn);

            if (c == null) {

                // 查找它属于哪个模块
                LoadedModule loadedModule = findLoadedModule(cn);
                // --------- 情况 A:属于某个模块(走直达) -----------
                //查到了,属于某个模块
                if (loadedModule != null) {

                    //获取这个模块的类加载器
                    BuiltinClassLoader loader = loadedModule.loader();
                    //如果是我自己
                    if (loader == this) {
	                    //模块系统初始化完成后,由我自己去加载
                        if (VM.isModuleSystemInited()) {
                            c = findClassInModuleOrNull(loadedModule, cn);
                        }
                    } else {
                        //甩给对应的加载器,实现“直达”
                        c = loader.loadClassOrNull(cn);
                    }

                } else {

                    //--------- 情况 B:不属于任何已知模块(走传统双亲委派) -----------
                    //向上委派
                    if (parent != null) {
                        c = parent.loadClassOrNull(cn);
                    }

                    // 向下退回
                    if (c == null && hasClassPath() && VM.isModuleSystemInited()) {
                        c = findClassOnClassPathOrNull(cn);
                    }
                }

            }

            if (resolve && c != null)
                resolveClass(c);

            return c;
        }
    }

什么是模块化?

JDK9之前,只有包和Jar包,

问题一:只要一个jar包在ClassPath里,它里面所有的public类都能被任意访问,但我的public只想当前组件内部用,不暴露给外部

问题二:如果运行时两个jar包里有同名同包的类,先扫描的会覆盖后面的,会报错(如 NoSuchMethodError)。

模块化系统,在包之上加了层:模块

一个模块不仅包含代码,还包含核心描述文件module-info.java:

  • 我导出了什么(exports): 只有被导出的包,别的模块才能访问。
  • 我依赖了什么(requires): 明确声明我需要哪些模块才能运行。

2.启动类加载器的改变

脱离了 C++ 的“隐形”实现: 在过去,Bootstrap ClassLoader 完全由 C++ 实现,在 JDK 9 之后引入了BuiltinClassLoader 的 Java 类。现在,Bootstrap、Platform 和 App 加载器全都继承自 BuiltinClassLoader

不再加载所有核心库: 过去所有的 Java 核心类都由 Bootstrap 一把抓。现在,核心类被拆分成了不同的模块,Bootstrap 只负责加载最核心的模块(如 java.base),而 java.sql(数据库相关)、java.xml 等模块,则交给了平台类加载器去加载。

3.目录结构与打包文件消失

rt.jartools.jar 消失了,所有内置类被重新打包成模块,存在了lib/modules

4.类加载器微调

内置的三个加载器都改为了 BuiltinClassLoader 的子类

好处:

  • 运行时性能与启动速度的飞跃,省去了沿途无效的逐级托举和全盘文件扫描
  • 强封装性,阻止访问核心库内部的私有实现,
  • 配置更可靠,项目依赖A.jar A.jar依赖B.jar,运行时不报错,但是执行A.jar里的某行代码,要加载B.jar里的类时,就会抛ClassNotFoundExceptionNoClassDefFoundError现在: 模块化要求在 module-info.java 中明确声明 requires 哪些模块。
 package java.lang.module;
 public Builder requires(Requires req) {
            if (automatic)
                throw new IllegalStateException("Automatic modules cannot declare"
                                                + " dependences");
            String mn = req.name();
            if (name.equals(mn))
                throw new IllegalArgumentException("Dependence on self");
            if (requires.containsKey(mn))
                throw new IllegalStateException("Dependence upon " + mn
                                                + " already declared");
            requires.put(mn, req);
            return this;
        }
  • jlink自动裁剪,用什么裁什么
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐