java类加载器
2016-06-04 16:12
441 查看
详细内容请参考原链接:http://blog.csdn.net/jiangwei0910410003/article/details/17733153,此文只是转载
1、新建一个java对象,JVM需要把这个对象对应的字节码加载到内存中,字节码就是对应的Class文件,自己java工程,其在bin目录下,也就是把.class文件的内容读到内存中;
2、Java虚拟机中类加载器
java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:BootStarp,ExtClassLoader,AppClassLoader,其中BootStrap是用C++写的,内置在JVM内核中,也就是所有类的鼻祖;
之所以有不同的类加载器,是因为不同的类加载器加载不同目录下的.class文件;采用委托的方式,向父类请求加载,每个父类去指定的目录加载,成功结束;不成功再顺序往下请求,直到加载成功,所有加载类都没有成功,则抛出ClassNotFoundException异常
类加载器的委托机制:
当Java虚拟机要加载第一个类的时候,到底派出哪个类加载器去加载呢?
(1). 首先当前线程的类加载器去加载线程中的第一个类(当前线程的类加载器:Thread类中有一个get/setContextClassLoader(ClassLoader cl);方法,可以获取/指定本线程中的类加载器)
(2). 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B
(3). 还可以直接调用ClassLoader.loadClass(String className)方法来指定某个类加载器去加载某个类
每个类加载器加载类时,又先委托给其上级类加载器当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则会抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild()方法。例如:如上图所示: MyClassLoader->AppClassLoader->Ext::ClassLoader->BootStrap.自定定义的MyClassLoader1首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrap,这时候BootStrap就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束;
因为BootStrap不是java类,所以打印出来的class是null
3、System类、list、map这样的类都在rt.jar中,需要由BootStrap来加载,所以其加载类打印出来为null;也就是说,谁加载的你当前这个jar,那么它的classloader就是谁,比方说把MyClassLoader打包成jar,然后放到jre1.x/lib/ext目录下,那么它的第一个加载类就会变成sun.misc.Launcher$ExtClassLoader
4、可以定义自己的类加载器,需要将自己的类加载器挂载到系统类加载器树上,在构造函数时可以指定类加载器
5、定义了自己的类加载器,每个加载器都有自己的搜索目录,自己类加载器有自己的目录,而它的父类也有自己的目录,所以类名字,如果父类找到了,那么子类不会继续查找了;所以即使使用了自己的类加载器,如果是父类找到了对应名称的类class,其加载器就是父类
编写自己的加载类:
先写一个测试类:
编译后,在bin目录会生成它的.class文件,然后在项目目录(bin的上一层把)创建一个”temp_folder”目录,用于生成加密后的.class文件
写一个main函数,把上面的ClassLoaderAttachment.class文件加密
会在temp_folder目录下生成一个新的ClassLoaderAttachment.class,此目录和class文件名用于创建自己的ClassLoader时,作为参数传入
下面编写自己的加载类
其中的main函数就是刚才加密class文件用的
然后写测试myDexLoader的测试方法:
也是一个main方法,执行即可;把bin目录下的ClassLoaderAttachment.class删除后,最后得到的加载类链表是:
由于temp_folder目录下是加了密的class文件,所以只能被我们自己能解密的ClassLoader进行加载执行,其实就是返回真是的字节码;
如果把它拷贝到bin目录,那么系统的sun.misc.Launcher$AppClassLoader会进行加载此class,但是文件是加了密的,所以其解析错误,则会报执行错误
1、新建一个java对象,JVM需要把这个对象对应的字节码加载到内存中,字节码就是对应的Class文件,自己java工程,其在bin目录下,也就是把.class文件的内容读到内存中;
2、Java虚拟机中类加载器
java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:BootStarp,ExtClassLoader,AppClassLoader,其中BootStrap是用C++写的,内置在JVM内核中,也就是所有类的鼻祖;
之所以有不同的类加载器,是因为不同的类加载器加载不同目录下的.class文件;采用委托的方式,向父类请求加载,每个父类去指定的目录加载,成功结束;不成功再顺序往下请求,直到加载成功,所有加载类都没有成功,则抛出ClassNotFoundException异常
类加载器的委托机制:
当Java虚拟机要加载第一个类的时候,到底派出哪个类加载器去加载呢?
(1). 首先当前线程的类加载器去加载线程中的第一个类(当前线程的类加载器:Thread类中有一个get/setContextClassLoader(ClassLoader cl);方法,可以获取/指定本线程中的类加载器)
(2). 如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B
(3). 还可以直接调用ClassLoader.loadClass(String className)方法来指定某个类加载器去加载某个类
每个类加载器加载类时,又先委托给其上级类加载器当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则会抛出ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild()方法。例如:如上图所示: MyClassLoader->AppClassLoader->Ext::ClassLoader->BootStrap.自定定义的MyClassLoader1首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrap,这时候BootStrap就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束;
因为BootStrap不是java类,所以打印出来的class是null
3、System类、list、map这样的类都在rt.jar中,需要由BootStrap来加载,所以其加载类打印出来为null;也就是说,谁加载的你当前这个jar,那么它的classloader就是谁,比方说把MyClassLoader打包成jar,然后放到jre1.x/lib/ext目录下,那么它的第一个加载类就会变成sun.misc.Launcher$ExtClassLoader
4、可以定义自己的类加载器,需要将自己的类加载器挂载到系统类加载器树上,在构造函数时可以指定类加载器
5、定义了自己的类加载器,每个加载器都有自己的搜索目录,自己类加载器有自己的目录,而它的父类也有自己的目录,所以类名字,如果父类找到了,那么子类不会继续查找了;所以即使使用了自己的类加载器,如果是父类找到了对应名称的类class,其加载器就是父类
编写自己的加载类:
先写一个测试类:
public class ClassLoaderAttachment extends Date{ private static final long serialVersionUID = 98398492342L; @Override public String toString() { // TODO Auto-generated method stub return "Hell ClassLoaderAttachment"; } }
编译后,在bin目录会生成它的.class文件,然后在项目目录(bin的上一层把)创建一个”temp_folder”目录,用于生成加密后的.class文件
写一个main函数,把上面的ClassLoaderAttachment.class文件加密
//测试,先将ClassLoaderAttachment.class文件加密写到工程的class_temp目录下 public static void main(String[] args) throws Exception{ //配置运行参数 String srcPath = args[0];//ClassLoaderAttachment.class原路径 String desPath = args[1];//ClassLoaderAttachment.class输出的路径 String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1); String desPathFile = desPath + "/" + desFileName; FileInputStream fis = new FileInputStream(srcPath); FileOutputStream fos = new FileOutputStream(desPathFile); //将class进行加密 encodeAndDecode(fis,fos); fis.close(); fos.close(); } /** * 加密和解密算法 * @param is * @param os * @throws Exception */ private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{ int bytes = -1; while((bytes = is.read())!= -1){ bytes = bytes ^ 0xff;//和0xff进行异或处理 os.write(bytes); } }
会在temp_folder目录下生成一个新的ClassLoaderAttachment.class,此目录和class文件名用于创建自己的ClassLoader时,作为参数传入
下面编写自己的加载类
public class myLoadClass extends ClassLoader{ //需要加载类.class文件的目录 private String classDir; //无参的构造方法,用于class.newInstance()构造对象使用 public myLoadClass(){ } public myLoadClass(String classDir){ this.classDir = classDir; } @SuppressWarnings("deprecation") @Override protected Class<?> findClass(String name) throws ClassNotFoundException { //class文件的路径 String classPathFile = classDir + "/" + name + ".class"; try { //将class文件进行解密 FileInputStream fis = new FileInputStream(classPathFile); ByteArrayOutputStream bos = new ByteArrayOutputStream(); encodeAndDecode(fis,bos); byte[] classByte = bos.toByteArray(); //将字节流变成一个class return defineClass(classByte,0,classByte.length); } catch (Exception e) { e.printStackTrace(); } return super.findClass(name); } //测试,先将ClassLoaderAttachment.class文件加密写到工程的class_temp目录下 public static void main(String[] args) throws Exception{ //配置运行参数 String srcPath = args[0];//ClassLoaderAttachment.class原路径 String desPath = args[1];//ClassLoaderAttachment.class输出的路径 String desFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1); String desPathFile = desPath + "/" + desFileName; FileInputStream fis = new FileInputStream(srcPath); FileOutputStream fos = new FileOutputStream(desPathFile); //将class进行加密 encodeAndDecode(fis,fos); fis.close(); fos.close(); } /** * 加密和解密算法 * @param is * @param os * @throws Exception */ private static void encodeAndDecode(InputStream is,OutputStream os) throws Exception{ int bytes = -1; while((bytes = is.read())!= -1){ bytes = bytes ^ 0xff;//和0xff进行异或处理 os.write(bytes); } } }
其中的main函数就是刚才加密class文件用的
然后写测试myDexLoader的测试方法:
public class myMain { public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException{ //输出ClassLoaderText的类加载器名称 System.out.println("ClassLoaderText类的加载器的名称:"+myMain.class.getClassLoader().getClass().getName()); System.out.println("System类的加载器的名称:"+System.class.getClassLoader()); System.out.println("List类的加载器的名称:"+List.class.getClassLoader()); ClassLoader cl = myMain.class.getClassLoader(); while(cl != null){ System.out.print(cl.getClass().getName()+"->"); cl = cl.getParent(); } System.out.println(cl); /** * 如果在bin目录下有ClassLoaderAttachment.class,那么用的是系统的appClassLader,因为父类已经找到了 * 如果想自己的类加载,那么需要把bin目录下对应的.class文件删掉,使父类找不到,这样自己的加载类才有机会去执行 * **/ Class myClass = new myLoadClass("temp_folder").loadClass("ClassLoaderAttachment"); Date myDate = (Date)myClass.newInstance(); System.out.println("classLoader is :"+myDate.getClass().getClassLoader().getClass().getName()); System.out.println("date result is "+myDate.toString()); ClassLoader cll = myDate.getClass().getClassLoader(); while(cll != null){ System.out.print(cll.getClass().getName()+"->"); cll = cll.getParent(); } System.out.println(cll); } }
也是一个main方法,执行即可;把bin目录下的ClassLoaderAttachment.class删除后,最后得到的加载类链表是:
由于temp_folder目录下是加了密的class文件,所以只能被我们自己能解密的ClassLoader进行加载执行,其实就是返回真是的字节码;
如果把它拷贝到bin目录,那么系统的sun.misc.Launcher$AppClassLoader会进行加载此class,但是文件是加了密的,所以其解析错误,则会报执行错误
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树