------
Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1、类加载器概念
1)通常就是将class字节码文件加载进内存的一个类
2)注意:第一个类加载器不是类:就是bootStrap
2、默认类加载器
1)三种默认的类加载器:BootStrap,ExtClassLoader,AppClassLoader
2)关系他们之间有:根->父->子的关系(当然也可以自己写类加载器,但必须要有父级加载器)
例:
public static void main(String[] args) throws Exception {
System.out.println(ClassLoadTest.class.getClassLoader().getClass().getName());//AppClassLoader加载器
System.out.println(System.class.getClassLoader());//根级类加载器,返回null
ClassLoader classLoader = ClassLoadTest.class.getClassLoader();
while(classLoader !=null)
{
System.out.println(classLoader.getClass().getName());
classLoader = classLoader.getParent();//获得父级类加载器
}<span style="white-space:pre"> </span>System.out.println(classLoader);
}
打印结果:sun.misc.Launcher$AppClassLoader
null
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$ExtClassLoader
null
上面的代码进一步证明了默认类加载器的存在,以及类加载器之间的父子关系。
3)在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象或者默认采用系统类加载器为其父级类加载。
3、类加载器的委托机制
1)每个类加载器加载时,都会先委托给上级类加载器,直至到达根类加载器,然后从上到下一级一级的找类加载器。 当所有
祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,
不是再去找发起
者类加
载器的子类,因为没有getChild方法
4、自定义类加载器
1)
自定义类加载器的
原理
类加载器中的loadClass方法内部实现了父类委托机制,因此我们没有必要自己覆盖loadClass,否则需要自己去实现父类委 托机制。我们只需要覆盖findClass方法。loadClass方法中调用了findClass方法,使用的是模板设计模式。我们得到了Class文件后,就可以通过defineClass方
法将二进制数据转换成字节码
。
2)编写一个自己的类加载器,可实现对加密过的类进行加载和解密。
过程:
a、自定义的类加载器必须继承ClassLoader
b、覆盖findClass方法。
c、在findClass方法中对字节码文件进行解密。
d、调用自定义的类加载器对字节码文件进行解密。
自定义类加载器代码:
package com.wit.day2;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
//自己定义的类加载器
public class MyClassLoader extends ClassLoader{
/**
* @author shen
*/
private String path = null;
public MyClassLoader(String path) throws Exception//检查文件是否存在
{
File f = new File(path);
if(!f.isDirectory())
{
throw new RuntimeException(path + " is not a directory");
}
this.path = path;
}
//对字节码文件进行加密解密
public static void cypher(InputStream istream,OutputStream ostream) throws Exception
{
//下面这段代码可能遇到255的字节,当成byte就成了-1
/*byte b = 0;
while((b = (byte)istream.read()) != -1)
{
ostream.write(b ^ 0xff);
}*/
int b = 0;
while((b = istream.read()) != -1)
{
ostream.write(((byte)b) ^ 0xff);
}
}
//此类作为类加载器时,此方法起作用
@Override
@SuppressWarnings("deprecation")
protected Class<?> findClass(String name)
{
String classFileName = path+"\\"+name+".class";
FileInputStream fis;
try {
fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis, bos);
fis.close();
byte[] bytes = bos.toByteArray();
return defineClass(bytes,0,bytes.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String [] args) throws Exception
{
//ClassLoaderAttachment类编译生成的class文件加密后放入文件夹lib中
String srcPath = args[0];//源路径
String destPath = args[1]; //目标路径
FileInputStream fis = new FileInputStream(srcPath);
File f = new File(destPath, new File(srcPath).getName());//不用检查目录最后是否有目录分割符
FileOutputStream fos = new FileOutputStream(f);
cypher(fis,fos);
fis.close();
fos.close();
}
}<span style="white-space:pre"> </span>
自定义类加载器测试类:
package com.wit.day2;
import java.util.Date;
//一个普通的类,用于给类加载器加密
public class ClassLoaderAttachment extends Date{
@Override
public String toString(){
return "用来加密的类ClassLoaderAttachment";
}
}
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
<span style="white-space:pre"> </span>Class clazz = new MyClassLoader("lib").loadClass("ClassLoaderAttachment");
<span style="white-space:pre"> </span>Date date = (Date) clazz.newInstance();
<span style="white-space:pre"> </span>System.out.println(date);
}
}
输出结果:用来加密的类ClassLoaderAttachment
3)自定义类加载的一个问题:ClassLoaderAttachment类继承Date
如果直接写ClassLoaderAttachment d = (ClassLoaderAttachment)clazz.newInstance();这条语句,编译的时候就会报错,因为此时的
ClassLoaderAttachment在AppClassLoader加载进内存后就无法识别。所以需要通过借助一个父类对象绕过编译器。也就
是:Date d1 = (Date)clazz.newInstance();。