黑马程序员_java之类加载器
类加载器
类加载器:加载类的工具
作用:把class文件加载到内存生成字节码文件
JVM可安装多个类加载器,系统默认3个:BootStrap、ExtClassLoader、AppClassLoader
除BootStrap不是java类,其他类加载也是Java类;BootStrap加载JRE/lib/rt.jar里面的类;ExtClassLoader加载JRE/lib/ext/*.jar
里面的类;AppClassLoader加载classpath指定的所有jar或目录里面的类;我们还可以写自己的类加载器去加载指定的特殊的目录
实例化类加载器对象时,需要指定父级类加载器或默认系统类加载器为其父类加载器
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
//空指针异常,看前面是否存在,有对象字节码肯定存在。看是否有对象
//System.out.println(System.class.getClassLoader().getClass().getName());
System.out.println(System.class.getClassLoader());
ClassLoader loader=ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());
loader=loader.getParent();
}
System.out.println(loader);
类加载器的委托机制
当JVM加载一个类时,到底用哪个类加载器去加载?
首先当前线程的类加载器去加载线程中的第一个类
若类A引用了类B,JVM使用加载A的类加载器去加载类B,还可使用ClassLoader.loaderClass(…)方法指定某个类加载器去加载某个类
每个类加载器加载类时,先委托给其上级类加载器,当所有祖宗类加载器没有加载到类时,返回给发起者加载器,若加载不了,返回
ClassNotFoundException,不再去找发起者类加载器的儿子。
例:能不能写一个java.lang.System类?
通常不行,因为有类加载器委托机制,总是使用java系统提供的System
但是我们可以自己写一个类加载器加载自己写的java.lang.System
自定义类加载器的编写原理分析
模板方法设计模式
父类—>loadClass/findClass/得到class文件的内容转换成字节码-->defineClass
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {}
}
类加密,类加载器解密实例
在cn.cast.day2里有个文件itcastlib,cn.cast.day2里有个类ClassLoaderAttachment
//后期不能使用该类名定义引用变量,编译器无法识别这个类。
public class ClassLoaderAttachment extends Date {
public String toString(){
return "hello,itcast";
}
}
//编写和测试自己编写的解密类加载器
public class MyClassLoader extends ClassLoader {
public static void 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();
}
private static void cypher(InputStream ips,OutputStream ops)throws Exception{
int b=-1;
while((b=ips.read())!=-1){
ops.write(b ^ 0xff );
}
}
private String classDir;
@Override//给子类用,用protected
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classfileName=classDir+"\\"+name.substring(name.lastIndexOf('.')+1)+".class";
try {
FileInputStream fis=new FileInputStream(classfileName);
ByteArrayOutputStream bos=new ByteArrayOutputStream();
cypher(fis,bos);
fis.close();
byte [] bytes=bos.toByteArray();
return defineClass(bytes,0,bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
public MyClassLoader(){ }
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);
/*正常能打印new ClassLoaderAttachment().toString();若用itcastlib文件覆盖ClassLoaderAttachment,则不能正常打印
此时删除classpath里面的ClassLoaderAttachment文件正常打印*/
类加载器的一个高级问题的实验分析
一个web项目的Servlet是由tomcat提供的类加载器加载的,如果我们把一个继承了HttpServlet的Servlet类放到了JRE/lib/ext/目录下,
那么这个Servlet就是由ExtClassLoader类加载器加载了,由于这个Servlet继承了HttpServlet类,所以也需要加载HttpServlet类,但是
此时加载HttpServlet类失败,这是因为类加载器的委托机制,而ExtClassLoader这个类加载器只加载JRE/lib/ext/目录下的类,而
HttpServlet类不位于JRE/lib/ext/目录下,所以加载失败,解决办法是把tomcat提供的servlet.jar包放到JRE/lib/ext/目录下,这时就没
问题了,但是此时加载Servlet类的类加载器是ExtClassLoader这个类加载器了,而不是tomcat提供的类加载器了。
浙公网安备 33010602011771号