自定义类加载器在复杂类加载情况下的运行分析
示例代码:
package com.leofight.jvm.classloader;
public class MyCat {
public MyCat() {
System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
}
}
package com.leofight.jvm.classloader;
public class MySample {
public MySample() {
System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());
new MyCat();
}
}
package com.leofight.jvm.classloader;
public class MyTest17 {
public static void main(String[] args) throws Exception {
MyTest16 loader1 = new MyTest16("loader1");
Class> clazz = loader1.loadClass("com.leofight.jvm.classloader.MySample");
System.out.println("class: " + clazz.hashCode());
Object object = clazz.newInstance();
}
}
该程序运行结果会输出什么?
输出
class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
上述类并非自定义加载器来加载的,而是由系统类加载器所加载。
修改代码如下:
package com.leofight.jvm.classloader;
public class MyTest17 {
public static void main(String[] args) throws Exception {
MyTest16 loader1 = new MyTest16("loader1");
Class> clazz = loader1.loadClass("com.leofight.jvm.classloader.MySample");
System.out.println("class: " + clazz.hashCode());
//Object object = clazz.newInstance();
}
}
输出结果会是什么?
输出
class: 1627674070
小结
如果注释掉该行,那么并不会实例化MySample对象,即MySample构造方法不会被调用。因此不会实例化MyCat对象,即没有对MyCat进行主动使用,这里就不会加载MyCat Class
加上-XX:+TraceClassLoading
查看详细的类加载情况
未注释掉Object object = clazz.newInstance();
的加载情况
......
[Loaded com.leofight.jvm.classloader.MyTest16 from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
[Loaded com.leofight.jvm.classloader.MySample from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
[Loaded com.leofight.jvm.classloader.MyCat from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
[Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]
注释掉Object object = clazz.newInstance();
的加载情况
......
[Loaded java.lang.Void from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded com.leofight.jvm.classloader.MyTest16 from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
[Loaded com.leofight.jvm.classloader.MySample from file:/Users/leofight/IdeaProjects/jvm_lecture/out/production/classes/]
class: 1627674070
[Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar]
类加载器命名空间实战剖析与透彻理解
示例代码:
package com.leofight.jvm.classloader;
public class MyTest17_1 {
public static void main(String[] args) throws Exception {
MyTest16 loader1 = new MyTest16("loader1");
loader1.setPath("/Users/Desktop/");
Class> clazz = loader1.loadClass("com.leofight.jvm.classloader.MySample");
System.out.println("class: " + clazz.hashCode());
Object object = clazz.newInstance();
}
}
运行上述程序,查看结果
输出
class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
拷贝存放.class文件的整个目录到桌面,并运行,查看结果
输出
class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
结果一样,原因:在当前的classpath仍然存在MySample、MyCat,系统加载器仍可找到。现在删除classpath下的整个两个class文件,查看运行结果。
输出:
findClass invoked: com.leofight.jvm.classloader.MySample
class loader name: loader1
class: 1625635731
MySample is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]
findClass invoked: com.leofight.jvm.classloader.MyCat
class loader name: loader1
MyCat is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]
现在由自定义类加载器所加载。
继续修改
重新build后删除MyCat,仅留MySample,桌面上这两个类的class文件都在,运行该程序会是什么结果?
输出:
class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
Exception in thread "main" java.lang.NoClassDefFoundError: com/leofight/jvm/classloader/MyCat
at com.leofight.jvm.classloader.MySample.(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:422)
at java.lang.Class.newInstance(Class.java:442)
at com.leofight.jvm.classloader.MyTest17_1.main(MyTest17_1.java:20)
Caused by: java.lang.ClassNotFoundException: com.leofight.jvm.classloader.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:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 7 more
为什么会出错?
MySample是由系统加载器所加载,MyCat是由加载了MySample的类加载器来加载,因为classpathMyCat的class文件不存在,所以报错。
继续修改
重新build,删除MySample,留下MyCat的class,接下来的输出结果。
输出:
findClass invoked: com.leofight.jvm.classloader.MySample
class loader name: loader1
class: 1625635731
MySample is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
为什么可以正常运行?
MySample是由自定义类加载器所加载,MyCat是由加载了MySample的类加载器来加载,因为桌面MySample、MyCat class字节码文件都存在,所以该程序正常运行。
修改MyCat如下:加入MySample的引用
package com.leofight.jvm.classloader;
public class MyCat {
public MyCat() {
System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
System.out.println("from MyCat: " + MySample.class);
}
}
重新build,执行结果如下:
输出
class: 1627674070
MySample is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
from MyCat: class com.leofight.jvm.classloader.MySample
复制classpath下的所有class文件到桌面,删除classpath下的MySample,然后运行,结果如下:
输出:
findClass invoked: com.leofight.jvm.classloader.MySample
class loader name: loader1
class: 1625635731
MySample is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
Exception in thread "main" java.lang.NoClassDefFoundError: com/leofight/jvm/classloader/MySample
at com.leofight.jvm.classloader.MyCat.(MyCat.java:8)
at com.leofight.jvm.classloader.MySample.(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:422)
at java.lang.Class.newInstance(Class.java:442)
at com.leofight.jvm.classloader.MyTest17_1.main(MyTest17_1.java:20)
Caused by: java.lang.ClassNotFoundException: com.leofight.jvm.classloader.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:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 8 more
为什么报错?
MySample与MyCat是由不同的类加载器所加载,所以命名空间不同,不可以互相访问。
修改MySample如下:加入MyCat的引用
package com.leofight.jvm.classloader;
public class MySample {
public MySample() {
System.out.println("MySample is loaded by:" + this.getClass().getClassLoader());
new MyCat();
System.out.println("from MySample " + MyCat.class);
}
}
修改MyCat,去除MySample的引用
package com.leofight.jvm.classloader;
public class MyCat {
public MyCat() {
System.out.println("MyCat is loaded by:" + this.getClass().getClassLoader());
//System.out.println("from MyCat: " + MySample.class);
}
}
重新build,复制classpath下的所有class文件到桌面,删除classpath下的MySample,然后运行,结果如下:
findClass invoked: com.leofight.jvm.classloader.MySample
class loader name: loader1
class: 1625635731
MySample is loaded by:[class com.leofight.jvm.classloader.MyTest16@1627674070]
MyCat is loaded by:sun.misc.Launcher$AppClassLoader@14dad5dc
from MySample class com.leofight.jvm.classloader.MyCat
小结
关于命名空间的重要说明
1.子加载器所加载的类能够访问父加载器所加载的类
2.父加载器所加载的类无法访问到子加载器所加载的类
类加载器实战剖析与疑难点解析
示例代码
package com.leofight.jvm.classloader;
public class MyTest18 {
public static void main(String[] args) {
System.out.println(System.getProperty("sun.boot.class.path"));
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println(System.getProperty("java.class.path"));
}
}
输出:
/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/sunrsasign.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/classes
/Users/leofight/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java
/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home/lib/tools.jar:/Users/lz/IdeaProjects/jvm_lecture/out/production/classes:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar
示例代码
package com.leofight.jvm.classloader;
public class MyTest18_1 {
public static void main(String[] args) throws Exception {
MyTest16 loader1 = new MyTest16("loader1");
loader1.setPath("/Users/leofight/Desktop/");
Class> clazz = loader1.loadClass("com.leofight.jvm.classloader.MyTest1");
System.out.println("class: " + clazz.hashCode());
System.out.println("class loader:" + clazz.getClassLoader());
}
}
运行输出
class: 1627674070
class loader:sun.misc.Launcher$AppClassLoader@14dad5dc
执行sudo cp -r ~/IdeaProjects/jvm_lecture/out/production/classes/com classes
,然后运行,输出
class: 1639705018
class loader:null
示例代码
package com.leofight.jvm.classloader;
import com.sun.crypto.provider.AESKeyGenerator;
public class MyTest19 {
public static void main(String[] args) {
AESKeyGenerator aesKeyGenerator = new AESKeyGenerator();
System.out.println(aesKeyGenerator.getClass().getClassLoader());
System.out.println(MyTest19.class.getClassLoader());
}
}
输出
sun.misc.Launcher$ExtClassLoader@2b193f2d
sun.misc.Launcher$AppClassLoader@14dad5dc
执行java -Djava.ext.dirs=./ com.leofight.jvm.classloader.MyTest19
输出
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/crypto/provider/AESKeyGenerator
at com.leofight.jvm.classloader.MyTest19.main(MyTest19.java:8)
Caused by: java.lang.ClassNotFoundException: com.sun.crypto.provider.AESKeyGenerator
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 1 more
示例代码
package com.leofight.jvm.classloader;
public class MyPerson {
private MyPerson myPerson;
public void setMyPerson(Object object) {
this.myPerson = (MyPerson) object;
}
}
package com.leofight.jvm.classloader;
import java.lang.reflect.Method;
public class MyTest20 {
public static void main(String[] args) throws Exception {
MyTest16 loader1 = new MyTest16("loader1");
MyTest16 loader2 = new MyTest16("loader2");
Class> clazz1 = loader1.loadClass("com.leofight.jvm.classloader.MyPerson");
Class> clazz2 = loader2.loadClass("com.leofight.jvm.classloader.MyPerson");
System.out.println(clazz1 == clazz2);
Object object1 = clazz1.newInstance();
Object object2 = clazz2.newInstance();
Method method = clazz1.getMethod("setMyPerson", Object.class);
method.invoke(object1, object2);
}
}
输出
true