Java和其余语言不一样的是,Java是运行于Java虚拟机(JVM)。这就意味着编译后的代码是以
一种和平台无关的格式保存的,而不是某种特定的机器上运行的格式。这种格式和传统的可
执行代码格式有不少重要的区别。具体来讲,不一样于C或者C++程序,Java程序不是一个独
立的可执行文件,而是由不少分开的类文件组成,每一个类文件对应一个Java类。 另外,这
些类文件并非立刻加载到内存,而是当程序须要的时候才加载。 类加载器就是Java虚拟
机中用来把类加载到内存的工具。并且,Java类加载器也是用Java实现的。这样你就不须要
对Java虚拟机有深刻的理解就能够很容易建立本身的类加载器了。
为何要建立类加载器?
既然Java虚拟金已经有了类加载器,咱们还要本身建立其余的呢?问得好。默认的类加载器
只知道如何从本地系统加载类。当你的程序彻底在本机编译的话,默认的类加载器通常都工
做的很好。可是Java中最激动人心的地方之一就是很容易的从网络上而不仅是本地加载类。
举个例子,浏览器能够经过自定义的类加载器加载类。 还有
不少加载类的方式。除了简单的从本地或者网络外,你还能够经过自定义Java中最激动人心
的地方之一:
* 执行非信任代码前自动验证数字签名
* 根据用户提供的密码解密代码
* 根据用户的须要动态的建立类
你关心的任何东西都能方便的以字节码的形式集成到你的应用中
自定义类加载器的例子
若是你已经使用过JDK(Java软件开发包)中的appletviewer(小应用程序浏览器)或者其余
Java嵌入式浏览器,你就已经使用了自定义类加载器了。Sun刚刚发布Java语言的时候,最
使人兴奋的一件事就是观看Java如何执行从远程网站下载的代码。执行从远程站点经过HTT
P链接传送来的字节码看起来有点难以想象。之因此可以工做,由于Java有安装自定义类加
载器的能力。小应用程序浏览器包含了一个类加载器,这个类加载器不从本地找Java类,而
是访问远程服务器,经过HTTP加载原始字节码文件,而后在Java虚拟机中转化为Java类。当
然类加载器还作了其余的不少事情:他们阻止不安全的Java类,并且保持不一样页面上的不一样
小程序不会互相干扰。Luke Gorrie写的一个包Echidna是一个开放的Java软件包,他容许在
一个Java虚拟机中安全的运行多个Java应用程序。它经过使用自定义类加载器给每一个应用程
序一份类文件的拷贝来阻止应用程序之间的干扰。html
java类加载器 :java
java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器(也叫应用类加载器)bootstrap
类加载器是Java最强大的特征之一。可是开发者经常忘记类加载组件。类加载器是在运行时负责寻找和加载类文件的类。Java容许使用不一样的类加载器,甚至自定义的类加载器。 小程序
Java 程序包含不少类文件,每个都与单个Java类相对应,这些类文件不像静态C程序,一次性加载入内存,它们随时须要随时加载。这就是类加载器不同凡响的地 方。它从源文件(一般是.class 或 .jar文件)得到不依赖平台的字节码,而后将它们加载到JVM内存空间,因此它们能被解释和执行。默认状态下,应用程序的每一个类由 java.lang.ClassLoader加载。由于它能够被继承,因此能够自由地增强其功能。浏览器
使用自定义类加载器的缘由安全
默认的 java.lang.ClassLoader仅仅能够从加载本地文件系统的类。Java被设计成不论本地磁盘或网络都有足够的弹性加载类,而且能够在加载 以前处理特殊事物。例如:应用程序能够检查Web站点或FTP上插入类的更新版本而且自动校验数字签名确保执行可信任的代码。许多众所周知的软件都使用自 己的类加载器。服务器
一般默认加载器是所谓的bootstrap类加载器;它负责加载诸如java.lang.Object等关键类和加 载其余rt.jar文件的运行时代码到内存。由于Java语言规范没有提供bootstrap类加载器的详细信息,不一样的JVM可能有不一样的类加载器。如 果看到网页上有applets在运行,则它使用的是自定义类加载器。嵌入到浏览器中的applet阅读器包含了能够访问远程服务器上站点的类加载器,它可 以经过HTTP加载原始字节码文件,而且在JVM中将它们转换成类。网络
类加载器(除了bootstrap类加载器)有父类加载器,这些父类是基本加载器的加载器实例。最重要的一点是设置正确的父加载器。而后可使用 类加载器的getParent()方法实现委派类请求(例如:自定义类加载器找不到使用专门方法的类时)。此时必须为将父加载器做为 java.lang.ClassLoader构造器的参数: app
public class MyClassLoader extends ClassLoader工具
{
public MyClassLoader()
{
super(MyClassLoader.class.getClassLoader());
}
}
loadClass(String name)方法是ClassLoader的入口。名字参数是彻底资格类名(FQCN),例如关于包类名。若是父加载器设置正确,当请求 MyClassLoader中的loadClass(String name)方法加载类,但又找不到须要加载的类时,则首先会询问父加载器。若是父加载器也找不到此类,则调用findClass(String name)方法。默认状态下findClass(String name)会抛出ClassNotFoundException例外,不少开发人员都很清楚这个例外。自定义类加载器的开发者都但愿从 java.lang.ClassLoader继承时跳过这个方法。
findClass()方法的目标是为MyClassLoader容纳全部专门代码,此时不须要重复其余代码(例如当加载失败时调用系统 ClassLoader)。在此方法中,ClassLoader须要从原文件中获取字节码。一旦找到字节码则会调用defineClass()方法。 ClassLoader实例调用此方法是很是重要的。所以,若是两个ClassLoader实例定义了来自不一样或相同原文件的字节码,则被定义的类也将区 别对待。
咱们给出两个类似的类加载器MyClassLoader1 和 MyClassLoader2,它们均可以从相同的源文件找到MyCoolClass字节码。若是一个程序经过这两个加载器分别独立加载 MyCoolClass实例(coolClass1经过MyClassLoader1加载, coolClass2经过MyClassLoader2加载),MyCoolClass.class可以被独立定义。执行下面的代码:
MyCoolClass coolClass1 = (MyCoolClass)coolClass2;
将获得一个ClassCastException例外。(开发者若是没有很好的理解类加载机制则常常碰到这样的状况。)由于它们是不一样的加载器 所定义的,JVM将它们当作不一样的类。虽然它们是相同类型的类而且从相同的源文件加载,可是变量coolClass1和coolClass2不兼容。
不管是否跳过findClass() 或 loadClass(),getSystemClassLoader()方法将以实际ClassLoader对象的形式直接访问系统 ClassLoader。也能够经过调用findSystemClass(String name)方法间接访问。getParent()方法容许得到父加载器。Listing A给出了能够运行的自定义类加载器示例。