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应用程序。它通过使用自定义类加载器给每个应用程
序一份类文件的拷贝来阻止应用程序之间的干扰。
java类加载器 :
java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器(也叫应用类加载器)
类加载器是Java最强大的特征之一。但是开发者常常忘记类加载组件。类加载器是在运行时负责寻找和加载类文件的类。Java允许使用不同的类加载器,甚至自定义的类加载器。
Java 程序包含很多类文件,每一个都与单个Java类相对应,这些类文件不像静态C程序,一次性加载入内存,它们随时需要随时加载。这就是类加载器与众不同的地 方。它从源文件(通常是.class 或 .jar文件)获得不依赖平台的字节码,然后将它们加载到JVM内存空间,所以它们能被解释和执行。默认状态下,应用程序的每个类由 java.lang.ClassLoader加载。因为它可以被继承,所以可以自由地加强其功能。
自定义类加载器
import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.reflect.Method;
public class CustomClassLoader extends URLClassLoader {
private FileInputStream input = null; //文件输入流
private ByteArrayOutputStream out = null; //字节数组输出流
private String[] url = null; //类文件加载路径
private byte[] data = null; //类文件字节码
private String extensionalName = ""; //类文件扩展名
public CustomClassLoader(URL[] urls) throws Exception{
super(urls);
this.url = new String[urls.length];
for (int i = 0; i < urls.length; i++) {
this.url[i] = urls[i].toURI().toString();
}
}
/*
* 解析URL
*/
private void setFilePath() {
for (int i = 0; i < this.url.length; i++) {
if (this.url[i].substring(0,4).toLowerCase().equals("file") == true) {
this.url[i] = this.url[i].substring(5);
}
}
}
/*
* 获得指定类名(包名+类名)文件的字节码
* @name name String
* @return byte[]
*/
private byte[] getFileData(String name) {
try {
this.setFilePath();
for (String url : this.url) {
String fileName = url + name.replace('.', '/').concat(".") +
this.getExtensionalName();
input = new FileInputStream(new File(fileName));
if (input != null) {
break;
}
}
out = new ByteArrayOutputStream();
data = new byte[1024];
int len = -1;
while ((len = input.read(data)) != -1) {
out.write(data, 0, len);
}
data = out.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (input != null)
input.close();
if (out != null)
out.close();
return data;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
/*
* 根据指定类名查找类文件
* @param name String
* @return Class
*/
protected Class findClassByName(String name) {
try {
byte[] data = this.getFileData(name);
if (data == null) {
return null;
}
return this.defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/*
* 重写loadClass()方法
* @param name String
* @return Class
*/
public Class loadClass(String name) {
Class c = null;
try {
c = super.loadClass(name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (c == null) //父类默认方法没有加载到指定类时,使用自定义方法查找
c = this.findClassByName(name);
return c;
}
}
public String getExtensionalName() {
return extensionalName;
}
public void setExtensionalName(String extensionalName) {
this.extensionalName = extensionalName;
}
public static void main(String[] args) throws Exception {
URL[] url = new URL[] {new URL("file:e:/")}; //添加你想要加载类的路径
//网络或本地均可
CustomClassLoader csl = new CustomClassLoader(url);
csl.setExtensionalName("rs");
Class c1 = csl.loadClass("com.demo");
Object obj = c1.newInstance();
Method method = c1.getMethod("printText", null);
method.invoke(obj, null);
}