(1)、java.lang.Class 概念:
Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,记录每个对象所属的类,有哪些属性和方法,选定那些方法
去执行。这些记录都保持在一个是Class类里面。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
(2)、类加载器:
BootStrap,ExtClassLoader,AppClassLoader 三个类加载器,每个类负责加载特定位置的类:
类加载器本身就是java 类,BootStrap是用c++写的一个类,它在jVM启动的时候自动加载到
内存,它负责其它类加载器的加载工作。
test 如何测试一个类是由那个类加载器加载的
package cn.com.chenlly.test;
/**
* @ Class ClassLoaderTest.java @ Description @ Company OpenData @ Author
* Chenlly E-mail: Chenlly99@Gmail.com @ Version 1.0 @ Date Jul 3, 2010
*/
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println(ClassLoaderTest.class.getClassLoader().getClass()
.getName());
}
}
ClassLoaderTest.class 得到这个类对象
ClassLoaderTest.class.getClassLoader() 得到加载这个类对象的类加载器对象
//out put
sun.misc.Launcher$AppClassLoader
测试BootStrap,ExtClassLoader,AppClassLoader三者之间的关系
package cn.com.chenlly.test;
/**
* @ Class ClassLoaderTest.java @ Description @ Company OpenData @ Author
* Chenlly E-mail: Chenlly99@Gmail.com @ Version 1.0 @ Date Jul 3, 2010
*/
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println( loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
}
}
//out put
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null //为null 说明这个类加载器是BootStrap
显而易见,这三个类加载器的关系是AppClassLoader->ExtClassLoader->BottStrap
AppClassLoader 只加载ClassPath 指定目录下的jar包
BootStrap 加载 jre/lib/rt.jar 目录下的rt.jar
ExtClassLoader 加载jre/lib/ext/*.jar 目录下的jar
(3)、类加载器的委托机制。
当在Ext 目录和 Classpath 下都有一份class 文件时,则首先会在Ext下去加载。这就是类加载器的委托机制。
我们可以写一个自己的类加载器,挂到这个树形结构中。但必须继承java.lang.ClassLoader。
我们自己写个java.lang.System类 会不会加载?
由于类加载器的委托机制,首先委托给AppClassLoader,然后AppClassLoader会委托给ExtClassLoader,ExtClassLoader委托给
BootStrap在 jre/lib/rt.jar 找到了java.lang.System。BootStrap就会把这个类加载进来。而不会去加载jre/lib/ext/
或者classpath 下的那个java.lang.System类。
如果回到发起者类加载器还加载不了,(比如我们写的java类没有配置classpth) 则会抛出ClassNotFoundException 异常。
(4)、编写自己的类加载器
首先对生成的ClassLoaderDate.class加密以后,是无法运行的
只有通过我自己写的一个类加载器解密以后,才可以运行的
步骤(1):对一个.class 文件加密,生产的加密后的文件放在cypherFiles下。
步骤(2):通过自定义的类加载器,在加载cypherFiles目录下的加密以后的.class文件以后哌解密然后放入内存。运行正常。
package cn.com.chenlly.test;
import java.util.Date;
/**
* @ Class ClassLoaderDate.java
* @ Description 需要加密的类
* @ Company OpenData
* @ Author Chenlly E-mail: Chenlly99@Gmail.com
* @ Version 1.0
* @ Date Jul 3, 2010
*/
public class ClassLoaderDate extends Date {
public String toString(){
return "Hello Str";
}
}
package cn.com.chenlly.test;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @ Class MyClassLoader.java
* @ Description 自定义的类加载器,继承ClassLoader类
* @ Company OpenData
* @ Author Chenlly E-mail: Chenlly99@Gmail.com
* @ Version 1.0
* @ Date Jul 3, 2010
*/
public class MyClassLoader extends ClassLoader {
private String fileDir;
public MyClassLoader(){
}
public MyClassLoader(String fileDir){
this.fileDir = fileDir;
}
//生产加密后的.class 文件到指定目录
public static void createClassFile(String srcPath,String destDir) throws IOException{
FileInputStream fis = new FileInputStream(srcPath);
String destFileName = srcPath.substring(srcPath.lastIndexOf(");
String destPath = destDir+";
FileOutputStream fos = new FileOutputStream(destPath);
//加密
cypher(fis,fos);
fis.close();
fos.close();
}
//加密或解密方法,对二进制数做异或
public static void cypher(InputStream ips,OutputStream ops) throws IOException{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b^0xff);//对二进制数做异或
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String clazzFileName = fileDir+"//"+name.substring(name.lastIndexOf(".")+1)+".class";
try {
FileInputStream fis = new FileInputStream(clazzFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//解密
cypher(fis,bos);
fis.close();
byte[] bytes = bos.toByteArray();
return this.defineClass(bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public static void main(String []args) throws IOException{
String srcPath = args[0];//.class 文件所在绝对路径,比如D:/workspace/HelloWorld/WebRoot/WEB-INF/classes/cn/com/chenlly/test/ClassLoaderDate.class
String destPath = args[1]; //加密后的.class 文件所在目录cypherFiles
createClassFile(srcPath,destPath);
}
}
package cn.com.chenlly.test;
import java.util.Date;
/**
* @ Class ClassLoaderTest.java
@ Description 测试客户端
@ Company OpenData
@ Author
* Chenlly E-mail: Chenlly99@Gmail.com
@ Version 1.0 @ Date Jul 3, 2010
*/
public class ClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException {
// 使用系统类加载器加载文件,抛出异常
//System.out.println(new ClassLoaderDate().toString());
// 调用自己的写的类加载器加载文件
Class clazz = new MyClassLoader("cypherFiles").loadClass("ClassLoaderDate");// 这个加载器被挂在系统加载器树上
// 注意:如果ClassLoaderDate不继承Date,在这里这样写:ClassLoaderDate d =
// (ClassLoaderDate) clazz.newInstance();
// 编译都通不过,因为ClassLoaderDate.class文件是加密以后的,编译器无法识别,所以让它去继承Date
Date d = (Date) clazz.newInstance();// 通过加载后的字节码创建对象
System.out.println(d.toString());
}
}