JVM学习笔记17——类加载器双亲委托机制实力剖析
发布日期:2021-06-29 01:18:53 浏览次数:2 分类:技术文章

本文共 4101 字,大约阅读时间需要 13 分钟。

@Override    protected Class
findClass(String className) throws ClassNotFoundException { System.out.println("findClass invoked: " + className); System.out.println("class loader name: " + this.classLoaderName); byte[] bytes = this.loadClassDate(className); return this.defineClass(className,bytes,0,bytes.length); }

在MyTest16中加入输出,发现并不会执行findClass方法

System.out.println(o.getClass().getClassLoader());

输出类加载器看到是AppClassLoader加载的o

上述两点说明在Object o 并不是由我们自定义的类加载器加载的,这是因为双亲委托模型,我们自定义的类加载器委托给了系统类加载器

系统类加载器就是这个类的定义类加载器,系统类加载器和MyTest16就是他的初始类加载器

public class MyTest16 extends ClassLoader{    private String classLoaderName;    private String path;    private final String fileExtension = ".class";    public MyTest16(String classLoaderName){        super(); // 将系统类加载器当做该类加载器的父类加载器        this.classLoaderName = classLoaderName;    }    public MyTest16(ClassLoader parent, String classLoaderName){        super(parent); // 显式指定父类加载器        this.classLoaderName = classLoaderName;    }    public void setPath(String path) {        this.path = path;    }    @Override    public String toString() {        return "MyTest16{" + "classLoaderName='" + classLoaderName + '\'' + '}';    }    @Override    protected Class
findClass(String className) throws ClassNotFoundException { System.out.println("findClass invoked: " + className); System.out.println("class loader name: " + this.classLoaderName); byte[] bytes = this.loadClassDate(className); return this.defineClass(className,bytes,0,bytes.length); } private byte[] loadClassDate(String name){ InputStream is = null; byte[] data = null; ByteArrayOutputStream baos = null; name = name.replace(".","/"); try { is = new FileInputStream(new File(this.path + name + this.fileExtension)); baos = new ByteArrayOutputStream(); int ch = 0; while(-1 != (ch = is.read())){ baos.write(ch); } data = baos.toByteArray(); } catch (Exception e) { e.printStackTrace(); }finally { try { is.close(); baos.close(); } catch (IOException e) { e.printStackTrace(); } } return data; } public static void main(String[] args) throws Exception{ MyTest16 loader1 = new MyTest16("loader1"); // 在本地建立指定目录,然后删掉idea编译的MyTest1,可以发现findClass执行了 loader1.setPath("D:\\workspace\\"); // loader1.setPath("/d/workspace/jvm_lecture/target/classes/"); Class
clazz = loader1.loadClass("com.yshuoo.jvm.classloader.MyTest1"); System.out.println("class: " + clazz.hashCode()); Object object = clazz.newInstance(); System.out.println(object); }}

对MyTest16做出修改,可以指定路径去加载,在系统类加载器默认路径外创建一个完整的路径,之后编译好的MyTest1,执行发现findClass方法执行,证明我们自定义的类加载器从指定路径加载了MyTest1(删掉了原本target里的MyTest1.class 父类加载器——系统类加载器加载失败)

new 一个新的类加载器loader2,和loader1一样从同一的路径加载,重新构造项目恢复MyTest1

public static void main(String[] args) throws Exception{        MyTest16 loader1 = new MyTest16("loader1");        // 在本地建立指定目录,然后删掉idea编译的MyTest1,可以发现findClass执行了        loader1.setPath("D:\\workspace\\");        // loader1.setPath("/d/workspace/jvm_lecture/target/classes/");        Class
clazz = loader1.loadClass("com.yshuoo.jvm.classloader.MyTest1"); System.out.println("class: " + clazz.hashCode()); Object object = clazz.newInstance(); System.out.println(object); System.out.println("************"); MyTest16 loader2 = new MyTest16("loader2"); loader2.setPath("D:\\workspace\\"); Class
clazz2 = loader2.loadClass("com.yshuoo.jvm.classloader.MyTest1"); System.out.println("class: " + clazz2.hashCode()); Object object2 = clazz2.newInstance(); System.out.println(object2); }

可以看出系统类加载器第一次加载过了MyTest1,第二次不再重新加载

再把MyTest1.class删掉

执行结果如下:

两个类加载器都执行了,而且同一个类被加载了两次,这和命名空间有关系

  • 每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成
  • 在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
  • 在不同的命名空间中,有可能会出现类的完整名字爱(包括类的包名)相同的两个类

 

转载地址:https://blog.csdn.net/yshuoo/article/details/116520594 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:JVM学习笔记18——类的命名空间与卸载详解及jvisualvm的使用
下一篇:JVM学习笔记16——类加载器重要方法详解

发表评论

最新留言

不错!
[***.144.177.141]2024年04月01日 16时09分42秒