类加载范围

2019-02-18  本文已影响1人  shz_Minato

类加载范围

 系统类加载器加载的为项目中的class
 每个类加载器都有自己的命名空间,命名空间由该加载器及所有的父加载器所加载的类组成
 如果被加载的类中引用着其他的类,比如

public class Sample {
    public Sample() {
        System.out.println("Sample is loaded by: " + this.getClass().getClassLoader());

        new Cat();
    }

}

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

Sample类的构造方法中构造了Cat类(是对Cat的主动使用,会引起Cat的加载流程)。

为了解释方便Sample类为 宿主类,Cat类为被引用类。

加载Sample类(宿主类)的类加载器,就是Cat类(被引用类)的类加载流程的双亲委托的起点。
 比如Sample类的类加载器为系统类加载器,那么Cat类加载器为系统类加载器。如果Sample类的类加载器为自定义类加载器,那么Cat类加载流程的起点就是自定义类加载器。

案例分析

 被加载的类

public class Sample {
    public Sample() {
        System.out.println("Sample is loaded by: " + this.getClass().getClassLoader());

        new Cat();
    }

}

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

 自定义的类加载器(从指定的路径加载class)

public class MyClassLoader extends ClassLoader {
    private String classLoaderName;
    private String fileExtension = ".class";
    private String path;

    //以系统类加载器为父加载器
    public MyClassLoader(String classLoaderName) {
        super();
        this.classLoaderName = classLoaderName;
    }

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

    //指定父加载器
    public MyClassLoader(ClassLoader parent, String classLoaderName) {
        super(parent);
        this.classLoaderName = classLoaderName;
    }

    
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("findClass invoke: " + name);
        System.out.println("class loader name: " + classLoaderName);
        byte[] classData = getClassData(name);
        return defineClass(null, classData, 0, classData.length);
    }

    public byte[] getClassData(String className) {
        byte[] bytes = null;
        InputStream is = null;
        ByteArrayOutputStream bos = null;

        className = className.replaceAll("\\.", "\\\\");
        try {

            File file = new File(this.path, className + fileExtension);

            is = new FileInputStream(file);
            bos = new ByteArrayOutputStream();

            int ch = 0;
            while ((ch = is.read()) != -1) {
                bos.write(ch);
            }

            bytes = bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }
}

 分情况执行
  加载指定路径的Sample类,Sample类和Cat类都不在项目中

public class Test {
    public static void main(String[] args) throws Exception {
       MyClassLoader classLoader = new MyClassLoader("loader1");
       
        classLoader.setPath("路径");
        //加载Sample类
        Class<?> loadClass = classLoader.loadClass("com.minato.jvm.chapter19.Sample");
    
        System.out.println("-----");
        //构造Sample实例(执行构造方法)
        Object instance = loadClass.newInstance();
   }
}

//指定结果
findClass invoke: com.minato.jvm.chapter19.Sample
class loader name: loader1  //加载Sample时,执行的findClass方法
-----
Sample is loaded by: com.minato.jvm.chapter19.MyClassLoader@677327b6 //执行Sample的构造方法
findClass invoke: com.minato.jvm.chapter19.Cat
class loader name: loader1 //加载Cat时,执行的findClass方法
Cat is loaded by: com.minato.jvm.chapter19.MyClassLoader@677327b6 //Cat的类加载器 

//以上说明 Sample类和Cat类的加载器为同一个类加载器

  加载指定路径的Sample类,但Sample类的class文件仍存在项目中,Cat类不在项目中


public class Test {
  
    public static void main(String[] args) throws Exception {
        //第一处
        MyClassLoader classLoader = new MyClassLoader("loader1");
        classLoader.setPath("C:\\Users\\shz\\Desktop\\");
        //第二处
        Class<?> loadClass = classLoader.loadClass("com.minato.jvm.chapter19.Sample");

        System.out.println("-----");
        //第三处
        Object instance = loadClass.newInstance();
   }
}

//执行结果
Sample is loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2 //执行Sample加载的类加载器
Caused by: java.lang.ClassNotFoundException: com.minato.jvm.chapter19.Cat //报错

//结果分析
第一处:classLoader的的父加载器为系统类加载器就是AppClassLoader
第二处:执行双亲委托的类加载流程,首先使用父加载器(系统类加载器)去加载,
系统类加载器会检查此项目中是否存在class文件,因为Sample的class文件存在项目中,
因此Sample的类加载器是系统类加载器。
报错原因:
    因为Sample类的加载器为系统类加载器,因此系统类加载器加载Cat类,项目中不存在Cat的class文件,因此会报错。

  加载指定路径的Sample类,但Sample类的class文件不在项目中,Cat类在项目中

public class Test {
   
    public static void main(String[] args) throws Exception {
        //classloader的父加载器为系统类加载器
        MyClassLoader classLoader = new MyClassLoader("loader1");
        classLoader.setPath("C:\\Users\\shz\\Desktop\\");
        Class<?> loadClass = classLoader.loadClass("com.minato.jvm.chapter19.Sample");

        System.out.println("-----");
        Object instance = loadClass.newInstance();
   }
}

//执行结果
findClass invoke: com.minato.jvm.chapter19.Sample
class loader name: loader1 //加载Sample时,执行的findClass方法
-----
Sample is loaded by: com.minato.jvm.chapter19.MyClassLoader@677327b6 //Sample的类加载器-----自定义类加载
Cat is loaded by: sun.misc.Launcher$AppClassLoader@18b4aac2 //Cat的类加载器----·系统类加载器

//结果分析
classloader加载Sample类,按照双亲委托模型,层层委托,都不可加载,因此自定义类加载
执行Sample的加载。 
加载Cat时,使用宿主的类加载加载Cat,因为项目中存在Cat的类class文件,按照双亲委托模型,
自定义类加载类加载器委托至系统类加载器,因为项目中存在Cat的class文件,因此使用系统类加载器去加载Cat文件。

加载范围的性质

 结合命名空间的性质,空间中承载的是按照双亲委托模型可加载的类。
  子加载器所加载的类能够 访问 父加载器加载的类
  而父加载器所加载的类 不能访问 自加载器所加载的类

上一篇下一篇

猜你喜欢

热点阅读