Java--类加载器

来源:互联网 发布:打水软件是什么 编辑:程序博客网 时间:2024/06/06 06:38

Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:
BootStrap,  ExtClassLoader,  AppClassLoader

类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是java类,这正是BootStrap。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。

测试代码如下:
       @Test//查看系统中的3个类加载器public void t1() throws Exception{Class c = Class.forName("cn.hucu.classLoader.Demo1");    ClassLoader loader = c.getClassLoader();    System.out.println("1>>>"+loader); //AppClassLoader  ---java        ClassLoader loader2 =loader.getClass().getClassLoader();    System.out.println(loader2);//null ---BootStrap         ClassLoader loader3 = loader2.getParent();    System.out.println("3>>>>"+ loader3); //ExtClassLoader  ---java        ClassLoader loader4 = loader3.getParent();    System.out.println("4>>>>"+ loader4);//null ---BootStrap  ---C语言        }

测试结果如下:

1>>>sun.misc.Launcher$AppClassLoader@425224ee2>>>>null3>>>>sun.misc.Launcher$ExtClassLoader@1ef6a7464>>>>null

☆类加载器的委托机制:通过API认识ClassLoader类
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
       首先当前线程的类加载器去加载线程中的第一个类。如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
      每个类加载器加载类时,又先委托给其上级类加载器。当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的aa.jar包中后,运行结果为ExtClassLoader的原因。
package cn.hucu.classLoader;public class Person {private String name = "Jack";private int age = 20;public Person(String name, int age) {this.name = name;this.age = age;}public Person() {}@Overridepublic String toString() {return name + ", " + age;}}
@Test //系统类加载器的父类委托机制    public void t2() throws Exception{Class clazz = Class.forName("cn.hucu.classLoader.Person");Constructor con = clazz.getConstructor(new Class[]{String.class,int.class});Object obj = con.newInstance("Jack",26);Method ms = clazz.getMethod("toString", null);Object str = ms.invoke(obj, null);System.out.println(str);}

测试结果 如下:
Jack, 26

首先,通过一个对比来来了解远程加载类:
/** * 很简单 * @throws Exception */@Testpublic void loadLocalClass() throws Exception {Class c = Class.forName("cn.hucu.classLoader.Person");Object obj = c.newInstance();  System.out.println(obj);} //这种方式不行的,---反模式@Test// 演示加载远程类(d盘a目录下)public void loadRemoteClass() throws Exception {Class c = Class.forName("d:/a/cn/hncu/classLoader/Person.class");Object obj = c.newInstance();System.out.println(obj);}

那如何通过自制类加载器加载远程类呢?

当然首先,先自制一个类加载器,
1)虚拟机的核心是通过类加载器来加载.class文件,然后进行相应的解析执行。那么我们可以自己做类加载器,手动加载需要的.class以进行解析执行,从而扩展虚拟机的功能。
2)应用程序需要实现 ClassLoader 的子类,以扩展 Java 虚拟机动态加载类的方式。
3)网络类加载器子类必须定义方法 findClass 和 loadClassData,以实现从网络加载类。下载组成该类的字节后,它应该使用方法 defineClass 来创建类实例。

附代码如下:
package cn.hucu.classLoader;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.InputStream;/* 写自定义类加载器,最关键的是调用ClassLoader类中的一个方法:  defineClass(),该方法能创建出一个Class对象。 为了实现上面的功能,我们得做两步: 1) 继承ClassLoader 2) 写一个方法,调用defineClass(),以返回出Class对象 */public class MyClassLoader extends ClassLoader {public Class load(String fileName) throws Exception {File file = new File(fileName);if (!file.exists()) {throw new RuntimeException("字节码文件不存在!");}// 字节码文件过大,可以考虑用内存流byte b[] = new byte[1024];InputStream in = new FileInputStream(file);ByteArrayOutputStream bout = new ByteArrayOutputStream();//数组内存流int len = 0;while ((len = in.read(b)) != -1) {bout.write(b, 0, len);}bout.close();byte b2[] = bout.toByteArray();Class clazz = defineClass(null, b2, 0, b2.length);return clazz;}}
// App系统类加载器只会加载classpath下的所有类,如果要加载其它地方(如本地磁盘、网络等)则必须自己写自定义类加载器@Test// 用自制类加载器,加载远程类(d盘a目录下)public void tesMyClassLoader() throws Exception {MyClassLoader loader = new MyClassLoader();Class c = loader.load("d:/a/Person.class");Object obj = c.newInstance();System.out.println(obj);System.out.println("loader:" + c.getClassLoader());// MyClassLoader}

那如果自制类加载器加载的类 和 AppClassLoader加载的类呢?

        @Test// 知识点: 我们自己类加载器加载的类 和 AppClassLoader加载的类 在不同的空间, 不能相互转换public void tesMyClassLoader2() throws Exception {Class c = Class.forName("cn.hncu.classLoader.Person");// AppClassLoaderPerson p1 = (Person) c.newInstance();System.out.println(p1.toString());       /** * 以下是用自己做的一个类加载器来加载Person类 */MyClassLoader loader = new MyClassLoader();Class c2 = loader.load("d:/a/Person.class");Person p2 = (Person) c2.newInstance(); // 出异常!!!!System.out.println(p2.toString());}
类加载器必不可少!!



原创粉丝点击