Java基础—ClassLoader的理解(转)

默认的三个类加载器

Java默认是有三个ClassLoader,按层次关系从上到下依次是:

  • Bootstrap ClassLoader
  • Ext ClassLoader
  • System ClassLoader

Bootstrap ClassLoader是最顶层的ClassLoader,它比较特殊,是用C++编写集成在JVM中的,是JVM启动的时候用来加载一些核心类的,比如:rt.jar,resources.jar,charsets.jar,jce.jar等,可以运行下面代码看都有哪些:

  1. URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
  2. for (int i = 0; i < urls.length; i++) {
  3. System.out.println(urls[i].toExternalForm());
  4. }
file:/C:/Program%20Files/Java/jre6/lib/resources.jar
file:/C:/Program%20Files/Java/jre6/lib/rt.jar
file:/C:/Program%20Files/Java/jre6/lib/sunrsasign.jar
file:/C:/Program%20Files/Java/jre6/lib/jsse.jar
file:/C:/Program%20Files/Java/jre6/lib/jce.jar
file:/C:/Program%20Files/Java/jre6/lib/charsets.jar
file:/C:/Program%20Files/Java/jre6/lib/modules/jdk.boot.jar
file:/C:/Program%20Files/Java/jre6/classes

其余两个ClassLoader都是继承自ClassLoader这个类。Java的类加载采用了一种叫做“双亲委托”的方式(稍后解释),所以除了Bootstrap ClassLoader其余的ClassLoader都有一个“父”类加载器, 不是通过集成,而是一种包含的关系。

  1. //ClassLoader.java
  2. public abstract class ClassLoader {
  3. ...
  4. // The parent class loader for delegation
  5. private ClassLoader parent;
  6. ...

“双亲委托”

所谓“双亲委托”就是当加载一个类的时候会先委托给父类加载器去加载,当父类加载器无法加载的时候再尝试自己去加载,所以整个类的加载是“自上而下”的,如果都没有加载到则抛出ClassNotFoundException异常。

上面提到Bootstrap ClassLoader是最顶层的类加载器,实际上Ext ClassLoader和System ClassLoader就是一开始被它加载的。

Ext ClassLoader称为扩展类加载器,负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目录下的所有的jar(包括自己手动放进去的jar包)。

System ClassLoader叫做系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件,包括我们平时运行jar包指定cp参数下的jar包。

运行下面的代码可以验证上面内容:

  1. ClassLoader loader = Debug.class.getClassLoader();
  2. while(loader != null) {
  3. System.out.println(loader);
  4. loader = loader.getParent();
  5. }
  6. System.out.println(loader);
[email protected]
[email protected]
null

“双亲委托”的作用

之所以采用“双亲委托”这种方式主要是为了安全性,避免用户自己编写的类动态替换Java的一些核心类,比如String,同时也避免了重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的class文件被不同的ClassLoader加载就是不同的两个类,如果相互转型的话会抛java.lang.ClassCaseException.

自定义类加载器

除了上面说的三种默认的类加载器,用户可以通过继承ClassLoader类来创建自定义的类加载器,之所以需要自定义类加载器是因为有时候我们需要通过一些特殊的途径创建类,比如网络。

至于自定义类加载器是如何发挥作用的,ClassLoader类的loadClass方法已经把算法定义了:

  1. protected synchronized Class<?> loadClass(String name, boolean resolve)
  2. throws ClassNotFoundException
  3. {
  4. // First, check if the class has already been loaded
  5. Class c = findLoadedClass(name);
  6. if (c == null) {
  7. try {
  8. if (parent != null) {
  9. c = parent.loadClass(name, false);
  10. } else {
  11. c = findBootstrapClassOrNull(name);
  12. }
  13. } catch (ClassNotFoundException e) {
  14. // ClassNotFoundException thrown if class not found
  15. // from the non-null parent class loader
  16. }
  17. if (c == null) {
  18. // If still not found, then invoke findClass in order
  19. // to find the class.
  20. c = findClass(name);
  21. }
  22. }
  23. if (resolve) {
  24. resolveClass(c);
  25. }
  26. return c;
  27. }

>1. Invoke findLoadedClass(String) to check if the class has already been loaded.

>2. Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.

>3. Invoke the findClass(String) method to find the class.

看上面的Javadoc可以知道,自定义的类加载器只要重载findClass就好了。

Context ClassLoader

首先Java中ClassLoader就上面提到的四种,Bootstrap ClassLoaderExt ClassLoaderSystem ClassLoader以及用户自定义的,所以Context ClassLoader并不是一种新的类加载器,肯定是这四种的一种。

首先关于类的加载补充一点就是如果类A是被一个加载器加载的,那么类A中引用的B也是由这个加载器加载的(如果B还没有被加载的话),通常情况下就是类B必须在类A的classpath下。

但是考虑多线程环境下不同的对象可能是由不同的ClassLoader加载的,那么当一个由ClassLoaderC加载的对象A从一个线程被传到另一个线程ThreadB中,而ThreadB是由ClassLoaderD加载的,这时候如果A想获取除了自己的classpath以外的资源的话,它就可以通过Thread.currentThread().getContextClassLoader()来获取线程上下文的ClassLoader了,一般就是ClassLoaderD了,可以通过Thread.currentThread().setContextClassLoader(ClassLoader)来显示的设置。

为什么要有Contex ClassLoader

之所以有Context ClassLoader是因为Java的这种“双亲委托”机制是有局限性的:

  • 举网上的一个例子:

> JNDI为例,JNDI的类是由bootstrap ClassLoader从rt.jar中间载入的,但是JNDI具体的核心驱动是由正式的实现提供的,并且通常会处于-cp参数之下(注:也就是默认的System ClassLoader管理),这就要求bootstartp ClassLoader去载入只有SystemClassLoader可见的类,正常的逻辑就没办法处理。怎么办呢?parent可以通过获得当前调用Thread的方法获得调用线程的>Context ClassLoder 来载入类。

  • 我上面提到的加载资源的例子。

Contex ClassLoader提供了一个突破这种机制的后门。

Context ClassLoader一般在一些框架代码中用的比较多,平时写代码的时候用类的ClassLoader就可以了。

参考链接

http://stackoverflow.com/questions/1771679/difference-between-threads-context-class-loader-and-normal-classloader

http://www.cnblogs.com/magialmoon/p/3952445.html

时间: 2024-10-17 06:07:17

Java基础—ClassLoader的理解(转)的相关文章

Java基础—ClassLoader的理解

##默认的三个类加载器 Java默认是有三个ClassLoader,按层次关系从上到下依次是: - Bootstarp ClassLoader - Ext ClassLoader - System ClassLoader Bootstrap ClassLoader是最顶层的ClassLoader,它比较特殊,是用C++编写集成在JVM中的,是JVM启动的时候用来加载一些核心类的,比如:`rt.jar`,`resources.jar`,`charsets.jar`,`jce.jar`等,可以运行下

Java 基础:认识&理解关键字 native 实战篇

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 泥瓦匠初次遇见 navicat 是在 java.lang.Object 源码中的一个hashCode方法: 1 public native int hashCode(); 为什么有个navicat呢?这是我所要学习的地方.所以今天泥瓦匠想要总结下navicat. 一.认识 native 即 JNI,Java Native Interface 凡是一种语言,都希望是纯.比如解决某一个方案都喜欢就单单这

Java 基础:认识&amp;理解关键字 native 实战篇

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 泥瓦匠初次遇见 navicat 是在 java.lang.Object 源码中的一个hashCode方法: ? 1 public native int hashCode(); 为什么有个navicat呢?这是我所要学习的地方.所以今天泥瓦匠想要总结下navicat. 一.认识 native 即 JNI,Java Native Interface 凡是一种语言,都希望是纯.比如解决某一个方案都喜欢就单

Java基础——ClassLoader

ClassLoader是用来处理类加载的类,它管理着具体类的运行时上下文. classloader是通过分层的关联方式来管理运行中使用的类,不同的classloader中管理的类是不相同的,或者即便两个类毫无二致(除了路径)也是不同的两个类,通俗的说就是,不同的类加载器加载的,即使是同一个类,也是不同的.所以,通过classloader的限制,我们可以建立不同的package路径以区别不同的类.那么也是因为有特定的classloader,我们可以实现具体模块的加载,而不影响jvm中其他类. 一.

黑马程序员——Java基础---面向对象之理解

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ------- 面向对象之理解 一:理解面向对象           1,什么是对象 对象就是实际生活中的事物,可以说一切事物都是对象.   如:桌子,椅子,电脑,电视机等. 对象的3个主要特征: a)对象行为:这个对象能做什么,既可以让这个对象完成什么

