Java中关于类加载器的代码详解

Java中关于类加载器的代码详解

这篇文章主要为大家详细介绍了中类加载器的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

从java的动态性到类加载机制

Java是一种动态语言。那么怎样理解这个“动态”呢?或者说一门语言具备了什么特性,才能称之为动态语言呢?对于java,我是这样理解的。

JVM(java虚拟机)执行的不是本地机器码指令,而是执行一种称之为字节码的指令(存在于class文件中)。这就要求虚拟机在真正执行字节码之前,先把相关的class文件加载到内存中。虚拟机不是一次性加载所有需要的class文件,因为它在执行的时候根本不会知道以后会用到哪些class文件。它是每用到一个类,就会在运行时“动态地”加载和这个类相关的class文件。这就是java被称之为动态性语言的根本原因。除了动态加载类之外,还会动态的初始化类,对类进行动态链接。动态初始化和动态链接放在其他文章中进行介绍。本文中只关心类的加载。

在JVM中负责对类进行加载的正是本文要介绍的类加载器(),所以,类加载器是JVM不可或缺的重要组件。

java中的类加载器及类加载器工作原理

java中(指的是)有三种类加载器。每个类加载器在创建的时候已经指定他们对应的目录, 也就是说每个类加载器去哪里加载类是确定的,我认为在类中应该会有()之类的方法, 得到他们对应的路径,找了找jdk的文档,发现是没有的。以下是这三种类加载器和他们对应的路径:

*   —   加载指定的路径中的类

*    —   加载jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下的类

*            —   加载JRE/lib/rt.jar中的类

那么类加载器是如何工作的呢?可以参看jdk中类的源码。这个类的实现使用了模板方法模式,首先是方法来加载类,方法又调用了方法,该方法读取并返回类文件的数据,方法返回后,方法继续调用方法,将返回的数据加工成虚拟机运行时可识别的类型信息。所以,我们如果开发自己的类加载器,只需要继承jdk中的类,并覆盖方法就可以了,剩下的而工作,父类会完成。其他java平台有的根据自己的需求Java中关于类加载器的代码详解,实现了自己特定的类加载器,例如平台中的服务器,平台中的虚拟机也定义了自己的类加载器。

虚拟机加载类有两种方式,一种方式就是上面提到的.()方法,另一种是使用反射API,Class.()方法,其实Class.()方法内部也是使用的。Class类中方法的实现如下:

public static Class> forName(String name, boolean initialize,
  ClassLoader loader)
  throws ClassNotFoundException
 {
 if (loader == null) {
  SecurityManager sm = System.getSecurityManager();
  if (sm != null) {
 ClassLoader ccl = ClassLoader.getCallerClassLoader();
 if (ccl != null) {
  sm.checkPermission(
 SecurityConstants.GET_CLASSLOADER_PERMISSION);
 }
  }
 }
 return forName0(name, initialize, loader);
 }
 /** Called after security checks have been made. */
 private static native Class forName0(String name, boolean initialize,
   ClassLoader loader)
 throws ClassNotFoundException;

类加载器的三个特性

类加载器有三个特性,分别为委派,可见性和单一性,其他文章上对这三个特性的介绍如下:

* 委托机制是指将加载一个类的请求交给父类加载器,如果这个父类加载器不能够找到或者加载这个类,那么再加载它。

java 类加载器

* 可见性的原理是子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。

* 单一性原理是指仅加载一个类一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。

其中,委派机制是基础,在其他资料中也把这种机制叫做类加载器的双亲委派模型,其实说的是同一个意思。可加性和单一性是依赖于委派机制的。

以下代码测试类加载器的委派机制:

 ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
 System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
 
 
 ClassLoader extClassLoader = appClassLoader.getParent();
 System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@addbf1
 //AppClassLoader的父加载器是ExtClassLoader
 
 System.out.println(extClassLoader.getParent()); //null
 //ExtClassLoader的父加载器是null, 也就是BootStrap,这是由c语言实现的

由打印结果可知,加载我们自己编写的类的加载器是,的父加载器是,在而的父加载器返回结果为null,这说明他的附加载器是,这个加载器是和虚拟机紧密联系在一起的,在虚拟机启动时,就会加载jdk中的类。它是由C实现的,没有对应的java对象,所以返回null。但是在逻辑上java 类加载器,仍是的父加载器。也就是说每当加载一个类时,总会委托给加载。

