----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
什么是类加载器
类加载器就是加载类的工具,我们在程序中用到一个类的时候,虚拟机通过类加载器将class文件加载加载到内存中在进行一系列的处理后变成字节码为我们使用。
Java中默认的类加载器
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
类加载器也是java类,因为其他java类加载器本身也要被类加载器加载,显然必须有一个类加载器不是java类,这就是BootStrap。
代码示例:查看个各类的加载器
package cn.itcast.classloader;
/**
* 测试类加载器的类
* @author hezhudong
*
*/
publicclass ClassLoaderTest {
/**
* @param args
*/
publicstaticvoid main(String[] args) {
// TODO Auto-generated method stub
//获取ClassLoaderTest类的加载器名称并打印
String classLoaderName = ClassLoaderTest.class.getClassLoader().getClass().getName();
System.out.println(classLoaderName);
//获取System类的加载器并打印
ClassLoader classLoader2 = System.class.getClassLoader();
System.out.println(classLoader2);
//获取classLoaderTest类的加载器以及加载器的父加载器
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while(loader!=null) {
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
}
}
类加载器的委托机制
Java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织如下图,在实例化每个类加载器对象时,需要为其指定一个父级加载器对象或默认采用系统加载器为其父类加载。
当java虚拟机要加载一个类时,到底由哪个类加载器加载呢?
1. 首先当前线程的类加载器去加载线程中的第一个类。
2. 如果A类中引用了B类,java虚拟机将使用加载A类的加载器来加载B类
3. 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类
每个类加载器在加载类时,又先委托给其上级类加载器。
1. 当所有上级类加载器没有加载到类时,回到发起者类加载器,还没有加载到类会抛出ClassNotFoundException,不会在找发起者类加载器的下级加载器,因为没有getChild方法,即使有,那有多个儿子,该去找哪一个?
2. 对类加载器的层次结构图和委托加载器原理,解释先前将ClassLoaderTest输出到jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。
自定义类加载器
1. 自定义类加载器必须继承ClassLoader
继承ClassLoader创建实例对象后就是自定义的类加载器。
2. 重写findClass方法
ClassLoader类的loadClass方法用委托机制加载类,如果没有加载到会调用findClass方法继续加载。所以自定义加载器需要重写findClass而不要重写loadClass方法修改委托机制。
3. findClass方法
得到字节码字节数组作为参数传入方法findClass返回Class类实例。
示例:
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}
自定义类加载器对class文件进行加密和解密
步骤:
1. 编写一个对文件内容进行加密解密的程序。
2. 编写一个自己的类加载器,可实现对加密过的类进行加载和解密。
3. 编写一个程序调用类加载器,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中可以出了是用ClassLoader.load()方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后使用Class.forName.
实验步骤:
1. 对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如,java MyClassLoader MyTest.class F:\itcast
2. 运行加载类的程序,结果能够被正常加载,但打印出来的类加载器的名称为AppClassLoader:java MyClassLoader MyTest.class F:\itcast
3. 用加密后的类文件替换classpath环境下的类文件,在执行上一步操作就出问题,错误说明AppClassLoader类加载器加载失败。
4. 删除classpath环境下的类文件,在执行上一步就没问题了。
代码示例:通过自定义类加载器对class文件进行加密,并通过自定义加载器加载class文件并进行解密。
进行加密的类ClassLoaderAttachment,需要使用自定义加载器加载
package cn.itcast.classloader;
import java.util.Date;
/**
* 进行加密的类ClassLoaderAttachment,需要使用自定义加载器加载
* @author hezhudong
*
*/
publicclass ClassLoaderAttachment extends Date {
/**
* 复写toString方法
*/
@Override
public String toString() {
return"hello,itheima";
}
}
自定义类加载器类MyClassLoader,执行主函数class文件进行加密,调用加载器类实例加载class文件并进行解密。
package cn.itcast.classloader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 自定义类加载器类,执行主函数class文件进行加密,调用加载器类实例加载class文件并进行解密。
* @author hezhudong
*
*/
publicclass MyClassLoader extends ClassLoader{
//class文件路径
private String classDir ;
/**
* 构造函数
*/
MyClassLoader() {}
MyClassLoader(String classDir ) {
this.classDir = classDir;
}
/**
* 主函数
*/
publicstaticvoid main(String[] args) throws Exception{
String srcPath = args[0];
String destDir = args[1];
FileInputStream fis = new FileInputStream(srcPath);
String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);
String destPath = destDir+"\\"+destFileName;
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
/*
* 对文件进行加密,同时也可以解密
*/
privatestaticvoid cypher(InputStream ips,OutputStream ops) throws Exception {
int b =-1;
while((b=ips.read())!=-1) {
ops.write(b^0xff);
}
}
/**
* 复写ClassLoader类的findCLass方法,自定义查找类方式。
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classFileName = classDir + "\\" + name + ".class";
FileInputStream fis = null;
ByteArrayOutputStream bos = null;
try {
fis = new FileInputStream(classFileName);
bos = new ByteArrayOutputStream();
cypher(fis,bos);
byte[] loadData = bos.toByteArray();
return defineClass(null,loadData,0,loadData.length);
} catch (Exception e) {
e.printStackTrace();
}finally {
try{
if(fis!=null)
fis.close();
}catch(Exception ex) {
ex.printStackTrace();
}
try{
if(bos!=null)
bos.close();
}catch(Exception ex) {
ex.printStackTrace();
}
}
returnsuper.findClass(name);
}
}
测试类自定义加载器类ClassLoaderTest
package cn.itcast.classloader;
import java.util.Date;
/**
* 测试类自定义加载器类ClassLoaderTest
* @author hezhudong
*
*/
publicclass ClassLoaderTest {
/**
* @param args
*/
publicstaticvoid main(String[] args) throws Exception{
//用自定义类加载器去指定位置加载ClassLoaderAttachment,而不能直接使用,因为已经被加密。
Class clazz =new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
Date d =(Date)clazz.newInstance();
//打印自定义类加载器获取到的对象,并打印由哪个加载器加载
System.out.println(d);
System.out.println(d.getClass().getClassLoader().getClass().getName());
}
}
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------