1.类加载器及其委托机制的深入分析
概念:java虚拟机将一个类加载到内存所需要的类就叫类加载器;从classpath 加载.class文件到内存生成字节码。
2java虚拟机可以安装多个类加载器, 系统默认三个类加载器:BootStrap ExtClassLoader AppClassLoader;
Java 加载器也是java类, 但是BootStrap不是java类写,而是C++ 写的;
例子:
packageorg.nagi.base;
//测试类加载器
publicclass TestClassLoader {
publicstaticvoid main(String[] args) {
String name = TestClassLoader.class.getClassLoader().getClass().getName();
System.out.println(name);
//运行结果sun.misc.Launcher$AppClassLoader
}
}
AppClassLoaderd加载ClassPacth 指定目录,ExtClassLoader加载JRE/lib/ext/*jar
BootStrap 加载JRE/lib/rt.jar 当我们在加载类的时候,系统默认是从当前的线程加载类,
这个我们也可以指定类加载器,在thread 方法中有setContextClassLoader()方法
void
setContextClassLoader(ClassLoader cl)
设置该线程的上下文 ClassLoader
当appClassLoader在加载时候,它会交给自己父类加载;这样可以避免各干个的,内存也不会出现的两份字节码。 这就是委托加载机制又叫双亲委派机制。
2.自定义类加载器的编写原理分析
自己编写类加载器:模块设计方法:
父类:classLoader()子类1:classLoader() 子类2 :classLoader
基础知识原理:
(1)自己定义加载器需要覆盖findClassLoader();这样在加载我们自己的类的时候不会交给双亲去处理。
(2)将class 文件变成了字节码,difineClass()方法,
(3)代码:
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
代码:
importjava.io.ByteArrayOutputStream;
importjava.io.FileInputStream;
importjava.io.FileOutputStream;
import java.io.InputStream;
importjava.io.OutputStream;
publicclass MyClassLoaderextends ClassLoader{
/**
* @param args
*/
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();
}
/**
* 对字节码加密
* @param ips
* @param ops
* @throws Exception
*/
privatestaticvoid cypher(InputStream ips ,OutputStream ops)throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);
}
}
private StringclassDir;
@Override
protected Class<?> findClass(String name)throws ClassNotFoundException {
StringclassFileName = classDir +"\\" +name.substring(name.lastIndexOf('.')+1) +".class";
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
fis.close();
System.out.println("aaa");
byte[] bytes = bos.toByteArray();
returndefineClass(bytes, 0,bytes.length);//返回字节码
} catch (Exception e) {
e.printStackTrace();
}
returnnull;
}
public MyClassLoader(){
}
//实例化classDir
public MyClassLoader(String classDir){
this.classDir = classDir;
}
}
调用自己定义好的类加载器:
Class clazz =new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttachment");
Date d1 = (Date)clazz.newInstance();
System.out.println(d1);
3.编写对class文件进行加密的工具类
/**
* 对字节码加密
* @param ips
* @param ops
* @throws Exception
*/
private static void cypher(InputStream ips ,OutputStream ops)throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);
}
}
**4.类加载器的一个高级问题的实验分析
编写一个类加载器,可以打印出自己类的加载器名称和父类的加载器名称,显示出加载的父子结构关系;**
建立一个Servlet
在doGet方法下打印出
ClassLoaderLoader =this.getClass().getClassLoader();
while(Loader !=null){
out.print(Loader.getParent());
}
在Tomcat加载的时候,就显示出加载类