Java类加载器工作原理

Java类加载器是用来在运行时加载类(*.class文件)。Java类加载器基于三个原则:委托、可见性、唯一性。委托原则把加载类的请求转发给父 类加载器,而且仅加载类当父 类加载器无法找到或者不能加载类时。可见性原则允许子类加载器查看由父类加载器加载的所有的类,但是父类加载器不能查看由子类加载器加载的类。唯一性原则只允许加载一次类文件,这基本上是通过委托原则来实现的并确保子类加载器不重新加载由父类加载器加载过的类。正确的理解类加载器原理必须解决像 NoClassDefFoundError in Java、 java.lang.ClassNotFoundException(这些现象和加载类有关系)的问题。类加载器在高级Java面试中也是一个重要的课题,在此场合中对类加载器和类路径的工作原理有一定的了解是Java程序员所预期的。我在各种Java面试中经常遇见这样的问题:在Java中一个类可以被两个不同的类加载器加载吗?在本文中,我们将学习什么是Java中的类加载器、它的工作原理以及与它有关的一些细节。

Java的类加载器是什么

Java中的类加载器是加载Java类文件(*.class)的一个类。Java代码被javac 编译器编译后以字节码的形式保存到类文件,JVM(Java虚拟机)通过操作类文件里的字节码来执行Java程序。类加载器负责从文件系统、网络或任何其它资源中加载类文件。

Java中使用的默认类加载器有以下三种:Bootstrap , Extension以及System or Application class loader。每个类加载器都有一个预定义的位置,它们在那里加载类文件。Bootstrap 类加载器负责从rt.jar中加载标准JDK类文件,并且它是Java中所有类加载器的父级。Bootstrap类加载器没有任何父级,如果你调用String.class.getClassLoader() 则返回null 而且与此相关的代码则抛出空指针异常。Bootstrap类加载器在java中也被称为Primordial ClassLoader(原生类加载器) 。Extension类加载器委托它的父级Bootstrap类加载器来加载类文件,如果委托失败,则从指向java.ext.dirs 系统属性的jre/lib/ext目录或者任何其它目录加载类文件。JVM中的Extension类加载器被sun.misc.Launcher$ExtClassLoader实现。

JVM 使用的第三种默认类加载器就是System or Applicationclass loader,它主要负责从CLASSPATH环境变量中加载应用程序特定的类文件,-classpath or -cp 命令行选项, JAR内部清单文件的类路径属性。Application类加载器是Extension类加载器的子级,它由sun.misc.Launcher$AppClassLoader类实现。而且,除Bootstrap 类加载器(大都由C 语言实现)以外,所有的Java类加载器使用java.lang.ClassLoader 来实现。

简而言之,这三种类加载器加载类文件的路径如下:

1) Bootstrap ClassLoader - JRE/lib/rt.jar

2) Extension ClassLoader - JRE/lib/ext 或者任何指向java.ext.dirs的路径

3) Application ClassLoader - CLASSPATH环境变量、-classpath or -cp 命令行选项,

JAR内部清单文件的类路径属性

正如我前面所描述的那样,java类加载器基于三个原则:委托、可见性、唯一性。在本节中我们将看到这些原则的细节之处以及通过示例来理解java类加载器工作原理。顺便说一句,这里有一个关系图,它解释了在java中通过使用委托类加载器如何加载类文件。

委托原则

在探讨java中的类文件在什么时候被加载和初始化的问题时,java中的类文件在它被需要的时候被加载。假设您有一个叫做Abc.class 的应用程序特定的类文件,加载这个类文件的第一个请求发送到 Application类加载器。Application类加载器委托它的父级Extension类加载器,而Extension类加载器委托给Bootstrap类加载器。Bootstrap类加载器在rt.jar 中寻找类文件,由于没有找到,请求转发到Extension类加载器。Extension类加载器在jre/lib/ext目录中寻找类文件并尝试加载类文件,如果找到了类文件,这时Extension类加载器将会加载类文件(Application类加载器将永远不会加载类文件);但是如果Extension类加载器没有加载类文件,那么Application类加载器将会从java类路径中加载类文件。请注意:类路径用于加载类文件,而路径用来查找可执行的像javac or java 的命令。

可见性原则

