类加载范围
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文件。
加载范围的性质
结合命名空间的性质,空间中承载的是按照双亲委托模型可加载的类。
子加载器所加载的类能够 访问 父加载器加载的类
而父加载器所加载的类 不能访问 自加载器所加载的类