大家好,我是鸭鸭!
此答案节选自鸭鸭最近弄的面试神器面试鸭 ,更多大厂常问面试题,可以点击进行阅读哈!
回归面试题!
类加载机制一问基本上就会接着问双亲委派。
双亲委派的意思是:如果一个类加载器需要加载类,那么首先它会把这个类加载请求委派给父类加载器去完成,如果父类还有父类则接着委托,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。
父类也不是我们平日所说的那种继承关系,只是调用逻辑是这样。
在 JDK 9 之前,Java 自身提供了 3 种类加载器:
- 启动类加载器(
Bootstrap ClassLoader
),它是属于虚拟机自身的一部分,用 C++ 实现的,主要负责加载<JAVA_HOME>\lib
目录中或被-Xbootclasspath
指定的路径中的并且文件名是被虚拟机识别的文件。它是所有类加载器的爸爸。 - 扩展类加载器(
Extension ClassLoader
),它是 Java 实现的,独立于虚拟机,主要负责加载<JAVA_HOME>\lib\ext
目录中或被java.ext.dirs
系统变量所指定的路径的类库。 - 应用程序类加载器(
Application ClassLoader
),它是 Java 实现的,独立于虚拟机。主要负责加载用户类路径(classPath
)上的类库,如果我们没有实现自定义的类加载器那这玩意就是我们程序中的默认加载器。
所以一般情况类加载会从应用程序类加载器委托给扩展类再委托给启动类,启动类找不到然后扩展类找,扩展类加载器找不到再应用程序类加载器找。
双亲委派模型不是一种强制性约束,也就是你不这么做也不会报错怎样的,它是一种JAVA设计者推荐使用类加载器的方式。
为什么要有双亲委派机制?
它使得类有了层次的划分。就拿 java.lang.Object
来说,加载它经过一层层委托最终是由 Bootstrap ClassLoader
来加载的,也就是最终都是由Bootstrap ClassLoader
去找\lib
中rt.jar
里面的java.lang.Object
加载到 JVM
中。
这样如果有不法分子自己造了个java.lang.Object
,里面嵌了不好的代码,如果我们是按照双亲委派模型来实现的话,最终加载到 JVM
中的只会是我们rt.jar
里面的东西,也就是这些核心的基础类代码得到了保护。
因为这个机制使得系统中只会出现一个java.lang.Object
。不会乱套了。你想想如果我们 JVM
里面有两个 Object
,那岂不是天下大乱了。
那你知道有违反双亲委派的例子吗?
典型的例子就是:JDBC
。
JDBC
的接口是类库定义的,但实现是在各大数据库厂商提供的 jar
包中,那通过启动类加载器是找不到这个实现类的,所以就需要应用程序加载器去完成这个任务,这就违反了自下而上的委托机制了。
具体做法是搞了个线程上下文类加载器,通过 setContextClassLoader()
默认设置了应用程序类加载器,然后通过 Thread.current.currentThread().getContextClassLoader()
获得类加载器来加载。
JDK 9 关于类加载器进行了一些修改,详细可以看面试鸭:《类加载器有了解过吗?》这题。
最后
最后再推荐下鸭鸭目前努力在做面试神器 面试鸭 ,已经有 2000 多道面试题目啦,欢迎大家来阅读!如果大家有不会的面试题,也可以在面试鸭内反馈!鸭鸭会第一时间为大家解答!
我是鸭鸭,我们下期见~