JVM学习之类加载器命名空间

先把知识点总结一下:

1.每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成。

2.在同一个命名空间里,不会出现类的完整名字(包括类的包名)相同的两个类。

3.在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类。

4.子加载器加载的类能加载父加载器加载的类。

5.父加载器加载的类不能加载子加载器锁加载的类。

后面会详细的解释上面所述,请继续关注后面的博客。

先举个例子,里面后面包含四个类:

JavaTest1.java:用来调用加载器加载的主类。

JVMTest1.java:自定义类加载器。

People.java:类中只包含一个无参构造函数,在其中创建了一个Man类的对象。

Man.java:只包含一个无参构造函数。

接下来依次贴代码:

JavaTest1.java

public class JavaTest1 {
    public static void main(String[] args) throws Exception {
        /*loader1加载器*/
        JVMTest1 loader1 = new JVMTest1("loader1");
        loader1.setPath("C:\\Users\\admin\\Desktop\\");
        Class clazz1 = loader1.loadClass("People");
        System.out.println("clazz1的加载器:"+clazz1.getClassLoader());
        Object object = clazz1.newInstance();//实验二

    }
}

JVMTest1.java(不理解代码的可以查阅一下我上篇博文)。

import java.io.*;

public class JVMTest1 extends ClassLoader{

    private String classLoaderName;

    private final  String fileExtension = ".class";

    private String path;

    public void setPath(String path) {
        this.path = path;
    }

    public JVMTest1(String classLoaderName){
        super();
        this.classLoaderName = classLoaderName;
    }

    public JVMTest1(ClassLoader parent, String classLoaderName){
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    @Override
    protected Class findClass(String className){
        byte[] data = this.loadClassData(className);
        System.out.println("运行一次自定义的类加载器"+this.classLoaderName);
        return this.defineClass(className,data,0, data.length);
    }

    private byte[] loadClassData(String className) {
        FileInputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        try {
            className = className.replace(".","\\");
            is = new FileInputStream(new File(this.path+className+ this.fileExtension));
            baos = new ByteArrayOutputStream();
            int ch = 0;
            while((ch = is.read())!=-1){
                baos.write(ch);
            }
            data = baos.toByteArray();

        } catch (Exception ex){
            ex.printStackTrace();
        }finally {
            try{
                is.close();
                baos.close();
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        return data;
    }

}

People.java

public class People {

    public People(){
        System.out.println("People的ClassLoader是:"+ this.getClass().getClassLoader());
        
        new Man();

        //下面这句话是实验六的注释
        //System.out.println("在People中打印Man.class:"+Man.class);
        
    }

}

Man.java

public class Man {

    public Man(){

        System.out.println("Man的ClassLoader是:"+ this.getClass().getClassLoader());
    
         //下面这句话是实验七的注释
        // System.out.println("在Man中打印People.class:"+People.class);
    }
}

实验一:

1.正常运行上述代码,观测运行结果。

JVM学习之类加载器命名空间_第1张图片

2.因为这People类能被系统类加载器加载,Man类在People类中被创建,属于对Man类的主动使用,所以它需要被加载然后被系统类加载了,这没有太大毛病吧?

实验二:

3.将JavaTest1中注释“实验二”那一行注释掉,观测运行结果。

4.为什么写在People.java和Man.java的输出没有运行的?大家看一下,咱们的输出语句都写在哪了?写在构造函数中了,咱们在JavaTest中是只让它加载,没让它运行,所以需要创建一个People类的实例,它才会运行。

实验三:

JVM学习之类加载器命名空间_第2张图片

5.将这两个类移到桌面(这样做的目的是让系统类加载器无法加载,这样咱们的自定义的加载器才发挥作用),观测运行结果。

JVM学习之类加载器命名空间_第3张图片

6.People类和Man类被咱们写的自定义加载器顺利加载。

实验四:

JVM学习之类加载器命名空间_第4张图片

7.将People.class移至桌面,观察结果

JVM学习之类加载器命名空间_第5张图片

8.在加载People.class时,系统类加载器找不到在哪,所以只能由自定义的加载器查找加载。

在加载Man.class时,系统类加载器能找到,所以Man被AppCLassLoader加载。

实验五:

JVM学习之类加载器命名空间_第6张图片

9.这次同样将Man.class移到桌面,观察运行结果。

JVM学习之类加载器命名空间_第7张图片

10.看People类顺利被系统类加载,而Man类却找不到,有些同学可能会疑惑,Man.class系统类加载器加载不了,自定义的类加载器应该能加载啊!大家在仔细看看咱们自定义加载的代码

这是指明People.class在桌面的存放地址,没有Man的,所以程序会报错。

实验六:

JVM学习之类加载器命名空间_第8张图片

11.将People.java移至桌面,把People.java中的注释去掉,观测运行结果。

JVM学习之类加载器命名空间_第9张图片

12.大家看People是自定义类加载器加载的,Man是系统类加载的,而在People中能使用系统类加载的类。

实验七:

JVM学习之类加载器命名空间_第10张图片

13.将People.java移至桌面,把Man.java中的注释去掉,观测运行结果。

JVM学习之类加载器命名空间_第11张图片

14.直接报错,这是因为在子加载器加载的类能加载父加载器加载的类,父加载器加载的类不能加载子加载器锁加载的类。他们存在一种包含关系,关系如下:

JVM学习之类加载器命名空间_第12张图片

不理解之处,欢迎留言讨论。

你可能感兴趣的:(JVM成长之路)