根据可见性原则,子级类加载器可以看到父级类加载器加载的类文件,但是反过来则行不通。这意味着,假如Application类加载器加载了Abc.class 文件,那么尝试使用Extension类加载器去加载Abc.class 文件,则会抛出异常java.lang.ClassNotFoundException。请看下面的示例:

packagetest;

importjava.util.logging.Level;
importjava.util.logging.Logger;

/**
* Java program to demonstrate How ClassLoader works in Java,

* in particular about visibility principle of ClassLoader.

*
* @author Javin Paul
*/

publicclass ClassLoaderTest {

publicstaticvoid main(String args[]){
try{
//printing ClassLoader of this class
System.out.println("ClassLoaderTest.getClass().getClassLoader() : "
+ ClassLoaderTest.class.getClassLoader());

//trying to explicitly load this class again using Extension class loader
Class.forName("test.ClassLoaderTest", true
, ClassLoaderTest.class.getClassLoader().getParent());
}catch(ClassNotFoundException ex){
Logger.getLogger(ClassLoaderTest.class.getName()).log(Level.SEVERE, null, ex);
}
}

}

Output:
ClassLoaderTest.getClass().getClassLoader() : [email protected]
16/08/20122:43:48 AM test.ClassLoaderTest main
SEVERE: null
java.lang.ClassNotFoundException: test.ClassLoaderTest
at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
at sun.misc.Launcher$ExtClassLoader.findClass(Launcher.java:229)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at test.ClassLoaderTest.main(ClassLoaderTest.java:29)

唯一性原则

根据这个原则,一个类文件被父级类加载器加载后,子级类加载器则不能加载它。尽管完全可以编写一个类加载器,它自己加载类文件,这违反了委托和唯一性原则,这样做没有什么好处。在您编写您自己的类加载器时,您应当遵守所有的类加载器原则。

java中如何准确加载类
java提供了API通过Class.forName(classname) 和Class.forName(classname, initialized, classloader)方法来明确加载一个类,还记得JDBC中用来加载JDBC驱动的方式吗,就是使用该机制来加载对应的类。就像我们在上边例子中展示的一样,你可以将加载特定类的加载器的名称连同类的二进制名称一起作为参数传进来。Class是通过调用java.lang.ClassLoader的loadClass()方法,该方法又会调用findClass()方法来为相应类查找对应的字节码。在这个例子中扩展类加载器使用java.lang.URLClassLoader用来在jar包和目录中查找类文件以及资源。其中,查询中所有以"/"结尾的的都会被当作目录处理。如果findClass()方法没有找到相关类,那么它会抛出java.lang.ClassNotFoundException异常;如果该方法找到了相关类,它会调用defineClass()方法来将字节码转换为.class的实例并将该实例返回给调用者。

java中在哪里使用类加载器
在Java中类加载器是一个强有力的概念,在许多地方都有使用。其中比较流行的一个例子就是applet中用来加载类的AppletClassLoader类加载器,由于applet是从互联网上加载而不是从本地文件系统中来加载类。通过使用单独的类加载器可以从多个不同的资源中加载多次相同的类,并且这些类在JVM中会被当作不同的类对待。J2EE使用不同的类加载器从不同的位置来加载类,比如war包中的类是通过Web-app类加载器来加载,而在EJB-JAR包中绑定的类则通过其他的类加载器加载。而一些web服务器也支持热部署功能,这都是通过实现ClassLoader来达到目的的。同时也可以使用ClassLoader来从数据库或者其他持久化存储系统中来加载类。
这就是所有关于java中的类加载器及其工作原理。我们已经了解了委托机制、可见性、唯一性原则,这些对代码调试或者解决java中类加载器相关问题时都很重要。总而言之,类加载器工作原理对每一位设计java应用和包的开发者和架构师所必备的知识。

时间: 2024-10-19 03:59:56

Java类加载器工作原理的相关文章

Java类加载器的工作原理

Java类加载器的作用就是在运行时加载类.Java类加载器基于三个机制:委托.可见性和单一性.委托机制是指将加载一个类的请求交给父类加载 器,如果这个父类加载器不能够找到或者加载这个类,那么再加载它.可见性的原理是子类的加载器可以看见所有的父类加载器加载的类,而父类加载器看不到子类 加载器加载的类.单一性原理是指仅加载一个类一次,这是由委托机制确保子类加载器不会再次加载父类加载器加载过的类.正确理解类加载器能够帮你解决 NoClassDefFoundError和java.lang.ClassNo

