类加载器就是加载类的工具
当出现一个类,用到此类的时候,Java虚拟机首先将类字节码加载进内存,通常字节码的原始信息放在硬盘上的classpath指定的目录下。
类加载器作用:将.class文件中的内容加载进内存进行处理,处理完后的结果就是字节码
默认类加载器
Java虚拟机中可安装多个类加载器,系统默认的有三个主要的,每个类负责加载特定位置的类:BootStrap、ExtClassLoader、AppClassLoader
BootStrap--顶级类加载器:
类加载器本身也是Java类,因为它是Java类,本身也需要加载器加载,显然必须有第一个类加载器而不是java类的,这正是BootStrap。它是嵌套在Java虚拟机内核中的,已启动即出现在虚拟机中,是用c++写的一段二进制代码。所以不能通过java程序获取其名字,获得的只能是null。
Java虚拟机中的所有类加载器采用子父关系的树形结构进行组织,在实例化每个类加载器对象或默认采用系统类加载器作为其父级类加载器。
类加载器之间的父子关系和管辖范围图
BootStrap----------------------------JRE/lib/rt.jar
|
ExtClassLoader---------------------JRe/lib/ext/*.jar
|
AppClassLoader---------------------CLASSPATH
|
自定义加载器-------------------------自定义目录
类加载器的委托机制
每个类加载器加载类时,又先委托给其上级类加载器。当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的子类,因为没有getChild方法。
自定义类加载器
自定义的类加载器的必须继承ClassLoader
父类的loadClass方法直接继承
覆盖findClass方法
步骤:
写一个对文件内容进行简单加密的程序。
写了一个自己的类装载器,可实现对加密过的类进行装载和解密。
写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。
自定义类加载器
package abc;
import java.io.*;
/*
* 该类是自定义类加载器,该类定义了类加解密算法*/
public class MyClassLoader extends ClassLoader{
public static void main(String[] args) throws Exception{
String src=args[0]; //主函数可对类进行加密,通过args数组传递
//导入类路径,加密后的文件存放路径
String dest=args[1];
String FileName=src.substring(src.lastIndexOf('\\')+1);
String FilePath=dest+"\\"+FileName;
System.out.println(FilePath);
FileInputStream i= new FileInputStream(src);
FileOutputStream o=new FileOutputStream(FilePath);
cypher(i,o);
i.close();
o.close();
}
private String classDir="itcast";
public static void cypher(InputStream in,OutputStream out)throws Exception{//加密算法,也可用于解密
int a=-1;
while((a=in.read())!=-1){
out.write(a^0xff); //其原理就于运算,所以可以用于加密和解密
}
}
protected Class<?> findClass(String name)throws ClassNotFoundException{ //覆盖ClassLoader的findClass
String classFileName=classDir+"\\"+name+".class";
try{
FileInputStream fis=new FileInputStream(classFileName);
ByteArrayOutputStream bos=new ByteArrayOutputStream();
cypher(fis,bos); //解密
byte[] bytes=bos.toByteArray();
return defineClass(bytes,0,bytes.length);
}
catch(Exception e)
{
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader(){
}
}
使用自定义类加载器
package abc;
import java.util.Date;
public class LoaderTest {
public static void main(String[] args)throws Exception{
//System.out.println(Test1.class.getClassLoader().getClass().getName());//获得Test1对象的类加载器
//System.out.println(System.class.getClassLoader()); //System类加载器是BootStrap加载的,因为BootStrap不是一个类所以无法用。class获取名字
ClassLoader loader=LoaderTest.class.getClassLoader();
while(loader!=null){ //通过while循环获得类加载器的父系
System.out.println(loader.getClass().getName());
loader=loader.getParent();
}
System.out.println(loader);
//System.out.println(new ClassTest().toString());
Class di=new MyClassLoader().loadClass("ClassTest"); //启动自定义的类加载器
Date d=(Date)di.newInstance();
System.out.println(d);
}
}
注意不能将要加载的类放在classpath路径下,如果classpath下使用loadClass方法时,由于类加载器的委托机制AppClassLoader会先加载