@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
System.out.println("findClass invoked: " + className);
System.out.println("class loader name: " + this.classLoaderName);
byte[] bytes = this.loadClassDate(className);
return this.defineClass(className,bytes,0,bytes.length);
}
在MyTest16中加入输出,发现并不会执行findClass方法
System.out.println(o.getClass().getClassLoader());
输出类加载器看到是AppClassLoader加载的o
上述两点说明在Object o 并不是由我们自定义的类加载器加载的,这是因为双亲委托模型,我们自定义的类加载器委托给了系统类加载器
系统类加载器就是这个类的定义类加载器,系统类加载器和MyTest16就是他的初始类加载器
public class MyTest16 extends ClassLoader{
private String classLoaderName;
private String path;
private final String fileExtension = ".class";
public MyTest16(String classLoaderName){
super(); // 将系统类加载器当做该类加载器的父类加载器
this.classLoaderName = classLoaderName;
}
public MyTest16(ClassLoader parent, String classLoaderName){
super(parent); // 显式指定父类加载器
this.classLoaderName = classLoaderName;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String toString() {
return "MyTest16{" + "classLoaderName='" + classLoaderName + '\'' + '}';
}
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
System.out.println("findClass invoked: " + className);
System.out.println("class loader name: " + this.classLoaderName);
byte[] bytes = this.loadClassDate(className);
return this.defineClass(className,bytes,0,bytes.length);
}
private byte[] loadClassDate(String name){
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
name = name.replace(".","/");
try {
is = new FileInputStream(new File(this.path + name + this.fileExtension));
baos = new ByteArrayOutputStream();
int ch = 0;
while(-1 != (ch = is.read())){
baos.write(ch);
}
data = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
is.close();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return data;
}
public static void main(String[] args) throws Exception{
MyTest16 loader1 = new MyTest16("loader1");
// 在本地建立指定目录,然后删掉idea编译的MyTest1,可以发现findClass执行了
loader1.setPath("D:\\workspace\\");
// loader1.setPath("/d/workspace/jvm_lecture/target/classes/");
Class<?> clazz = loader1.loadClass("com.yshuoo.jvm.classloader.MyTest1");
System.out.println("class: " + clazz.hashCode());
Object object = clazz.newInstance();
System.out.println(object);
}
}
对MyTest16做出修改,可以指定路径去加载,在系统类加载器默认路径外创建一个完整的路径,之后编译好的MyTest1,执行发现findClass方法执行,证明我们自定义的类加载器从指定路径加载了MyTest1(删掉了原本target里的MyTest1.class 父类加载器——系统类加载器加载失败)
new 一个新的类加载器loader2,和loader1一样从同一的路径加载,重新构造项目恢复MyTest1
public static void main(String[] args) throws Exception{
MyTest16 loader1 = new MyTest16("loader1");
// 在本地建立指定目录,然后删掉idea编译的MyTest1,可以发现findClass执行了
loader1.setPath("D:\\workspace\\");
// loader1.setPath("/d/workspace/jvm_lecture/target/classes/");
Class<?> clazz = loader1.loadClass("com.yshuoo.jvm.classloader.MyTest1");
System.out.println("class: " + clazz.hashCode());
Object object = clazz.newInstance();
System.out.println(object);
System.out.println("************");
MyTest16 loader2 = new MyTest16("loader2");
loader2.setPath("D:\\workspace\\");
Class<?> clazz2 = loader2.loadClass("com.yshuoo.jvm.classloader.MyTest1");
System.out.println("class: " + clazz2.hashCode());
Object object2 = clazz2.newInstance();
System.out.println(object2);
}
可以看出系统类加载器第一次加载过了MyTest1,第二次不再重新加载
再把MyTest1.class删掉
执行结果如下:
两个类加载器都执行了,而且同一个类被加载了两次,这和命名空间有关系
- 每个类加载器都有自己的命名空间,命名空间由该加载器及所有父加载器所加载的类组成
- 在同一个命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
- 在不同的命名空间中,有可能会出现类的完整名字爱(包括类的包名)相同的两个类