黑马程序员--05.类加载器--05【自定义类加载器】【自定义类加载器举例】
来源:互联网 发布:ubuntu server配置网络 编辑:程序博客网 时间:2024/06/17 19:03
类加载器--5
自定义类加载器
自定义类加载器举例
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1. 自定义类加载器
1). 类加载过程可操控部分 (回顾)
(1). 类加载过程的五个环节
[1]. 一个类被加载的五个阶段:加载、验证、准备、解析和初始化。
[2]. 只有加载阶段中的环节可以被编程人员进行自定义编码
(2). 类加载的加载阶段中具体可控环节
[1]. JVM并没有对这条规范中“从哪里获取此类的二进制流”并且“怎样获取此类的二进制流”做出明确的规定。
[2]. 设计JVM的团队将“通过全类名获取定义此类的二进制字节 (byte)流”这个动作放到JVM的外部去实现,以便让应用程序自己决定如何去获取所需要的类。
[3]. 实现“通过全类名获取定义此类的二进制字节 (byte)流”这个动作代码的模块称为类加载器。
(3). 加载阶段的代码对应
以上加载阶段的代码主要对应的就是java.lang.ClassLoader中的protectedClass<?> loadClass(String name, boolean resolve)方法
2). findClass( )和 loadClass( )方法的关系
(1). findClass() 和loadClass() 出现的时机
[1]. loadClass( )方法出现的版本JDK1.0
[2]. findClass( )方法出现在JDK1.2以后
(2). 引入findClass( )方法的目的
[1]. 类加载器的双亲委托机制是在JDK1.2引入的
[2]. 为了向前兼容并遵循JDK1.2引入的双亲委托机制,这样自定义的类加载器的内容应该写道这个findClass方法中。
[3]. loadClass( )会调用findClass方法。
3). 自定义类加载器
(1). 自定义类加载器的方式
[1]. 继承java.lang.ClassLoader直接覆盖ClassLoader抽象父类中loadClass方法
[2]. 继承java.lang.ClassLoader直接覆盖ClassLoader抽象父类中findClass方法
(2). 两种自定义类加载器的方法的比较
[1]. 如果采取覆盖ClassLoader抽象父类中loadClass方法,就要自己重新编写双亲委托机制,这样势必非常麻烦。
[2]. JDK1.2以后不再提倡直接覆盖loadClass方法,而是应当把自己的类加载逻辑写到findClass方法中。在loadClass()方法的逻辑中如果父类加载失败,就会调用到自定义的findClass方法中的内容。这样就能保证自定义的类加载器符合双亲委托机制。
(3). 自定义类加载器的步骤
[1]. 新建类继承java.lang.ClassLoader抽象类
[2]. 子类重写ClassLoader类的findClass( )方法【通常做法】
findClass原型:protected Class<?> findClass(String name);
[2] 1. 按照自己的逻辑获取字节码数据
[2]2. 将获取到的字节码数据转换为对应的字节数组(byte[])
[2]3. 调用ClassLoader的defineClass(byte[], int off, int length)方法将字节码对应的byte[]数据作为实参传给defineClass方法并将defineClass构建的Class对象返回。【因为findClass方法要求返回Class对象】
(4). 自定义类加载器的位置
[1]. 自定义类加载器本身是java.lang.ClassLoader的直接子类。
【注意】类加载器本身之间的“父子”关系并不是通过extends关键字来表达的,而是通过继承java.lang.ClassLoader的parent属性指向不同类加载器构建类加载器之间的关系。
[2]. 测试自定义类加载器在类加载器的结构中的位置
{1}. 自定义类加载器
public class MyClassLoader extends ClassLoader {}
{2}. 测试代码
public class ClassLoaderTest { publicstatic voidmain(String[] args) { ClassLoader loader =new MyClassLoader(); while(loader!= null){ System.out.println(loader); loader =loader.getParent(); } }}
{3}. 打印结果
从打印结果上来看:直接继承的类加载器MyClassLoader的parent属性直接指向了AppClassLoader的实例
【疑问】这个MyClassLoader的parent是怎么被指定的呢?
分析:由于parent是MyClassLoader从java.lang.ClassLoader类中继承来的一个成员属性,仅仅是在被new之后就能打印出这样的组织关系。推断:一定是在MyClassLoader的无参构造方法中调用的无参父类构造方法【全部隐式调用】指定了这个MyClassLoader的实例的parent属性。
[3]. 自定义类加载器和系统默认类加载器的关联纽带 --- ClassLoader的构造方法
{1}.ClassLoader的无参构造方法
protectedClassLoader() { this(checkCreateClassLoader(),getSystemClassLoader());}
这个方法又调用了另一个重载的构造方法,传入的第二个参数是 getSystemClassLoader()的返回值。这个方法的返回值就是系统类加载器,就是AppClassLoader类加载器。
{2}.ClassLoader的有参构造方法
privateClassLoader(Void unused, ClassLoader parent) { this.parent= parent;//…}
【结论】自定义类加载器在new的过程中会自动调用父类ClassLoader的构造方法。父类的这个构造方法会调用另一个重载的构造方法,这个构造方法将自定义的类加载器的parent属性直接指向了AppClassLoader类加载器。
所以一般情况下定义的类加载器实例的“父类加载器”【指的是parent指向的实例】都是AppClassLoader。
【这样即使使用自定义的类加载器加载类的时候,也一定遵循类加载器的双亲委托机制】
2. 自定义类加载器举例
1). 在所有默认类加载器管辖范围之外放置一个字节码文件
(1). 自定义代码 -----放置到默认类加载器管辖范围之外
[1]. OutClass源代码
public class OuterClass{ publicvoid showOuter(){ System.out.println("Show Outer...");}
[2]. 对OutClass.class打成out.jar包+OutClass.class本身 一同放置到工程的lib文件夹下,如图:
[3]. 将out.jar引入工程ClassLoader中:
【这样所有的默认类加载器无法自动所寻到“D:\BlackHorse_Advanced\ClassLoader\lib”下面的class文件】
2). 测试 + 自定义类加载器
(1). OuterClass测试代码1 ----传统方式
import userDefinedClasses.OuterClass; public static void loadWithoutLoader(){ OuterClassouterClass =new OuterClass(); outerClass.showOuter();}
main:
loadWithoutLoader();
[1]. 编译正确
[2]. 运行抛出异常
【分析】
当前线程类加载器是AppClassLoader并采用委托机制去搜索指定的类,但是没有找到OuterClass字节码。所以抛出ClassNotFoundException异常
(2). OuterClass测试代码2 ----反射方式
public static void loadWithoutLoaderII() throws ClassNotFoundException,InstantiationException, IllegalAccessException, NoSuchMethodException,SecurityException, IllegalArgumentException, InvocationTargetException{ Classclazz =Class.forName("userDefinedClasses.OuterClass"); Objectobj =clazz.newInstance(); Methodmethod =clazz.getMethod("showOuter", null); method.invoke(obj,null);}
[1]. 编译同样正确
[2]. 运行抛出异常
【分析】
采用Class.forName方法并传入"userDefinedClasses.OuterClass"。但是由于当前线程的类加载器是AppClassLoader,所以还是没有办法根据这个字符串找到OuterClass的字节码。所以抛出异常
(3). 解决办法
由于系统的默认类加载器不能搜索到特定位置的类,所以必须重新自定义类加载器对这个指定目录的类进行加载。通过loadClass方法加载之后会获取这个OuterClass类的Class对象。这样就可以通过反射来操作这个OuterClass类的对象
(4) 自定义类加载器加载指定目录的字节码文件
[1]. 自定义类加载器MyClassLoader
import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException; public class MyClassLoader extendsClassLoader { @Override protected Class<?> findClass(Stringname) throws ClassNotFoundException { try { FileInputStreamfis =new FileInputStream(new File(name +".class")); byte[] classBytes =new byte[1024]; int length =fis.read(classBytes, 0, classBytes.length); return defineClass(classBytes, 0,length); }catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { e.printStackTrace(); } return null; } }
[2]. 通过MyClassLoader加载D:\BlackHorse_Advanced\ClassLoader\lib下面的OuterClass
public static void loadWithMyClassLoader(StringclassName, ClassLoader classLoader) throws ClassNotFoundException,InstantiationException, IllegalAccessException, NoSuchMethodException,SecurityException, IllegalArgumentException, InvocationTargetException{ Classclazz =classLoader.loadClass("D:\\BlackHorse_Advanced\\ClassLoader\\lib\\"+ className); Objectobj =clazz.newInstance(); Methodmethod =clazz.getMethod("showOuter", null); method.invoke(obj,null);}
main中:
ClassLoader myLoader =new MyClassLoader();String className ="OuterClass";loadWithMyClassLoader(className, myLoader);
[3]. 编译通过,打印结果如下
Show Outer...
3). 总结
(1). 自定义类加载器需要显式调用loadClass方法
由于自定义类加载器并不是系统类加载器的一部分,所以必须显式调用loadClass才能加载指定的类。
【总结】无论是默认类加载器的隐式调用loadClass加载各自范围的类
还是自定义类加载器的显式调用loadClass加载指定位置的类,目的只有一个,将这个类的字节码文件变成内存中的Class对象。
(2). 获取Class类对象的第四种方式
自定义类加载器实例化之后,调用这个类加载器的loadClass方法。这样就可以直接获取这个指定位置【未必在默认类加载器的目录下】的类。
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
- 黑马程序员--05.类加载器--05【自定义类加载器】【自定义类加载器举例】
- 黑马程序员-Java自定义类加载器
- 黑马程序员——【Java】【高新技术】自定义类加载器
- 黑马程序员——自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- ecshop优化5-多语言切换中英文切换
- JRTPLIB使用实例
- 阿里智勇大闯关通关方法
- ios下拉刷新,基于EGOTableViewPullRefresh框架实现
- hdu2919
- 黑马程序员--05.类加载器--05【自定义类加载器】【自定义类加载器举例】
- hdu 2988 Dark roads
- 外面的天是黑色的
- ecshop优化6-后台左侧导航中增加新菜单
- JBPM4 事件监听器(listener)获取ProcessEngine对象
- Java--Dom解析XML文件
- VS解决方案文件格式说明
- 用Python做Logistic回归
- 贝叶斯网络(一)