在Java中,如果你定义了一个类名与Java核心类(比如java.lang.Object
)相同的类,按照默认的类加载机制,这个自定义类将不会被加载。这是因为类加载器在加载类的过程中会按照双亲委派模型,优先委派给父类加载器去加载类。
双亲委派模型
双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载
器。不过这里类加载器之间的父子关系一般不是以继承( Inheritance )的关系来实现的,而是通常使用组合(Composition )关系来复用父加载器的代码。
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
所以当我们定义一个与系统内置类同名的类时,类加载器的双亲委派模型会使得类加载器首先向上级委派请求加载类,直到达到启动类加载器。如果所有的类加载器都无法加载这个类,那么才会由当前类加载器尝试加载。
并且Java的核心类是由启动类加载器(Bootstrap Class Loader)加载的,而启动类加载器是用C++实现的,并不是一个普通的Java类加载器。它负责加载核心的Java API,如 java.lang.Object
等。当虚拟机遇到与核心类同名的类时,由于双亲委派模型,启动类加载器已经加载了这个核心类,因此不会再委派给应用程序类加载器加载你定义的同名类,从而导致你定义的类不会被加载。
所以,尽管你可以定义一个类名与Java核心类相同的类,但是在正常情况下,由于双亲委派机制,自定义类不会被加载,而是使用核心类。
那怎么样才能让它加载我这个类呢?
如果你希望一个类加载器加载你定义的同名类而不使用双亲委派模型,可以考虑以下两种方式:
自定义类加载器
你可以编写一个自定义的类加载器,重写它的loadClass
方法。在这个方法中,你可以控制类的加载逻辑,不再按照双亲委派模型。你可以通过重写loadClass
方法,自己定义加载规则,确保你的类被加载。
public class CustomClassLoader extends ClassLoader { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { // 自定义加载逻辑,确保加载你的类 if ("java.lang.Object".equals(name)) { return findClass(name); } return super.loadClass(name); } }
使用线程上下文类加载器
在一些特殊情况下,你可以使用线程上下文类加载器(Thread Context Class Loader)。线程上下文类加载器是由线程的创建者设定的,它可以独立于双亲委派模型加载类。你可以在代码中通过Thread.currentThread().setContextClassLoader()
来设置线程上下文类加载器,使得该线程在加载类时使用你指定的类加载器。
Thread.currentThread().setContextClassLoader(new CustomClassLoader());
值得注意的是,自定义类加载器需要继承自ClassLoader
类,并实现findClass
方法来定义类的加载逻辑。使用自定义类加载器需要小心,确保不破坏Java类加载的一些基本规则,以避免出现类加载的冲突和问题。