一、类加载器定义:
顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。
就是将类的class文件加载进内存的工具,java虚拟机要运行类中的数据,必须先将他的
二进制字节码对象加载进内存,就要要对类加载器。
二、系统中已经存在3个类加载器,他们各自有自己的加载任务(或是范围):
根-加载器(bootstrapClassLoader):它用来加载 Java 的核心库,是用c++编写的一段代码,对于你java是不可见的
扩展类加载器(extClassLoader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录
系统类加载器(AppClassLoader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类
三、java虚拟机要加载一个类时,到底由谁去加载呢?
1、首先当前线程的类加载器去加载线程中的第一个类
2、如果类A引用了类B ,java虚拟机会使用加载A的加载器去加载B
3、还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类
类加载器的委托机制:
1、每个类的字节码成功加载进内存,至始至终是由加载器中的一个为其加载的、
如果内存中已经存在加载过的字节码,避免重复加载,直接调用即可
2、比如说,当一个类A 为发起者,需要AppClassLoader加载,-------->但是它会委托它福加载器:ExtClassLoader----->委托给父类加载器:BootStrap
再从BootStrap开始往下查找自己负责的范围是否有.class文件,有就加载成功,没有就继续往下,直到发起者
public class Test2 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ClassLoader loader=Test2.class.getClassLoader();
System.out.println("本类的加载器:"+loader.getClass().getName());
loader=loader.getParent();
System.out.println("父类加载器:"+loader.getClass().getName());
loader=loader.getParent();
//根加载器的getName()为null
System.out.println("根--加载器:"+loader);
}
}
打印结果:
本类的加载器:sun.misc.Launcher$AppClassLoader
父类加载器:sun.misc.Launcher$ExtClassLoader
根--加载器:null
四、自定义类加载器
1、自定义类加载器必须继承ClassLoader抽象类,成为类加载器中的体系中,类加载的方式可以按照委托方式进行
2、如果上面的类加载器没有加载成功,本类中只要覆盖findClass()方法,就可以用自己的类加载器进行加载
这种方式:应用了模板方法设计模式。
五、模拟自定义类加载器用于加载Data类
import java.util.Date;
//定义一个测试类,继承Date,便于使用时加载
public class ClassLoaderAttachment extends Date{
//复写toString方法
public String toString(){
return "Hello World!";
}
}
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
//自定义类加载器,模拟类加载器使用自身加载器的过程
public class MyClassLoader extends ClassLoader{
public static void main(String[] args) throws Exception {
String srcPath=args[0];//文件源
String destDir=args[1];//文件目的
InputStream ips=new FileInputStream(srcPath);
String destFileName=srcPath.substring(srcPath.lastIndexOf("\\")+1);
String destFilePath=destDir+"\\"+destFileName;
OutputStream ops=new FileOutputStream(destFilePath);
cypher(ips,ops);//加密class字节码
ips.close();
ops.close();
}
//加密方法
private static void cypher(InputStream ips,OutputStream ops) throws Exception{
int b=-1;
while((b=ips.read())!=-1){
ops.write(b^0xff);
}
}
@Override
//覆盖ClassLoader的findClass方法
protected Class<?> findClass(String name) throws ClassNotFoundException {
name=name.substring(name.lastIndexOf(".")+1);
String classFileName=classDir+"\\"+name+".class";//获取class文件名
InputStream ips=null;
try {
ips=new FileInputStream(classFileName);
ByteArrayOutputStream bos=new ByteArrayOutputStream();//定义字节数组流
cypher(ips,bos);//解密
ips.close();
byte[] buf=bos.toByteArray();//取出字节数组流中的数据
return defineClass(null, buf,0,buf.length);//加载进内存
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return null;
//return super.findClass(name);
}
private String classDir;
public MyClassLoader(){}
//带参数的构造函数
public MyClassLoader(String classDir){
this.classDir=classDir;
}
}
import java.util.Date;
public class ClassLoaderDemo {
public static void main(String[] args) throws Exception {
//将用自定义的类加载器加载.class文件
Class clazz=new MyClassLoader("itheimalib").loadClass("cn.itheima.demo.ClassLoaderAttachment");
Date d1 = (Date)clazz.newInstance();//获取Class类的实例对象
System.out.println(d1);
}
}