父子类加载器命名空间隔离本质是各加载器在方法区维护独立类登记表,同名类由不同加载器加载即视为不同类型;双亲委派强化隔离但不制造隔离,打破需显式设置contextClassLoader。

父子类加载器之间的命名空间隔离,本质是每个类加载器实例在 JVM 方法区中维护一个独立的类登记表,彼此不共享、不查询、不穿透。
每个加载器独占一个命名空间
命名空间不是物理存储区域,而是 JVM 内部对“谁加载了哪个类”的映射管理机制。同一个类名(如 com.example.Service),只要由不同加载器实例加载,就会被注册进各自独立的命名空间里,彼此完全无关。
- 父加载器只认识自己加载过的类,以及通过委派成功加载并缓存下来的类
- 子加载器加载的类,只登记在它自己的命名空间中,父加载器既没有引用,也无法遍历或反射访问该空间
- 即使两个加载器都加载了同名类,JVM 也认为它们是两个不同的类型——Class 对象不相等,不能强制转换,也不能互相赋值
双亲委派模型强化了这种隔离
委派机制本身不制造隔离,但它让隔离更可控、更可预期:
- 类加载请求总是先向上委托,只有父加载器明确返回“找不到”,子加载器才执行 findClass
- 这保证了核心类(如 java.lang.String)永远由 Bootstrap 加载,避免子加载器重复定义或篡改
- 反过来,子加载器加载的业务类不会自动暴露给父加载器,因为委派链是单向的、只上不下
隔离效果的典型验证方式
你可以用几行代码直观看到命名空间隔离的结果:
- 用两个 URLClassLoader 分别加载同一个 jar 中的 com.example.User 类,打印 clazz1 == clazz2 → 结果为 false
- 打印两个 Class 对象的 getClassLoader() → 分别指向各自的加载器实例
- 尝试用父加载器调用 Class.forName("com.example.User") → 抛 ClassNotFoundException,不是路径问题,是根本不可见
打破隔离需显式切换上下文
如果确实需要跨层级访问(比如框架回调业务类),不能靠父加载器去“找”子类,而要让执行逻辑运行在正确的类加载器上下文中:
- 设置线程的 contextClassLoader 为子加载器实例
- 后续的 Class.forName、资源加载、SPI 服务发现等,都会基于这个上下文加载器执行
- 注意:不要把子加载的 Class 或实例直接传给父环境代码,否则仍会触发 ClassCastException
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











