文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么

2023-06-06 11:19

关注

这篇“Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么”文章,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要参考一下,对于“Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么”,小编整理了以下知识点,请大家跟着小编的步伐一步一步的慢慢理解,接下来就让我们进入主题吧。

Java可以用来干什么

Java主要应用于:1. web开发;2. Android开发;3. 客户端开发;4. 网页开发;5. 企业级应用开发;6. Java大数据开发;7.游戏开发等。

JVM自带的类加载器:

Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么

其关系如下:

Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么

其中,类加载器在加载类的时候是使用了所谓的“父委托”机制。其中,除了根类加载器以外,其他的类加载器都有且只有一个父类加载器。

关于父委托机制的说明:

Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么

当生成 一个自定义的类加载器实例时,如果没有指定它的父加载器,那么系统类加载器将成为该类加载器的父类加载器

下面,自定义类加载器。自定义的类加载器必须继承java.lang.ClassLoader类

import java.io.*;public class MyClassLoader extends ClassLoader {  private String name;  //类加载器的名字  private String path;  //加载类的路径  private final String fileType = ".class"; //class文件的扩展名  public MyClassLoader(String name){    super(); //让系统类加载器成为该类加载器的父 类加载器,该句可省略不写    this.name = name;  }  public MyClassLoader(ClassLoader parent,String name){    super(parent); //显示指定该类加载器的父 类加载器    this.name = name;  }  @Override  public String toString() {    return this.name;  }  public String getPath() {    return path;  }  public void setPath(String path) {    this.path = path;  }  //实现自定义的类加载器必须重写findClass方法,否则ClassLoader类中的findClass()方法是抛出了异常  @Override  public Class findClass(String name)throws ClassNotFoundException{    byte[] data = this.loadClassData(name);    return this.defineClass(name,data,0,data.length);  }  private byte[] loadClassData(String name){    InputStream is = null;    byte[] data = null;    ByteArrayOutputStream baos = null;    try {      this.name = this.name.replace(".","\\"); //com.dream.it---->com\dream\it      is = new FileInputStream(new File(path + name + fileType));      int ch;      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 {    MyClassLoader loader1 = new MyClassLoader("loader1");    loader1.setPath("d:/myapp/serverlib/");    MyClassLoader loader2 = new MyClassLoader(loader1,"loader2"); //loader1作为loader2的父 类加载器    loader2.setPath("d:/myapp/clientlib");    MyClassLoader loader3 = new MyClassLoader(null,"loader3");//父类加载器为null,表明其父类加载器为根类加载器    loader3.setPath("d:/myapp/otherlib");    test(loader2);    test(loader3);  }  public static void test(ClassLoader cl) throws Exception {    Class clazz = cl.loadClass("Sample");    Object object = clazz.newInstance();  }}

附上findClass()方法的JDK说明

protected Class<?> findClass(String name) throws ClassNotFoundExceptionFinds the class with the specified binary name. This method should be overridden by class loaderimplementations that follow the delegation model for loading classes, and will be invoked by the loadClass method after checking the parent classloader for the requested class. The default implementation throws a ClassNotFoundException.

大致说明一下意思:通过指定的name来查找类。该方法应该被类加载器的实现类重写,从而能够保证在加载类的时候可以遵循委托机制模型。在loadClass()方法(该方法是由JVM调用的)中,检查其父类加载器之后,该方法再被调用去加载请求的类。默认该方法的实现是抛出了一个ClassNotFoundException异常。

其实,所谓的加载类,无非就是读取.class文件到内存中,所以在findClass()方法中,loadClassData()方法用于读取.class文件的数据,并返回一个字节数组。然后利用ClassLoader类的defineClass()方法将字节数组转换为Class对象。

上述自定义的类加载器loader1,loader2,loader3及JVM自带的类加载器之间的关系如下:

Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么

对于各个类加载器,系统的类加载器是从环境变量classpath中读取.class文件实现类的加载;loader1是从目录d:/myapp/serverlib/下读取.class文件;loader2是从目录d:/myapp/clientlib/下读取.class文件,loader3是从目录d:/myapp/otherlib/下读取.class文件

执行结果:

Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么

此处我们分析一下出现这种执行结果的原因:

当执行loader2.loadClass(“Sample”)时先由它上层的所有父类加载器尝试加载Sample类。

loader1从D:\myapp\serverliv目录下成功加载了Sample类,所以loader1是Sample类的定义类加载器,loader1和loader2是Sample类的初始类加载器。

当执行loader3.loadClass(“Sample”)时,先由它上层的所有父类加载器尝试加载Sample类。

loader3的父加载器为根类加载器,它无法加载Sample类,接着loader3从D:\myapp\otherlib目录下成功加载Sample类,所以loader3是Sample类的定义类加载器及初始类加载器。

在Sample类中主动使用了Dog类(new Dog()),当执行Sample类的构造方法中的new Dog()语句时,JVM需要先加载Dog类,到底用哪个类加载器家在呢?

从上述的打印结果中可以看出,加载Sample类的loader1还加载了Dog类,JVM会用Sample类的定义类加载器去加载Dog类,加载过程中也同样采用了父亲委托机制。

为了验证这一点,可以吧D:\myapp\serverlib目录下Dog.class文件删除,然后在D:\myapp\syslib目录下存放一个Dog.class文件,此时打印结果如下:

Sample:loader1Dog:sun.misc.Launcher$AppClassLoader@1b84c92Sample:loader3Dog:loader3

由此可见,当由loader1加载的Sample类首次主动使用Dog类时,Dog类由系统类加载器加载,如果把D:\myapp\serverlib和D:\myapp\syslib目录下的Dog.class文件都删除,然后在D:\myapp\client目录下存放一个Dog.class文件。

此时文件结构如下图所示:

Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么

当Loader1加载Sample类首次主动使用Dog类时,由于loader1及其父类加载器都无法加载Dog类,因此test(loader2)会抛出ClassNotFoundExcption.

这又是因为什么原因呢?

这又牵扯到命名空间的问题。

同一个命名空间内的类时相互可见的。

子加载器的命名空间包含所有父类加载器的命名空间,因此由子加载器加载的类能看见父类加载器加载的类。例如系统类加载器加载的类能看见根类加载器加载的类。由父加载器加载的类不能看见子加载器加载的类。

如果两个加载器之间没有直接或间接的父子关系,那么它们各自加载的类相互不可见。

对于上述问题,loader1可以加载Sample类,而Dog类只能由loader2加载Dog类,loader1是Loader2的父类加载器,父加载器loader1加载的类Sample不能看见子加载器loader2加载的类Dog,所以会抛出异常。

对于上述实例中的main方法,我们不调用test方法,换成如下代码

Class clazz = loader1.loadClass("Sample");Object obj = clazz.newInstance();Sample sample = (Sample)obj;System.out.println(sample.v1);

MyClassLoader类由系统类加载器加载,而Sample类由loader1类加载器加载,所以MyClassLoader类看不见Sample类。在MyClassLoader类的main方法中使用Sample类,会导致NoClassFoundError错误。

当两个不同命名空间内的类相互不可见时,可采用Java反射机制来访问对象实例的属性和方法。

将上述代码修改:

Class clazz = loader1.loadClass("Sample");Object obj = clazz.newInstance();Field field = clazz.getField("v1");int v1 = field.getInt(obj);System.out.println(v1);

此时,可以获取到对象中的v1属性值。利用反射机制,我们可以跨越这种命名空间的限制。

补充:

命名空间:

Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么

运行时包:

Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么

以上是“Java自定义类加载器及JVM自带的类加载器之间的交互关系是什么”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网行业资讯频道!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