Java类加载机制
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
在 Sun 的 java 指南中,文章“理解扩展类加载”(Understanding Extension Class Loading) 对以上三个类加载器路径有更详尽的解释,这是其他几个 JDK 中的类加载器
java.net.URLClassLoader java.security.SecureClassLoader java.rmi.server.RMIClassLoader sun.applet.AppletClassLoader
的字段 class,这个字段表示的就是一个 java.lang.Class 型的实例。因为它是 public 类型的, 我们可以通过标识符来访问它,像这样:
java.lang.Class klass = Myclass.class;
只要一个类被加载到 JVM,相同的类(强调:相同的类)将不会被重复加载。这将产 生一个问题,什么才是相同的类?一个对象有一种特定状态和标识,对象总是与它所属类 联系在一起,与这种状况相似,一个被加载到 JVM 中类也有特定的标识,接下来我们就阐 述:
} catch (ClassNotFoundException e) {
// If still not found, then invoke // findClass to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
这篇文章将从最基本的开始,比如代码和数据的关系,以及他们怎么样关系起来形成一 个实例或者对象。然后将会说到,java 中怎样通过类加载器把代码加载到 JVM 中,以及 java 中实现的主要的几种类型的类加载器。在这篇文章中,然后我们将会了解到 java 类加载机 制的内幕,我们将使用最基本的代码来描述,这些代码执行于类加载器之后,但在加载一 个类之前。在接下来的部分将使用一些例子来证实,对于开发者继承和开发自己的类加载 器的必要性。接着将告诉你们怎样编写自己的类加载器,以及怎样使用它们去创建一个一 般的能加载包括远程客户端辅助代码的类加载器引擎,以及怎样把它在 JVM 中定义,实例 化,然后执行。习惯上,把 J2EEspecific components 中说明的作为 java 类加载的规范,这 篇文章正是从这本手册总结来的。
} }
或者 public class MyClassLoader extends ClassLoader{
public MyClassLoader(){ super(getClass().getClassLoader());
} }
第一种方法更为常用,因为在构造方法中使用 getClass() 方法是不提倡的,因为对象初 始化仅在构造方法结束后才会完成。因此,如果正确设置了类加载器的父类加载器,不论 什么时候从类加载器实例请求一个类时,如果此类加载器不能加载此类,则首先会交给它 的父类加载器处理。如果此父类加载器也不能加载那个类,则又会交给上一层的父类加载 器,依此类推。但是如果 findBootstrapClass0()方法也未能加载那个类时,就会唤醒 findClass() 去处理。findClass()的默认的实现是抛 ClassNotFoundException 异常。所以开发者需要继承 java.lang.ClassLoader 来实现用户自编写的类加载器。findClass()默认的实现如下:
在 java 中,一个类通过认证的类全名来唯一标识。认证的类全名是由包名和类名两部 分组成。但是在一个类被加载到 JVM 中则是通过认证的类全名,还有加载这个类的加载器 来唯一标识。因此,一个类的类名为 C1,包名为 Pg,被类加载器类 KClassLoader 的一个 实例 k1 加载,则 C1,也就是 C1.class ,的类实例,在 JVM 中将被解释为(C1,Pg,k1)。 这就意味着两个不同的类加载器实(Cl, Pg, kl1) 和 (Cl, Pg, kl2) ,加载的类在 JVM 中将有不 同的类实例对象,不是类型可比型(typecompatible)的。在 JVM 中有多少个类加载器实 例呢?下面,我们将讲解这个。
protected Class<?> findClass(String name)
throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}Βιβλιοθήκη Baidu
深入 findClass()方法,类加载器需要从其它资源获取字节码。这些资源可以是文件系统、网 络 URL、数据库、其它的可以把字节码转换为流的应用程序,或者能够产生与 java 特性相 适应的字节码的类似资源。你可以使用BCEL (Byte Code Engineering Library),它能非常方便 的根据运行时的迹象创建类 。BCEL 已经成功的应用在了一些地方,如编译器、优化程序、 模糊程序(obsfuscators)、代码生成器、分析工具。只要这个这些字节码被重新获取,findClass()
图 1 展 示 了 一 个 带 有 main 方 法 的 应 用 程 序 类 MyMainClass 。 正 如 前 面 所 说 的 , MyMainClass.class 将被 AppClassLoader 加载 ,MyMainClass 创建两个加载器类实例, CustomClassLoader1 和 CustomClassLoader2,他们都能从某些资源(比如说:网络)中加载 第四个类 Target 的字节码。这就意味着 Target 这个类的定义超出了应用程序的 class path 或 者扩展 class path 范围。在这种 情况下,如果 MyMainClass 让客户加载器实例去加载 Target 类。Target 将同时被 CustomClassLoader1 和 CustomClassLoader2 加载和定义。在 java 中这样就会有严重的问题。如果在 Target 中包含一段静态(static)的初始化代码,如果我 们要求这段代码执行且仅贝被执行一次,在我们目前的情况下,这段代码将被执行两次。 在两个 CustomClassLoader 中,都执行了一次。如果 Target 被两个 CustomClassLoader 同时 初始化,他将会有两个实例 target1 和 target2 ,正如下面图 1 所示的,target1 和 target2 是不 可比的。也就是说,在 java 中不能执行这段代码:
// First check if the class is already loaded Class c = findLoadedClass(name); if (c == null) {
try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClass0(name); }
类和数据
一个类代表一段要执行的代码,然而数据则代表与这些代码相关联的某种状态。状态可 以改变,代码不能改变。我们把一种特定状态与一个类关联起来时,就得到了这个类的一 个实例。所以同一个类的不同实例有不同的状态,但都参照相同的代码。在 java 中,一个 类通常它的代码就包含在一个 .class 文件中,虽然其中也包括异常。然而,在 java 运行时, 每个类都会构造一个超类对象(firstclass object),它们其实是 java.lang.Class 的实例。不 论何时编译一个 java 文件,编译器都会在编译后的字节码中嵌入一个 public, static, final 型
设置父类加载器,我们有两种方法,在 ClassLoader 的构造方法中。 public class MyClassLoader extends ClassLoader{
public MyClassLoader(){ super(MyClassLoader.class.getClassLoader());
Java 类加载机制(一)
译:ayi 译文疏漏,请多多指点
原文:http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.html
注:因内容太多,分为一、二两篇文章
类加载是 java 特性的一个很重要的部分。尽管,java 中“advanced topics”的发展,使 java 的类加载机制地位有所下降。但每位编程者都应该知道这部分的工作机制,以及怎样去配 合其工作。这可以使我们节省很多时间,而不必要浪费在调试ClassNotFoundException, ClassCastException, 等。
载器的正常工作来说都非常重要。在这里,最重要的是怎样正确的设置父类加载器。类加 载器的父类加载器实例会负责加载此类加载器类。(记住:一个类加载器本身也是一个类。) 在一个类加载器外部请求一个类时,使用 loadClass() 方法。这个方法的具体工作,我们可 以从源代码来看:
protected synchronized Class<?> loadClass (String name, boolean resolve) throws ClassNotFoundException{
java.lang.Thread,包含了 public ClassLoader getContextClassLoader()方法,这一方法返回针 对一具体线程的上下文环境类加载器。上下文加载器是线程创建着提供的,用以来加载线 程运行时需要的类和资源。如果没有设定,默认的是父线程的上下文类加载器。最原始的 上下文类加载器由加载 application 应用程序的类加载器建立。 类加载器怎样工作 所有的类加载器,除了引导类加载器,都一个父类加载器。而且,它们都是 java.lang.ClassLoader 类型的。上面两句话是不同的,而且对与开发者开发的任何一个类加
比如,如果我们试图得到一个核心 java 运行时类的一个类加载器,我们将得到 null 值,如 下: log(java.lang.String.class.getClassLoader()); 下面要说到的是 java 扩展类加载器。在 java.ext.dirs 路径下面,我们可以放 java 扩展类库, 这样我们可以获得超出 java 核心运行时类的特性。扩展类加载器(ExtClassLoader)将会加 载 java.ext.dirs 目录下的所有 .jar 文件。开发者可以为自己的应用增加新的 .jar 文件 或者 类 库,只要他把它们添加到 java.ext.dirs 目录下面以至于能被扩展类加载器找到。
方法将会调用 defineClass()方法,而且这时运行时(runtime )环境是非常特殊的对于具体 哪一个类加载器实例去调用 defineClass()这个方法。所有,两个类加载器实例从两个相同的、 或者不同的资源加载字节码时,这些加载的类都是不同的。
在 java 语 言 详 解 ( Java language specification ) 中 , 给 出 了 在 java 执 行 引 擎 (Java Execution Engine)中加载、链接、初始化的类和接口等过程的详细解释。
类加载器
在 java 中,每个类都会被 java.lang.ClassLoader 的一个实例加载。ClassLoader 类处于 java.lang
包下面,开发者可以自由的创建它的子类,添加自己功能的类加载器。
每当敲入 java MyMainClass,一个新的 JVM 开始时,引导类加载器(bootstrap class loader ) 首先会把 java 中的一些关键类,像 java.lang.Objent,和运行时的代码载入内存。这些运行 时类打包在 JRE\lib\rt.jar 文件中。因为是一个本地的接口,我们并不能从 java 文档中得到 引 导 类 加 载 器 ( bootstrap class loader ) 信 息 。 也 正 是 这 个 原 因 , 引 导 类 加 载 器 (bootstrap class loader )的表现也根据 JVM 的不同而异。
java.net.URLClassLoader java.security.SecureClassLoader java.rmi.server.RMIClassLoader sun.applet.AppletClassLoader
的字段 class,这个字段表示的就是一个 java.lang.Class 型的实例。因为它是 public 类型的, 我们可以通过标识符来访问它,像这样:
java.lang.Class klass = Myclass.class;
只要一个类被加载到 JVM,相同的类(强调:相同的类)将不会被重复加载。这将产 生一个问题,什么才是相同的类?一个对象有一种特定状态和标识,对象总是与它所属类 联系在一起,与这种状况相似,一个被加载到 JVM 中类也有特定的标识,接下来我们就阐 述:
} catch (ClassNotFoundException e) {
// If still not found, then invoke // findClass to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
这篇文章将从最基本的开始,比如代码和数据的关系,以及他们怎么样关系起来形成一 个实例或者对象。然后将会说到,java 中怎样通过类加载器把代码加载到 JVM 中,以及 java 中实现的主要的几种类型的类加载器。在这篇文章中,然后我们将会了解到 java 类加载机 制的内幕,我们将使用最基本的代码来描述,这些代码执行于类加载器之后,但在加载一 个类之前。在接下来的部分将使用一些例子来证实,对于开发者继承和开发自己的类加载 器的必要性。接着将告诉你们怎样编写自己的类加载器,以及怎样使用它们去创建一个一 般的能加载包括远程客户端辅助代码的类加载器引擎,以及怎样把它在 JVM 中定义,实例 化,然后执行。习惯上,把 J2EEspecific components 中说明的作为 java 类加载的规范,这 篇文章正是从这本手册总结来的。
} }
或者 public class MyClassLoader extends ClassLoader{
public MyClassLoader(){ super(getClass().getClassLoader());
} }
第一种方法更为常用,因为在构造方法中使用 getClass() 方法是不提倡的,因为对象初 始化仅在构造方法结束后才会完成。因此,如果正确设置了类加载器的父类加载器,不论 什么时候从类加载器实例请求一个类时,如果此类加载器不能加载此类,则首先会交给它 的父类加载器处理。如果此父类加载器也不能加载那个类,则又会交给上一层的父类加载 器,依此类推。但是如果 findBootstrapClass0()方法也未能加载那个类时,就会唤醒 findClass() 去处理。findClass()的默认的实现是抛 ClassNotFoundException 异常。所以开发者需要继承 java.lang.ClassLoader 来实现用户自编写的类加载器。findClass()默认的实现如下:
在 java 中,一个类通过认证的类全名来唯一标识。认证的类全名是由包名和类名两部 分组成。但是在一个类被加载到 JVM 中则是通过认证的类全名,还有加载这个类的加载器 来唯一标识。因此,一个类的类名为 C1,包名为 Pg,被类加载器类 KClassLoader 的一个 实例 k1 加载,则 C1,也就是 C1.class ,的类实例,在 JVM 中将被解释为(C1,Pg,k1)。 这就意味着两个不同的类加载器实(Cl, Pg, kl1) 和 (Cl, Pg, kl2) ,加载的类在 JVM 中将有不 同的类实例对象,不是类型可比型(typecompatible)的。在 JVM 中有多少个类加载器实 例呢?下面,我们将讲解这个。
protected Class<?> findClass(String name)
throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}Βιβλιοθήκη Baidu
深入 findClass()方法,类加载器需要从其它资源获取字节码。这些资源可以是文件系统、网 络 URL、数据库、其它的可以把字节码转换为流的应用程序,或者能够产生与 java 特性相 适应的字节码的类似资源。你可以使用BCEL (Byte Code Engineering Library),它能非常方便 的根据运行时的迹象创建类 。BCEL 已经成功的应用在了一些地方,如编译器、优化程序、 模糊程序(obsfuscators)、代码生成器、分析工具。只要这个这些字节码被重新获取,findClass()
图 1 展 示 了 一 个 带 有 main 方 法 的 应 用 程 序 类 MyMainClass 。 正 如 前 面 所 说 的 , MyMainClass.class 将被 AppClassLoader 加载 ,MyMainClass 创建两个加载器类实例, CustomClassLoader1 和 CustomClassLoader2,他们都能从某些资源(比如说:网络)中加载 第四个类 Target 的字节码。这就意味着 Target 这个类的定义超出了应用程序的 class path 或 者扩展 class path 范围。在这种 情况下,如果 MyMainClass 让客户加载器实例去加载 Target 类。Target 将同时被 CustomClassLoader1 和 CustomClassLoader2 加载和定义。在 java 中这样就会有严重的问题。如果在 Target 中包含一段静态(static)的初始化代码,如果我 们要求这段代码执行且仅贝被执行一次,在我们目前的情况下,这段代码将被执行两次。 在两个 CustomClassLoader 中,都执行了一次。如果 Target 被两个 CustomClassLoader 同时 初始化,他将会有两个实例 target1 和 target2 ,正如下面图 1 所示的,target1 和 target2 是不 可比的。也就是说,在 java 中不能执行这段代码:
// First check if the class is already loaded Class c = findLoadedClass(name); if (c == null) {
try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClass0(name); }
类和数据
一个类代表一段要执行的代码,然而数据则代表与这些代码相关联的某种状态。状态可 以改变,代码不能改变。我们把一种特定状态与一个类关联起来时,就得到了这个类的一 个实例。所以同一个类的不同实例有不同的状态,但都参照相同的代码。在 java 中,一个 类通常它的代码就包含在一个 .class 文件中,虽然其中也包括异常。然而,在 java 运行时, 每个类都会构造一个超类对象(firstclass object),它们其实是 java.lang.Class 的实例。不 论何时编译一个 java 文件,编译器都会在编译后的字节码中嵌入一个 public, static, final 型
设置父类加载器,我们有两种方法,在 ClassLoader 的构造方法中。 public class MyClassLoader extends ClassLoader{
public MyClassLoader(){ super(MyClassLoader.class.getClassLoader());
Java 类加载机制(一)
译:ayi 译文疏漏,请多多指点
原文:http://www.onjava.com/pub/a/onjava/2005/01/26/classloading.html
注:因内容太多,分为一、二两篇文章
类加载是 java 特性的一个很重要的部分。尽管,java 中“advanced topics”的发展,使 java 的类加载机制地位有所下降。但每位编程者都应该知道这部分的工作机制,以及怎样去配 合其工作。这可以使我们节省很多时间,而不必要浪费在调试ClassNotFoundException, ClassCastException, 等。
载器的正常工作来说都非常重要。在这里,最重要的是怎样正确的设置父类加载器。类加 载器的父类加载器实例会负责加载此类加载器类。(记住:一个类加载器本身也是一个类。) 在一个类加载器外部请求一个类时,使用 loadClass() 方法。这个方法的具体工作,我们可 以从源代码来看:
protected synchronized Class<?> loadClass (String name, boolean resolve) throws ClassNotFoundException{
java.lang.Thread,包含了 public ClassLoader getContextClassLoader()方法,这一方法返回针 对一具体线程的上下文环境类加载器。上下文加载器是线程创建着提供的,用以来加载线 程运行时需要的类和资源。如果没有设定,默认的是父线程的上下文类加载器。最原始的 上下文类加载器由加载 application 应用程序的类加载器建立。 类加载器怎样工作 所有的类加载器,除了引导类加载器,都一个父类加载器。而且,它们都是 java.lang.ClassLoader 类型的。上面两句话是不同的,而且对与开发者开发的任何一个类加
比如,如果我们试图得到一个核心 java 运行时类的一个类加载器,我们将得到 null 值,如 下: log(java.lang.String.class.getClassLoader()); 下面要说到的是 java 扩展类加载器。在 java.ext.dirs 路径下面,我们可以放 java 扩展类库, 这样我们可以获得超出 java 核心运行时类的特性。扩展类加载器(ExtClassLoader)将会加 载 java.ext.dirs 目录下的所有 .jar 文件。开发者可以为自己的应用增加新的 .jar 文件 或者 类 库,只要他把它们添加到 java.ext.dirs 目录下面以至于能被扩展类加载器找到。
方法将会调用 defineClass()方法,而且这时运行时(runtime )环境是非常特殊的对于具体 哪一个类加载器实例去调用 defineClass()这个方法。所有,两个类加载器实例从两个相同的、 或者不同的资源加载字节码时,这些加载的类都是不同的。
在 java 语 言 详 解 ( Java language specification ) 中 , 给 出 了 在 java 执 行 引 擎 (Java Execution Engine)中加载、链接、初始化的类和接口等过程的详细解释。
类加载器
在 java 中,每个类都会被 java.lang.ClassLoader 的一个实例加载。ClassLoader 类处于 java.lang
包下面,开发者可以自由的创建它的子类,添加自己功能的类加载器。
每当敲入 java MyMainClass,一个新的 JVM 开始时,引导类加载器(bootstrap class loader ) 首先会把 java 中的一些关键类,像 java.lang.Objent,和运行时的代码载入内存。这些运行 时类打包在 JRE\lib\rt.jar 文件中。因为是一个本地的接口,我们并不能从 java 文档中得到 引 导 类 加 载 器 ( bootstrap class loader ) 信 息 。 也 正 是 这 个 原 因 , 引 导 类 加 载 器 (bootstrap class loader )的表现也根据 JVM 的不同而异。