Java基础之详细理解回收机制

在以前从事C/C++开发的时候,内存的管理一直是需要被谨慎考虑的内容.在C语言中,我们使用库函数malloc()和free()两个库函数来实现从堆中分配内存与释放,而C++则使用操作符new和delete来实现内存的管理,对于这两个方式,后者是操作符而前者是库函数,后者能够被编译器处理而前者着重于对内部数据实现构造,在面向对象设计中,后者能更好的结合构造函数对自定义对象实现内存分配.但是,在接触了Java之后,我们在内存的管理上可以轻松许多,关键是Java实现了内存的自动管理模式,具体是怎么样的

Java基础学习之--理解Object类

看Java API的Object类, 一共11个方法.按使用的频度排名: toString() 这个方法最常用在打日志,定位代码问题. equals()和hashCode(), 这两个方法的使用经典例子是HashMap的源码 public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int h

Java基础-四大特性理解(抽象、封装、继承、多态)

抽象: 象就是有点模糊的意思,还没确定好的意思. 就比方要定义一个方法和类.但还没确定怎么去实现它的具体一点的子方法,那我就可以用抽象类或接口.具体怎么用,要做什么,我不用关心,由使用的人自己去定义去实现. 封装: 属性可用来描述同一类事物的特征,方法可描述一类事物可做的操作.封装就是把属于同一类事物的共性(包括属性与方法)归到一个类中,以方便使用. 概念: 封装也称为信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,

Java基础12:深入理解Class类和Object类

Java基础12:深入理解Class类和Object类 Java中Class类及用法 Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,即所谓的RTTI. 这项信息纪录了每个对象所属的类.虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类.Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建. 说白了就是: Class类也是类的一种,只是名字和class关键字高度相似.Java是大小写敏感的语言.