Java类加载的护城河:深入探究双亲委派机制( 三 )

运行结果:
arduino复制代码错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:public static void main(String[] args)否则 JavaFX 应用程序类必须扩展javafx.application.ApplicationProcess finished with exit code 1问题分析:
当运行自己定义的java.lang.String 类时,首先会由系统类加载器(应用程序类加载器)尝试加载这个类 。由于类加载的双亲委派机制,当应用程序类加载器在其类加载缓存无法找到java.lang.String 类时,它会委托父加载器(扩展类加载器)尝试加载 。同样,扩展类加载器也无法找到,会继续委托给引导类加载器 。由于引导类加载器负责加载 Java 核心类库,它会在自己的类路径中找到系统提供的 java.lang.String 类 。因此,最终执行的是核心类库中的 java.lang.String 类,该类没有定义 main 方法,导致执行报错 。
这个示例,证实了双亲委派机制上述所说的沙箱安全机制特性,它阻止了开发人员在核心类库中创建同名类来替代原有的核心类 。这样的机制确保了核心类库的稳定性和一致性,同时也防止了开发人员意外地覆盖核心类的行为 。
全盘负责委托机制: “全盘负责”是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入 。
四、自定义类加载器自定义类加载器只需要继承 java.lang.ClassLoader 类,该类有两个核心方法,一个是loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法 。
代码示例:
java复制代码package com.jvm.classloader;import java.io.FileInputStream;import java.lang.reflect.Method;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.isolves.com/it/cxkf/yy/JAVA/2023-08-21/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("/Users/lan/data/test");// 在路径/Users/lan/data/test下创建test/com/tuling/jvm 几级目录,将Book1类的复制类Book1.class丢入该目录Class clazz = classLoader.loadClass("com.jvm.test.Book1");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("getName", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}}}注意:如果classpath下有com.jvm.test.Book1的.class,先删除 。
因为自定义加载器的父加载器是程序类加载器(AppClassLoader),基于类加载的双亲委派机制,比如我们示例中的com.jvm.test.Book1,会被委托给程序类加载器加载,如果classpath下存在此Book1.class,输出结果将是:sun.misc.Launcher$AppClassLoader 。
因此,为了自定义加载器能按预期从路径其类加载路径/Users/lan/data/test下加载Book1,需要先删除classpath下的Book1.class 。
五、如何打破双亲委派来一个沙箱安全机制示例,尝试打破双亲委派机制,主要是通过重写类加载loadClass方法,实现自己的加载逻辑,不委派给双亲加载 。然后用自定义类加载器加载我们自己实现的java.lang.String.class 。
代码示例:
java复制代码package com.jvm.classloader;import java.io.FileInputStream;import java.lang.reflect.Method;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.isolves.com/it/cxkf/yy/JAVA/2023-08-21/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();}}/*** 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载* @param name* @param resolve* @return* @throws ClassNotFoundException*/protected Class


推荐阅读