JVM学习笔记(3)之命名空间

加载器命名空间

什么是加载器命名空间

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

  • 在同一个命名空间中不会出现类的完整名字(类的包名+类名)相同的两个类

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

代码示例1
package com.zh.classloader;

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

/**
 * @ClassName:MyClassLoader
 * @Description:TODO
 * @author:Jerry
 * @Date 2019/10/21 12:55
 * @Version 1.0.2
 **/
public class MyClassLoader2 extends ClassLoader {

    /**
     * 自定义加载器的名称
     */
    private String classLoaderName;

    /**
     * 字节码文件路径
     */
    private String path;

    //文件扩展名
    private static final String FILE_EXTENSTION = ".class";

    public MyClassLoader2(String classLoaderName) {
        //这里的super加与不加无影响?
        super();
        this.classLoaderName = classLoaderName;
    }

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

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

    @Override
    public String toString() {
        return "MyClassLoader{" +
                "classLoaderName='" + classLoaderName + '\'' +
                '}';
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        System.out.println("findClass");
        byte[] bytes = loadClassData(className);
        return defineClass(className, bytes, 0, bytes.length);
    }

    private byte[] loadClassData(String className) {
        System.out.println("loadClassData");
        className = className.replace(".", File.separator);
        byte[] result = null;
        try (InputStream is = new FileInputStream(new File(path + className + FILE_EXTENSTION)); ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
//            classLoaderName = classLoaderName.replace("\\", ".");
            byte[] buffer = new byte[256];
            int len = 0;
            while ((len = is.read(buffer, 0, buffer.length)) != -1) {
                bos.write(buffer, 0, len);
            }
            result = bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

//    public static void testClassLoader(ClassLoader classLoader) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//        Class<?> clazz = classLoader.loadClass("com.zh.classloader.Demo1");
//        Object o = clazz.newInstance();
//        System.out.println(o);
//    }

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        MyClassLoader2 loader1 = new MyClassLoader2("loader1");
        loader1.setPath("C:\\Users\\Jack\\Desktop\\");
        Class<?> clazz = loader1.loadClass("com.zh.classloader.Demo11");
        System.out.println("classloader:"+clazz.getClassLoader());
        System.out.println("class:" + clazz.hashCode());
        Object o = clazz.newInstance();
        System.out.println(o);

        System.out.println("----------------------");

        //卸载
//        loader1 = null;
//        o = null;
//        clazz = null;
//        System.gc();

        /*loader1 = new MyClassLoader2("loader1");
        loader1.setPath("C:\\Users\\Jack\\Desktop\\");
        clazz = loader1.loadClass("com.zh.classloader.Demo11");
        System.out.println("classloader:"+clazz.getClassLoader());
        System.out.println("class:" + clazz.hashCode());
        o = clazz.newInstance();*/

        MyClassLoader2 loader2 = new MyClassLoader2("loader1");
        loader2.setPath("C:\\Users\\Jack\\Desktop\\");
        Class clazz2 = loader2.loadClass("com.zh.classloader.Demo11");
        System.out.println("classloader:"+clazz2.getClassLoader());
        System.out.println("class:" + clazz2.hashCode());
        o = clazz.newInstance();

        //写在这里 会对loader1,clazz,o之前指向的对象进行回收,此时上面的引用置为null就可以省略了
//        System.gc();
        System.out.println(o);



    }
}

结果打印如下(删除了项目中的Demo11.class):

在这里插入图片描述

发现打印的两个Class对象的hashCode不一致,我们都是用自定义的MyClassLoader加载的呀?
原来类加载器有自己的命名空间,对应了上面的第三条有可能会出现类的完整名字(类的包名+类名)相同的两个类

下面再看一个例子

MySamle.java

package com.zh.classloader;

/**
 * @author Jack
 * @version 1.0
 * @date 2019/10/22 11:24
 */
public class MySample2 {

    public MySample2() {
        System.out.println("MySample2 is loaded by " + this.getClass().getClassLoader());
        new MyCat2();
    }
}

MyCat2.java

package com.zh.classloader;

/**
 * @author Jack
 * @version 1.0
 * @date 2019/10/22 11:24
 */
public class MyCat2 {

    public MyCat2() {
        System.out.println("MyCat2 is loaded by " + this.getClass().getClassLoader());
        System.out.println("From MyCat2:" + MySample2.class);
    }
}

MyTest16.java

package com.zh.classloader;

/**
 * @author Jack
 * @version 1.0
 * @date 2019/10/22 11:27
 */
public class MyTest16 {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader loader = new MyClassLoader("loader1");
        loader.setPath("C:\\Users\\Jack\\Desktop\\");
        Class<?> clazz = loader.loadClass("com.zh.classloader.MySample2");
        Object o = clazz.newInstance();
        System.out.println(o);
    }
}

打印结果(删除了MySample2.class):

在这里插入图片描述

分析下原因:MySample2因为找不到项目下的class文
件所以被MyClassLoader加载,MyCat2找到了class文件被AppClassLoader加载,问题就是在MyCat2类中又调用MySample2.class,此时AppClassLoader的双亲加载器无法加载,返回给AppClassLoader也无法加载,此时去目录下找MySample2.class,那个文件我们已经删掉了,找不到了,所以就报了找不到类路径异常,把那段话注释掉再执行,结果如下

在这里插入图片描述

结论:子加载器可以访问父加载器加载的类,而父加载器加载的类无法访问子加载器加载的类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值