MyTest类(自定义加载器)
package com.test;
import java.io.*;
public class MyTest extends ClassLoader {
private String classLoaderName;//加载器名字
private String path;
private final String fileExtension = ".class";
public MyTest(String classLoaderName)
{
super();// 将系统类加载器当做该类加载器的父加载器
this.classLoaderName = classLoaderName;
}
public MyTest(ClassLoader parent, String classLoaderName)
{
super(parent);// 显示指定该类加载器的父加载器
this.classLoaderName = classLoaderName;
}
public void setPath(String path)
{
this.path = path;
}
@Override
protected Class<?> findClass(String classname) throws ClassNotFoundException {
System.out.println("findClass invoked:" + classname);
System.out.println("class loader name: " + this.classLoaderName);
byte[] data = this.loadClassData(classname);
return this.defineClass(classname, data, 0, data.length);
}
private byte[] loadClassData(String className)
{
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
className = className.replace(".", "/");
try {
is = new FileInputStream(new File(this.path + className + this.fileExtension));
baos = new ByteArrayOutputStream();
int ch = 0;
while(((ch = is.read()) != -1))
{
baos.write(ch);
}
data = baos.toByteArray();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (Exception ex){
ex.printStackTrace();
}
}
return data;
}
}
MyCat类
package com.test;
public class MyCat {
public MyCat() {
System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
}
}
MySample类
package com.test;
public class MySample {
public MySample() {
System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());
new MyCat();
}
}
MyTest2类
package com.test;
public class MyTest2 {
public static void main(String[] args) throws Exception {
MyTest loader1 = new MyTest("loader1");
loader1.setPath("C:/Users/小呆呆/Desktop/");//当父类加载器都解决不了的时候,才会调用自身的findClass往这个地址找
Class<?> clazz = loader1.loadClass("com.test.MySample");
System.out.println("class:" + clazz.hashCode());
// 如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用
// 因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat.class
Object object = clazz.newInstance();
}
}
在项目中存在MyTest.class,MyTest2.class,桌面上的com.test中存在MyCat.class和MySample.class,当实例化MySample时,初始化时会对MyCat进行实例化
- 1、当项目中存在MyCat.class和存在MySample.class时
加载MySample.class是由系统类加载器进行加载,实例化MySample类时,需要对MyCat进行实例化,因此在MyCat实例化前,对MyCat进行加载,也是由系统类加载器进行加载
输出
class:1735600054
MySample is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
- 2、当项目中不存在MyCat.class和不存在MySample.class时
系统类加载器加载不了MySample.class,因此由自定义加载器MyTest对MySample.class进行加载,实例化MySample类时,需要对MyCat进行实例化,因此在MyCat实例化前,由加载MySample的类加载器(MyTest自定义加载器)对MyCat.class进行加载
疑问?
是系统类加载器加载不了才会让MyTest去加载它,还是因为MySample是由MyTest加载的,所以要让MyTest去加载它?
答案:后者是正确的,MySample.class是由MyTest加载的,MyCat是在MySample里面的,因此MyCat.class让加载MySample的类加载器(自定义加载器)直接进行加载的,
findClass invoked:com.test.MySample
class loader name: loader1
class:2133927002
MySample is loaded by:com.test.MyTest@677327b6
findClass invoked:com.test.MyCat
class loader name: loader1
MyCat is loaded by:com.test.MyTest@677327b6
- 3、当项目不存在MyCat.class,存在MySample.class时
由于项目中存在MySample.class,因此系统类加载器可以对MySample.class进行加载,当对MySample类进行实例化时,需要对MyCat进行实例化,因为MyCat是在MySample里面的,在加载MyCat时是让加载MySample的类加载器(系统类加载器)直接进行加载的(因为父亲都加载不了)
输出(存在找不到MyCat对象的错误)
class:1735600054
MySample is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Exception in thread "main" java.lang.NoClassDefFoundError: com/test/MyCat
at com.test.MySample.<init>(MySample.java:8)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at com.test.MyTest2.main(MyTest2.java:13)
Caused by: java.lang.ClassNotFoundException: com.test.MyCat
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more
- 4、当项目存在MyCat.class,不存在MySample.class时
由于项目中不存在MySample.class,需要自定义加载MyTest才能对它进行加载,当对MySample进行实例化时,需要对MyCat进行实例化,因为MyCat是在MySample里面的,在加载MyCat时是让加载MySample的类加载器(自定义加载器)直接进行加载的,又因为类加载器符合双亲委派机制,因此最终是由系统类加载器对MyCat进行加载
输出
findClass invoked:com.test.MySample
class loader name: loader1
class:2133927002
MySample is loaded by:com.test.MyTest@677327b6
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
命名空间的关系
image.png理论
存在两个类
-
如果二者在“同一个命名空间”下,则相互访问没有问题。
-
如果一个在子命名空间,一个在父命名空间,则在子命名空间的类可以访问到父命名空间的类,而父命名空间中的类访问不到子命名空间的类(这种情况会报错java.lang.ClassNotFoundException)。
-
如果二者在两个没有任何父子关系的同级命名空间中,则不能相互访问。
MyTest2
package com.test;
public class MyTest2 {
public static void main(String[] args) throws Exception {
MyTest loader1 = new MyTest("loader1");
loader1.setPath("C:/Users/小呆呆/Desktop/");//当父类加载器都解决不了的时候,才会调用自身的findClass往这个地址找
Class<?> clazz = loader1.loadClass("com.test.MySample");
System.out.println("class:" + clazz.hashCode());
// 如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用
// 因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat.class
Object object = clazz.newInstance();
}
}
总结果:
项目存在MyCat.class,不存在MySample.class时
通过上面的分析可知,最终MySample类由MyTest自定义加载器进行加载,MyCat由系统类加载器进行加载,则MySample类可以访问MyCat,而MyCat不能访问MySample
- 1、修改MyCat,加了一句代码引用MySample(其中MySample不引用Cat)
MyCat类(和上面的MyCat不同)
package com.test;
public class MyCat {
public MyCat() {
System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
System.out.println("from MyCat:" + MySample.class);
}
}
MySample类
package com.test;
public class MySample {
public MySample() {
System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());
new MyCat();
}
}
输出(报错)
findClass invoked:com.test.MySample
class loader name: loader1
class:2133927002
MySample is loaded by:com.test.MyTest@677327b6
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
Exception in thread "main" java.lang.NoClassDefFoundError: com/test/MySample
at com.test.MyCat.<init>(MyCat.java:8)
at com.test.MySample.<init>(MySample.java:8)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at com.test.MyTest2.main(MyTest2.java:13)
Caused by: java.lang.ClassNotFoundException: com.test.MySample
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 8 more
- 2、修改了MySample,加了一句代码引用MyCat(其中MyCat不引用MySample)
MyCat
public class MyCat {
public MyCat() {
System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
//System.out.println("from MyCat:" + MySample.class);
}
}
MySample
package com.test;
public class MySample {
public MySample() {
System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());
new MyCat();
System.out.println("from MySample:" + MyCat.class);
}
}
输出
findClass invoked:com.test.MySample
class loader name: loader1
class:2133927002
MySample is loaded by:com.test.MyTest@677327b6
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@18b4aac2
from MySample:class com.test.MyCat
网友评论