系统类加载器和线程上下文类加载器

java 类加载器

爱克网络企业网站建设系统 No.

系统特点:功能简洁实用。目前互联网上最简洁的企业网站建设系统!原创程序代码。非网络一般下载后修改的代码。更安全。速度快!界面模版分离。原创的分离思路,完全不同于其他方式,不一样的简单感受!搜索引擎优化。做了基础的seo优化。对搜索引擎更友好系统功能关于我们:介绍企业介绍类信息,可自由添加多个介绍栏目!资讯中心:公司或行业资讯类内容展示。可自由添加多个资讯内容!产品展示:支持类别设置,可添加产品图片

下载

在java中,还存在两个概念,分别是系统类加载器和线程上下文类加载器。

其实系统类加载器就是应用程序类加载器,它两个值得是同一个加载器,以下代码可以验证:

 ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
 System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
 
 ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
 System.out.println(sysClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
 //由上面的验证可知, 应用程序类加载器和系统类加载器是相同的, 因为地址是一样的

这两个类加载器对应的输出,不仅类名相同,连对象的哈希值都是一样的,这充分说明系统类加载器和应用程序类加载器不仅是同一个类,更是同一个类的同一个对象。

每个线程都会有一个上下文类加载器,由于在线程执行时加载用到的类,默认情况下是父线程的上下文类加载器, 也就是。

 new Thread(new Runnable() {
 
 @Override
 public void run() {
 ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
 System.out.println(threadcontextClassLosder); //sun.misc.Launcher$AppClassLoader@19821f
 }
 }).start();

java 类加载器

这个子线程在执行时打印的信息为sun.misc.$@,可以看到和主线程中的是同一个对象(哈希值相同)。

也可以为线程设置特定的类加载器,这样的话java 类加载器,线程在执行时就会使用这个特定的类加载器来加载使用到的类。如下代码:

 Thread th = new Thread(new Runnable() {
 
 @Override
 public void run() {
 ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
 System.out.println(threadcontextClassLosder); //jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74
 }
 });
 
 th.setContextClassLoader(new ClassLoader() {});
 
 th.start();

在线程运行之前Java中关于类加载器的代码详解,为它设置了一个匿名内部类的类加载器对象,线程运行时,输出的信息为:jg.zhang.java..$3@,也就是我们设置的那个类加载器对象。

类加载器的可见性

下面验证类加载器的可见性,也就是 子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。

以下代码使用父加载器加载子加载器路径下的类,由输出可知,是不可能实现的。

 try {
 Class.forName("jg.zhang.java.testConcurrent.Person", true, 
  ClassLoaderTest.class.getClassLoader().getParent());
 System.out.println("1 -- 类被加载");
 } catch (ClassNotFoundException e) {
 //e.printStackTrace();
 System.out.println("1 -- 未找到类");
 }

输出为 :1 — 未找到类 。说明抛出了on异常。原因是让加载 jg.zhang.java..这个类因为这个类不在jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下,所以抛出on。所以父加载器不能加载应该被子加载器加载的类。也就是说这个类在父加载器中不可见。这种机制依赖于委派机制。

下面代码使用子加载器 加载父加载器中的类,这是可以实现的。

 try {
 Class.forName("java.lang.String", true, 
  ClassLoaderTest.class.getClassLoader());
 System.out.println("2 -- 类被加载");
 } catch (ClassNotFoundException e) {
 //e.printStackTrace();
 System.out.println("2 -- 未找到类");
 }

输出为:2 — 类被加载。说明成功加载了类。是因为在指定由加载类时,由一直委派到加载。虽然是由子加载器的父加载器加载的,但是也可以说,父加载器加载的类对于子加载器来说是可见的。这同样依赖于委派机制。其实在虚拟机启动初期java 类加载器,java.lang.已经被预加载了,这时再次加载,虚拟机发现已经加载,不会再重复加载。这同时也证明了类加载器的单一性。

测试代码

到此为止,类加载器的知识就全部讲完了。以下是整个测试代码:

package jg.zhang.java.testclassloader;
/**
 * 参考ImportNew上的一篇文章>,
 * 文章地址:http://www.importnew.com/6581.html
 * 
 * Java类加载器基于三个机制:委托、可见性和单一性。
 * 委托机制是指将加载一个类的请求交给父类加载器,如果这个父类加载器不能够找到或者加载这个类,那么再加载它。
 * 可见性的原理是子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类加载器加载的类。
 * 单一性原理是指仅加载一个类一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类。
 * 
 * 三种类加载器: 每个类加载器在创建的时候已经指定他们对应的目录, 也就是说每个类加载器去哪里加载类是确定的
 * 我认为在ClassLoader类中应该会有getTargetPath()之类的方法, 得到他们对应的路径,找了找jdk的文档,发现是没有的.
 * AppClassLoader -- 加载classpath指定的路径中的类
 * ExtClassLoader -- 加载jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下的类
 * BootStrap  -- 加载JRE/lib/rt.jar中的类
 * 
 * 
 * 
 * @author zhangjg
 *
 */
public class ClassLoaderTest {
 
 public static void main(String[] args) {
 test1();
 test2();
 test3();
 }
 /**
 * 验证线程上下文类加载器
 */
 private static void test3() {
 /**
 * 1 每个线程都会有一个上下文类加载器,由于在线程执行时加载用到的类,默认情况下是父线程
 * 的上下文类加载器, 也就是AppClassLoader
 */
 new Thread(new Runnable() {
 
 @Override
 public void run() {
 ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
 System.out.println(threadcontextClassLosder); //sun.misc.Launcher$AppClassLoader@19821f
 }
 }).start();
 
 /**
 * 2 也可以给创建的线程设定特定的上下文类加载器
 */
 Thread th = new Thread(new Runnable() {
 
 @Override
 public void run() {
 ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
 System.out.println(threadcontextClassLosder); //jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74
 }
 });
 
 th.setContextClassLoader(new ClassLoader() {});
 
 th.start();
 }
 /**
 * 测试可见性,可见性依赖于委托机制
 */
 private static void test2() {
 
 /**
 * 1 让ExtClassLoader加载 jg.zhang.java.testConcurrent.Person这个类
 * 因为这个类不在jre/lib/ext目录下或者java.ext.dirs系统属性定义的目录下
 * 所以抛出ClassNotFoundException
 * 
 * 所以父加载器不能加载应该被子加载器加载的类,这个类在父加载器中不可见
 * 这种机制依赖于委派机制
 */
 
 try {
 Class.forName("jg.zhang.java.testConcurrent.Person", true, 
  ClassLoaderTest.class.getClassLoader().getParent());
 System.out.println("1 -- 类被加载");
 } catch (ClassNotFoundException e) {
 //e.printStackTrace();
 System.out.println("1 -- 未找到类");
 }
 
 
 /**
 * 2 让AppClassLoader加载java.lang.String类
 * 没有抛出异常,说明类被正常加载了
 * 虽然是由AppClassLoader一直委派到BootStrap而加载的
 * 所以可以说,父加载器加载的类对于子加载器来说是可见的,这同样依赖于委派机制
 * 
 * 其实在虚拟机启动初期,java.lang.String已经被BootStrap预加载了
 * 这时再次加载,虚拟机发现已经加载,不会再重复加载
 */
 try {
 Class.forName("java.lang.String", true, 
  ClassLoaderTest.class.getClassLoader());
 System.out.println("2 -- 类被加载");
 } catch (ClassNotFoundException e) {
 //e.printStackTrace();
 System.out.println("2 -- 未找到类");
 }
 
 }
 /**
 * 验证三种类加载器的父子关系
 */
 private static void test1() {
 ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
 System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
 
 ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
 System.out.println(sysClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
 //由上面的验证可知, 应用程序类加载器和系统类加载器是相同的, 因为地址是一样的
 
 ClassLoader extClassLoader = appClassLoader.getParent();
 System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@addbf1
 //AppClassLoader的父加载器是ExtClassLoader
 
 System.out.println(extClassLoader.getParent()); //null
 //ExtClassLoader的父加载器是null, 也就是BootStrap,这是由c语言实现的
 }
}

Java免费学习笔记:立即使用

解锁 Java 大师之旅:从入门到精通的终极指南

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » Java中关于类加载器的代码详解

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们