什么是加载器命名空间
每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成
在同一个命名空间中不会出现类的完整名字(类的包名+类名)相同的两个类
在不同的命名空间中,有可能会出现类的完整名字(类的包名+类名)相同的两个类
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,那个文件我们已经删掉了,找不到了,所以就报了找不到类路径异常,把那段话注释掉再执行,结果如下
结论:子加载器可以访问父加载器加载的类,而父加载器加载的类无法访问子加载器加载的类