# 问题

27. ClassLoader 加载类的顺序是什么?

注意:本篇只做简要介绍,详细内容请参考《深入理解Java虚拟机》 (opens new window)

或下文,JVM篇,垃圾回收章节。

# 标准答案

ClassLoader 加载类的顺序遵循父加载委托模型。首先,当前类加载器会将加载请求委托给父加载器,如果父加载器无法加载类,则由当前类加载器加载该类。Java 的类加载器有三种主要类型:根类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和应用程序类加载器(Application ClassLoader)。它们按照父子关系形成了一个加载链,加载顺序从根类加载器开始,直到应用程序类加载器。

# 答案解析

# 核心原理:

Java 的类加载机制遵循父加载委托模型(Parent Delegation Model)。具体来说,当一个类加载器收到加载请求时,它会首先将这个请求委托给父类加载器。如果父类加载器无法加载该类,才会由当前加载器负责加载。通过这种方式,Java 保证了系统的类加载不会冲突,避免了不同版本的类被加载。

类加载的顺序和流程如下:

  1. 根类加载器(Bootstrap ClassLoader)

    • 负责加载 JDK 核心库中的类,如 java.lang.*java.util.* 等。
    • 这些类通常位于 $JAVA_HOME/jre/lib 或类似的目录中。
    • 根类加载器是 JVM 的一部分,由 C/C++ 实现,并且是没有父加载器的。
  2. 扩展类加载器(Extension ClassLoader)

    • 负责加载 Java 扩展库中的类,通常位于 $JAVA_HOME/jre/lib/ext 目录下,或者通过 java.ext.dirs 系统属性指定的目录。
    • 扩展类加载器的父加载器是根类加载器。
  3. 应用程序类加载器(Application ClassLoader)

    • 负责加载应用程序的类,通常是从类路径(Classpath)中加载。
    • 应用程序类加载器的父加载器是扩展类加载器。
  4. 类加载器的委托机制

    • 当某个类加载器(如应用程序类加载器)接收到类加载请求时,它会先委托给父类加载器(如扩展类加载器或根类加载器)加载类。
    • 如果父类加载器无法加载该类,则当前加载器会尝试自己加载。
    • 这种机制保证了核心类库不会被用户自定义类库覆盖,避免了潜在的类冲突问题。

# 加载过程示例:

假设需要加载一个名为 com.example.MyClass 的类,加载顺序如下:

  1. 应用程序类加载器(AppClassLoader)首先会请求扩展类加载器加载该类。
  2. 扩展类加载器将请求传递给根类加载器(Bootstrap ClassLoader)。
  3. 根类加载器检查是否可以加载该类。根类加载器通常只会加载 JDK 核心库中的类,因此根类加载器不负责加载 com.example.MyClass
  4. 如果根类加载器无法加载类,则扩展类加载器会再尝试加载(如果该类在扩展库中),否则将请求交给应用程序类加载器。
  5. 应用程序类加载器最终加载 com.example.MyClass 类。

# 常见错误:

  1. 类加载器冲突:如果同一类存在多个版本,并且被不同的类加载器加载,可能会导致类冲突或 ClassCastException。这种情况通常发生在存在多个 classpath 或类加载器加载器同一个类的不同版本时。
  2. 类加载器优先级理解错误:开发者可能误以为当前类加载器的加载优先级高于父加载器,导致错误的加载结果。

# 最佳实践:

  1. 避免同一个类被多个类加载器加载:在应用中,要确保同一个类只被一个类加载器加载。可以通过设置合理的类路径和类加载器,避免类加载器之间的冲突。

  2. 自定义类加载器时谨慎:如果需要创建自定义类加载器,应该遵循父委托模型,并在必要时重写 findClass() 方法。

  3. 了解类加载顺序:开发者在设计大型应用时需要理解类加载顺序,特别是在使用第三方库或框架时,避免出现不同版本的类被加载。

# 性能优化:

  1. 合理配置 ClassLoader:确保应用中类加载器的配置合理,避免不必要的类加载,例如通过合理的类路径和依赖管理工具(如 Maven 或 Gradle)避免重复加载类。

  2. 延迟加载:对于不常用的类,可以考虑使用懒加载策略,在实际需要时再进行加载,避免启动时加载所有类。

# 深入追问

🔹 在什么情况下,父加载器和子加载器会加载同一个类?如何解决这种冲突? 🔹 如果一个类是由多个类加载器加载的,如何定位正确的类实例? 🔹 自定义类加载器如何影响 Java 应用的性能和安全性?

# 相关面试题

  • 如何实现自定义类加载器?
  • 什么是类加载器双亲委派模型?它有什么优势?
  • 如何调试和诊断类加载器相关的问题?