【面试题】Java ClassLoader(Java类加载器)
1. 什么是类加载器?
类加载器负责加载所有的类,其为所有被载入内存中的类生成一个java.lang.Class实例对象。一旦一个类被加载如JVM中,同一个类就不会被再次载入了,实现这个动作的代码模块称为“类加载器”。
2. 有哪些类加载器,分别加载哪些类?
根类加载器(Bootstrap ClassLoader):它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.ClassLoader(负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类)。
扩展类加载器(Extensions ClassLoader):它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。
系统类加载器或应用程序类加载器(System ClassLoader、App ClassLoader ):被称为系统(也称为应用)类加载器,它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。
3. 双亲委派模型
应用程序都是由以上三种类加载器互相配合进行加载的,还可以加入自己定义的类加载器。称为类加载器的双亲委派模型 ,使用 组合关系 来复用父加载器的。
4. 双亲委派模型的工作原理
双亲委派机制,其工作原理的是,如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式的工作原理
5. 使用双亲委派模型好处?
可以避免重复加载:当父亲已经加载了该类的时候,子类不需要再次加载。
避免核心类篡改:考虑到安全因素,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的类装载器。
6、一道面试例题
能不能自己写个类叫java.lang.System?
答案:一般来说是不可以的,因为类加载采用委派机制,父类加载器能够加载的类就不会交由子类加载器进行处理,而System类是Bootstrap加载器加载的,就算自己重写,也总是使用Java系统提供的System,自己写的System类根本没有机会得到加载。但我们可以自己定义一个类加载器来达到这个目的,为了避免双亲委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,这样就能将我们自己写的类交由自定义类加载器进行处理。