学习ClassLoader和自定义ClassLoader的使用

这几天在看ClassLoader,推荐一篇讲ClassLoader的文章,地址:http://longdick.iteye.com/blog/442213,然后又看了下《深入java虚拟机》里的ClassLoader章节,下面就随便说点,主要还是以练习代码为主。

1.对于任意一个类,由加载它的ClassLoader和它本身决定了在java虚拟机中的唯一性。
也就是说比较2个类,只有它们都是由同一个ClassLoader加载,那么比较才有意义。否则,即使是同一个类文件,如果加载它们的ClassLoader不同,那么这2个类必定不相等。

2.类加载的双亲委派机制是可以破坏的,通过改变CallClassLoader和ContextClassLoader。
被当前类引用的类的加载也是由加载当前类的ClassLoader加载,子线程的ContextClassLoader是由父线程的ContextClassLoader派生出来。

3.扩展ClassLoader一般应该建议重写findClass(String)方法。
原因是自定义的ClassLoader只专注于加载自己的Class,在加载自己的Class的过程中,又会先加载父类Object,Object类是由启动类加载器加载的,默认可以直接由loadClass(String)执行,这么做可以用ClassLoader来选择性加载某一些类做隔离。

贴代码

自定义ClassLoader,重写loadClass()。

@Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        try {
            String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
            InputStream is = getClass().getResourceAsStream(fileName);
            if (is == null) {
                return super.loadClass(name);
            }
            byte[] b = new byte[is.available()];
            is.read(b);
            return defineClass(name, b, 0, b.length);
        }
        catch (Exception e) {
            throw new ClassNotFoundException(name);
        }
    }

测试类



    public void say() {
        System.out.println("hello world");
    }


    public static void main(String[] args) throws Exception {
        // 打印java虚拟机的ClassLoader
        System.out.println(Thread.currentThread().getContextClassLoader());
        System.out.println(Thread.currentThread().getContextClassLoader().getParent());
        System.out.println(Thread.currentThread().getContextClassLoader().getParent().getParent());
        // 自定义ClassLoader
        ClassLoader myLoader = new MyClassLoader();
        Class<?> clazz = myLoader.loadClass("com.zoo.classloader.ClassLoaderTest");
        Object obj = clazz.newInstance();
        // 打印自定义ClassLoader加载的Class对象
        System.out.println(obj.getClass());
        // 打印被加载的Class对象是由哪个ClassLoader加载的
        System.out.println(obj.getClass().getClassLoader());
        /*
         * 对于任意一个类,由加载它的ClassLoader和它本身决定了在jvm虚拟机中的唯一性。
         * 也就是说比较2个类,只有它们都是由同一个ClassLoader加载,那么比较才有意义。
         * 否则,即使是同一个类文件,只要加载它们的ClassLoader不同,那么这2个类必定不相等。
         */
        System.out.println(obj instanceof com.zoo.classloader.ClassLoaderTest);
        // 由自定义ClassLoader加载后,在程序里运行。
        Method method = clazz.getDeclaredMethod("say", new Class<?>[] {});
        method.invoke(obj, new Object[] {});
        // 获取当前上下文的ClassLoader
        System.out.println(Thread.currentThread().getContextClassLoader());
        // 改变上下文的ClassLoader
        Thread.currentThread().setContextClassLoader(myLoader);
        // 获取当前上下文的ClassLoader
        System.out.println(Thread.currentThread().getContextClassLoader());
        // 改变当前上下文的ClassLoader可以改变在当前线程派生出的子线程的上下文ClassLoader
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread t2 = Thread.currentThread();
                System.out.println(t2.getName() + ":" + t2.getContextClassLoader());
            }
        });
        t.start();
        Thread.sleep(3000);
        // 获取CallClassLoader
        Class<?> clz = Class.forName("com.zoo.classloader.ClassLoaderTest");
        System.out.println(clz);
        System.out.println(clz.getClassLoader());
        System.out.println(clz.getClass());
        System.out.println(clz.getClass().getClassLoader());
    }

执行结果:
sun.misc.Launcher$AppClassLoader@addbf1 ==> 当前上下文类加载器是应用类加载器
sun.misc.Launcher$ExtClassLoader@42e816 ==> 应用类加载器的父类加载器是扩展类加载器
null ==> 扩展类加载器的父类加载器是启动类加载器,因为启动类加载器是C++编写,java里获取不到
class com.zoo.classloader.ClassLoaderTest ==> 自定义类加载器加载进行加载
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 通过Class对象获取它的类加载器
false ==> 同一个Class,不同的类加载器加载,那么Class不相等
hello world ==> 被自定义类加载器加载的Class在程序中执行
sun.misc.Launcher$AppClassLoader@addbf1 ==> 获取当前上下文类加载器
com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 设置上下文类加载器
Thread-0:com.zoo.classloader.MyClassLoader@1fb8ee3 ==> 派生出来的子线程的类加载器
class com.zoo.classloader.ClassLoaderTest ==> 默认加载类
sun.misc.Launcher$AppClassLoader@addbf1 ==> 默认加载用的是应用类加载器
class java.lang.Class ==> 获取Class
null ==> 获取类加载器,由于是启动类加载器,所以为null,java基础类是由启动类加载器加载。

练习代码看这里

此条目发表在编程语言分类目录,贴了标签。将固定链接加入收藏夹。