jvm双亲委派机制详解( 二 )


我们来看下应用程序类加载器AppClassLoader加载类的双亲委派机制源码,AppClassLoader的loadClass方法最终会调用其父类ClassLoader的loadClass方法,该方法的大体逻辑如下:
我们来看下应用程序类加载器AppClassLoader加载类的双亲委派机制源码 , AppClassLoader的loadClass方法最终会调用其父类ClassLoader的loadClass方法,该方法的大体逻辑如下:
我们来看下应用程序类加载器AppClassLoader加载类的双亲委派机制源码,AppClassLoader的loadClass方法最终会调用其父类ClassLoader的loadClass方法,该方法的大体逻辑如下:

  1. 首先 , 检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回 。
  2. 如果此类没有加载过,那么 , 再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载 。
  3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载 。
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded// 检查当前类加载器是否已经找到Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {// 有无父类加载器if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}为什么要设计双亲委派机制?沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次 , 保证被加载类的唯一性
例子:比如我们自己新建了一个java.lang.String类,我们看能不能加载成功 。
package java.lang;public class String {public static void main(String[] args) {System.out.println("============自己的类加载器====");}}// 执行结果错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:public static void main(String[] args)否则 JavaFX 应用程序类必须扩展javafx.application.Application自定义类加载器自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass , 默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法 。
public class MyClassLoaderTest {static class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name+ ".class");int len = fis.available();byte[] data = https://www.huyubaike.com/biancheng/new byte[len];fis.read(data);fis.close();return data;}protected Class findClass(String name) throws ClassNotFoundException {try {byte[] data = loadByte(name);//defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组 。return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}}public static void main(String[] args) throws Exception {//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoaderMyClassLoader classLoader = new MyClassLoader("D:/test");//D盘创建 test/com/hyz/jvm 几级目录 , 将User类的复制类User1.class丢入该目录Class clazz = classLoader.loadClass("com.hyz.jvm.User1");//Class clazz = classLoader.loadClass("java.lang.String");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("hello", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}}打破双亲委派机制?假如我们的target下有一个User类,但是我们的程序代码中需要去读取D:/test/com/hyz/jvm/User.class类,根据双亲委派机制 , 肯定是会加载到target下的User类的,要如何才能加载到D:/test下的User类呢?

推荐阅读