**
* 类加载器
1、每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的
类装载器去加载类,这就是类加载器的委托模式。
2、类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的 类时,
然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装 载器时,
如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异 常。
3、当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
因为线程中有自己的类加载器,线程中的类都可以指定自己的类加载器。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
4、每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛
ClassNotFoundException,不是再去找发起者类加载器的儿子,
因为没有getChild 方法,即使有,那有多个儿子,找哪一个呢?
5、对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest
输出成 jre/lib/ext目录下的itcast.jar包中后,运行结果为 ExtClassLoader的原因。
自定义类加载器
知识讲解:
自定义的类加载器的必须继承ClassLoader
loadClass方法与findClass方法
defineClass方法
编程步骤:
编写一个对文件内容进行简单加密的程序。
编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。
编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,
因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,
还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。
实验步骤:
对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如: java MyClassLoader MyTest.class F:\itcast
运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTest F:\itcast
用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
*
*/
/**在项目下建一个文件夹,把未加密 的目标class文件 加密并写入该文件夹
* 然后用该类解密该文件,获取该字节码文件对象
* @author yaomaokai
*
*/
import java.io.*;
public class MyClassLoader extends ClassLoader
{
public static void main(String[] args) throws Exception
{
String srcPath = args[0];
//截取文件名
String fileNmae = srcPath.substring(srcPath.lastIndexOf("\\") + 1);
//目标路径
String destDir = args[1];
//目标路径+截取的文件名组成目标绝对路径
String destPath = destDir + "\\" + fileNmae;
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath);
//加密
syhpern(fis, fos);
fis.close();
fos.close();
}
//输入流加密到输出流
private static void syhpern(InputStream ips, OutputStream ops)
throws Exception
{
int b = 0;
while ((b = ips.read()) != -1)
{//加密后写入输出流
ops.write(b ^ 0xff);
}
}
private String classPath;
public MyClassLoader()
{
}
//类加载器传入目标路径
public MyClassLoader(String classPath)
{
this.classPath = classPath;
}
@Override//传入一个字节码文件名,重写父类的findClass方法,返回该类的字节码文件对象
protected Class<?> findClass(String name) throws ClassNotFoundException
{
String classFileName=classPath+"\\"+name+".class";
try
{ //字节输入流读取未解密的文件
FileInputStream fis = new FileInputStream(classFileName);
System.out.println(classFileName);
//字节数组输出流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//解密
syhpern(fis,bos);
fis.close();
//字节输出流变成字节数据
byte[] buff= bos.toByteArray();
//传入该字节数组 返回字节码对象
return defineClass(buff, 0, buff.length);
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// TODO Auto-generated method stub
return null;
}
}
测试类
public class Test
{
/**
* @param args
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
MyClassLoader loader = new MyClassLoader("itcastlib");
Class clazz = loader.loadClass("Itheima");
Object obj =clazz.newInstance();
System.out.println(obj);
}
}
|