ClassLoader实战二、ClassLoader双亲委派模型

1、双亲委派模型以及类加载机制前几篇文章都介绍过了,不懂的去看!!!此篇文章直接用用代码来证明是存在双亲委派的。

2、类加载器双亲委派图

ClassLoader实战二、ClassLoader双亲委派模型_第1张图片
Paste_Image.png

自顶向下去加载类,双亲委派的目的就是为了防止重复加载!

3、实战准备
需要在D盘下的tmp文件夹下建立如下的目录

ClassLoader实战二、ClassLoader双亲委派模型_第2张图片
Paste_Image.png
ClassLoader实战二、ClassLoader双亲委派模型_第3张图片
Paste_Image.png
ClassLoader实战二、ClassLoader双亲委派模型_第4张图片
Paste_Image.png
ClassLoader实战二、ClassLoader双亲委派模型_第5张图片
Paste_Image.png

a/Demo.java

package com.dn;
public class Demo {

    public Demo() {
        System.out.println("loader1 init....." + this.getClass().getClassLoader());
    }
}

b/Demo.java

package com.dn;
public class Demo {

    public Demo() {
        System.out.println("loader2 init....." + this.getClass().getClassLoader());
    }
}

c/Demo.java

package com.dn;
public class Demo {

    public Demo() {
        System.out.println("loader3 init....." + this.getClass().getClassLoader());
    }
}

4、自定义类加载器

package jvm.dongnao.demo1;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {

    //类加载器名称
    private String name;
    //类加载路径
    private String path;
    
    public MyClassLoader(String name, String path) {
        super(); //默认的父类加载器
        this.name = name;
        this.path = path;
    }
    
    public MyClassLoader(ClassLoader parent, String name, String path) {
        super(parent); //父类加载器
        this.name = name;
        this.path = path;
    }
    
    /**
     * 获取当前指定路径下的类
     */
    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        byte[] b = readFileToByteArray(name);
        return this.defineClass(name, b, 0, b.length);
    }

    /**
     * 将包名转换成全路径名,比如
     * 
     * temp.a.com.dn.Demo -> D:/temp/a/com/dn/Demo.class
     * 
     * @param name
     * @return
     */
    private byte[] readFileToByteArray(String name) {
        InputStream is = null;
        byte[] rtnData = null;
        //转换
        name = name.replaceAll("\\.", "/");
        //拼接
        String filePath = this.path + name + ".class";
        File file = new File(filePath);
        
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        
        try {
            is = new FileInputStream(file);
            int tmp = 0;
            while((tmp = is.read()) != -1) {
                os.write(tmp);
            }
            
            rtnData = os.toByteArray();
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return rtnData;
    }
}

5、main方法

package jvm.dongnao.demo1;

public class Test1 {
    
    public static void main(String[] args) throws Exception {
        //默认为AppClassLoader
        MyClassLoader loader1 = new MyClassLoader("loader1", "D:/tmp/a/");
        
        //父类加载器指定为loader1
        MyClassLoader loader2 = new MyClassLoader(loader1, "loader2", "D:/tmp/b/");
        
        //父类加载器传null,代表父类是bootstrapClassLoader
        MyClassLoader loader3 = new MyClassLoader(null, "loader3", "D:/tmp/c/");
        
        Class c = loader1.loadClass("com.dn.Demo"); //Demo.class,这里是全路径名称,必须带包名
        
        c.newInstance(); //实例化
    }
}

6、运行结果
6.1、loader1

Class c = loader1.loadClass("com.dn.Demo");
loader1 init.....jvm.dongnao.demo1.MyClassLoader@5c647e05

6.2、loader2

Class c = loader2.loadClass("com.dn.Demo");
loader2 init.....jvm.dongnao.demo1.MyClassLoader@5c647e05

6.2、loader3

Class c = loader3.loadClass("com.dn.Demo");
loader3 init.....jvm.dongnao.demo1.MyClassLoader@6d06d69c

可以发现loader1和loader2的类加载器是同一个,这就验证了loader2选取的类加载器并不是自己的,而是父类的类加载器,因为

new MyClassLoader(loader1, "loader2", "D:/tmp/b/");

第一个参数是传递父类类加载器,将loader1传进去了,所以去loader1加载Demo.class,loader2不会重复加载,双亲委派模型也正是为了防止重复加载而生的。

若有兴趣,欢迎来加入群,【Java初学者学习交流群】:458430385,此群有Java开发人员、UI设计人员和前端工程师。有问必答,共同探讨学习,一起进步!
欢迎关注我的微信公众号【Java码农社区】,会定时推送各种干货:


qrcode_for_gh_577b64e73701_258.jpg

你可能感兴趣的:(ClassLoader实战二、ClassLoader双亲委派模型)