# 17. 讲一下classloader(类加载器),有几种?

# 标准答案

Java 中的类加载器(ClassLoader)是负责动态加载类的工具,主要功能是从外部资源加载 Java 类的字节码文件。Java 提供了多种类加载器,常见的有三种:启动类加载器扩展类加载器应用程序类加载器。类加载器通过双亲委派模型来确保类加载的安全性和稳定性。

# 答案解析

Java 类加载器(ClassLoader)负责将类的字节码从外部源(如磁盘、网络等)加载到 JVM 中,并将其转化为 JVM 可执行的类对象。类加载器的工作机制基于 双亲委派模型,即一个类加载器的加载请求会首先委托给其父加载器,直到根加载器(启动类加载器)为止。

# 1. 启动类加载器(Bootstrap ClassLoader)

启动类加载器是 Java 虚拟机中最顶层的加载器,它负责加载 JRE/lib 目录下的核心类库(如 rt.jar)以及 JVM 必需的基本类库。启动类加载器由 JVM 实现,通常使用 C++ 编写,因此它不是 Java 类,可以说是 Java 的本地代码。启动类加载器没有父类加载器,它是整个类加载器层次结构的根。

# 2. 扩展类加载器(Extension ClassLoader)

扩展类加载器用于加载 JDK 的扩展类库,这些库位于 JRE/lib/ext 目录中。扩展类加载器是 ClassLoader 的子类,它的父加载器是启动类加载器。它负责加载 JDK 提供的额外功能,如 javax 等相关的扩展类库。

# 3. 应用程序类加载器(Application ClassLoader)

应用程序类加载器是我们最常接触到的类加载器,它负责加载 classpath(即系统环境变量中的 CLASSPATH 路径或者启动时指定的路径)中存放的应用程序类。通常应用程序类加载器会加载项目中的 .class 文件或 JAR 包。它的父加载器是扩展类加载器。

# 4. 自定义类加载器(Custom ClassLoader)

Java 允许开发者自定义类加载器,继承 ClassLoader 类并重写 findClass 方法来定义自己的类加载规则。自定义类加载器常用于一些特殊的场景,如插件系统、热更新等。在这些场景下,系统可能需要动态加载一些类或资源,而不希望使用默认的类加载器。

# 双亲委派模型

Java 的类加载机制采用了 双亲委派模型。即当某个类加载器加载一个类时,它会首先委托给父类加载器去加载,直到找到启动类加载器。只有当父类加载器无法加载时,当前类加载器才会尝试自行加载。这样可以防止重复加载同一个类,同时也增强了系统的安全性。

  1. 父加载器的委托优先级:类加载器在加载一个类时,会先检查自己是否已经加载过该类。如果没有,它会将加载任务委托给父类加载器。
  2. 防止类冲突:双亲委派模型能够避免不同加载器加载同名类,从而避免类冲突问题。

# 常见问题

  • 自定义类加载器的用途:可以通过自定义类加载器加载特殊的类文件,例如动态加载插件、从网络上加载类等。
  • 类加载的顺序:类加载器通过父加载器委托的顺序加载类,这一机制确保了类的唯一性和一致性。

# 深入追问

  1. 如果应用程序类加载器加载了一个类,为什么还要有扩展类加载器和启动类加载器?
  2. 自定义类加载器如何避免类加载冲突,如何解决类加载器之间的相互依赖?
  3. 如何使用反射来获取类的加载器,如何通过类加载器的不同作用来影响类的加载?

# 相关面试题

  • Java 类加载时,如何确保一个类只加载一次?
  • 双亲委派模型的工作原理是怎样的?为什么需要双亲委派?
  • 类加载器的 loadClassfindClass 方法有什么区别?