java笔记--理解java类加载器以及ClassLoader类

类加载器概述: java类的加载是由虚拟机来完成的,虚拟机把描述类的Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成能被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制.JVM中用来完成上述功能的具体实现就是类加载器.类加载器读取.class字节码文件将其转换成java.lang.Class类的一个实例.每个实例用来表示一个java类.通过该实例的newInstance()方法可以创建出一个该类的对象. 类的生命周期: 类从加载到虚拟机内存到被从内存中释放,经历的

java gc的工作原理、如何优化GC的性能、如何和GC进行有效的交互

java gc的工作原理.如何优化GC的性能.如何和GC进行有效的交互 一个优秀的Java 程序员必须了解GC 的工作原理.如何优化GC的性能.如何和GC进行有效的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等.只有全面提升内存的管理效 率,才能提高整个应用程序的性能. 本篇文章首先简单介绍GC的工作原理,然后再对GC的几个关键问题进行深入探讨,最后提出一些Java程序设计建议,从GC角度提高Java程序的性能. GC的基本原理     Java 的内存管理实际上就是对象的管

Java类加载器深入探索

林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 什么是.class文件? class文件全名称为Java class文件,主要在平台无关性和网络移动性方面使Java更适合网络.它在平台无关性方面的任务是:为Java程序提供独立于底层主机平台的二进制形式的服务.class文件径打破了C或者C++等语言所遵循的传统,使用这些传统语言写的程序通常首先被编译,然后被连接成单独的.专门支持特定硬件平台和操作系统的二进制文件.通常情况下,一个平台上的

【正文】Java类加载器( CLassLoader ) 死磕 4: 神秘的双亲委托机制

[正文]Java类加载器(  CLassLoader ) 死磕4:  神秘的双亲委托机制 本小节目录 4.1. 每个类加载器都有一个parent父加载器 4.2. 类加载器之间的层次关系 4.3. 类的加载次序 4.4 双亲委托机制原理与沙箱机制 4.5. forName方法和loadClass方法的关系 4.6. 使用组合而不用继承 4.7. 各种不同的类加载途径 4.1.每个类加载器都有一个parent父加载器 每个类加载器都有一个parent父加载器,比如加载SystemConfig.cl

深入探讨 Java 类加载器

转自:http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 类加载器(class loader)是 Java™中的一个很重要的概念.类加载器负责加载 Java 类的字节代码到 Java 虚拟机中.本文首先详细介绍了 Java 类加载器的基本概念,包括代理模式.加载类的具体过程和线程上下文类加载器等,接着介绍如何开发自己的类加载器,最后介绍了类加载器在 Web 容器和 OSGi™中的应用. 类加载器是 Java 语言的一个创新,也是

java类加载器——ClassLoader

Java的设计初衷是主要面向嵌入式领域,对于自定义的一些类,考虑使用依需求加载原则,即在程序使用到时才加载类,节省内存消耗,这时即可通过类加载器来动态加载. 如果你平时只是做web开发,那应该很少会跟类加载器打交道,但如果你想深入学习tomcat服务器的架构,它是必不可少的.所谓类加载器,就是用于加载Java类到Java虚拟机中,它负责读取Java字节码,并转换成java.lang.Class类的一个实例,使字节代码.class文件得以运行.一般类加载器负责根据一个指定的类找到对应的字节代码,然

Java 类加载器(转)

java虚拟机中可以安装多个类加载,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap(内嵌在java虚拟机中由C++编写),ExtClassLoader,AppClassLoad    类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap.    java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象    或者

JAVA 类加载器 第14节

JAVA 类加载器 第14节 今天我们将类加载机制5个阶段中的第一个阶段,加载,又叫做装载.为了阅读好区分,以下都叫做装载. 装载的第一步就是要获得二进制的字节流,它可以从读.class文件获得,也可以从网络中接收别人发送的字节流.反正只要符合虚拟机规定的字节流格式都可以进入这个阶段. 有了字节流了之后,要进行装载还需要一个工具,那就是加载器了.加载器既可以使用系统提供的引导类加载器,也可以用户自己定义加载器,只需要继承ClassLoader,再重写loadClass()方法就可以实现一个自己的