1、jvm自带各级加载器的加载路径
public class MyTest19 {
public static void main(String[] args) throws Exception {
System.out.println(System.getProperty("sun.boot.class.path"));
System.out.println("=======");
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println("=======");
System.out.println(System.getProperty("java.class.path"));
}
}
C:\Program Files\Java\jdk1.8.0_201\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_201\jre\classes
=======
C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext;C:\Windows\Sun\Java\lib\ext
=======
C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;G:\JVM-lecture\target\classes;L:\idea\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar
2、测试根类加载器是否可以加载自定义类
将生成的类的class文件放入到根类加载器的其中一个加载目录:C:\Program Files\Java\jdk1.8.0_201\jre\classes
,然后查看是由哪个加载器加载的。
在C:\Program Files\Java\jdk1.8.0_201\jre
目录下新建classes目录,在classes目录下新建com/jvm/classloader
目录,然后将user.class放入该目录下。
package com.jvm.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class MyTest17 extends ClassLoader {
private String classLoaderName;
private String path; // class文件路径
private final String fileExtension = ".class";
public MyTest17(String classLoaderName) {
super(); // 将系统类加载器作为该类加载器的父类加载器
this.classLoaderName = classLoaderName;
}
public void setPath(String path) {
this.path = path;
}
public MyTest17(ClassLoader parent, String classLoaderName) {
super(parent); // 显式指定该类加载器的父加载器
this.classLoaderName = classLoaderName;
}
@Override
public String toString() {
return "MyTest17{" +
"classLoaderName='" + classLoaderName + '\'' +
'}';
}
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
System.out.println("findClass:" + className);
System.out.println("class loader name:" + this.classLoaderName);
// 父类中findClass方法返回的是一个异常,所以必须要重写
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));
System.out.println(this.path + className + this.fileExtension);
baos = new ByteArrayOutputStream();
int ch;
while (-1 != (ch = is.read())) {
baos.write(ch);
}
data = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
baos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return data;
}
}
package com.jvm.classloader;
public class User {
public static int age = 13;
}
public class MyTest19 {
public static void main(String[] args) throws Exception {
MyTest17 loader1 = new MyTest17("loader1");
loader1.setPath("C:/Users/lifeline张/Desktop/");
Class<?> aClass = loader1.loadClass("com.jvm.classloader.User");
System.out.println("aclass: " + aClass.hashCode());
System.out.println("class loader:" + aClass.getClassLoader());
}
}
运行结果:
aclass: 1163157884
class loader:null
可以看到,确实是由根加载器进行加载的。
3、判断类是否相等
public class MyPerson {
private MyPerson myPerson;
public void setMyPerson(Object ob) {
this.myPerson = (MyPerson) ob;
}
}
public class MyTest20 {
public static void main(String[] args) throws Exception {
MyTest17 loader1 = new MyTest17("loader1");
MyTest17 loader2 = new MyTest17("loader2");
Class<?> aClass1 = loader1.loadClass("com.jvm.classloader.MyPerson");
Class<?> aClass2 = loader2.loadClass("com.jvm.classloader.MyPerson");
System.out.println(aClass1 == aClass2);
}
}
因为两者都会委托给app进行加载,所以是同一个对象。
4、引例
public class MyTest20 {
public static void main(String[] args) throws Exception {
MyTest17 loader1 = new MyTest17("loader1");
MyTest17 loader2 = new MyTest17("loader2");
Class<?> aClass1 = loader1.loadClass("com.jvm.classloader.MyPerson");
Class<?> aClass2 = loader2.loadClass("com.jvm.classloader.MyPerson");
System.out.println(aClass1 == aClass2);
Object o1 = aClass1.newInstance();
Object o2 = aClass2.newInstance();
// 通过反射调用setMyPerson方法
Method method = aClass1.getMethod("setMyPerson", Object.class);
// 给O1对象传入O2的实例
method.invoke(o1, o2);
}
}
输出结果为ture,并且不会报错。
修改:将MyPerson的class文件放到桌面上:C:\Users\lifeline张\Desktop\com\jvm\classloader
,删除工程中的MyPerson的class文件,运行结果如下:
findClass:com.jvm.classloader.MyPerson
class loader name:loader1
C:/Users/lifeline张/Desktop/com/jvm/classloader/MyPerson.class
findClass:com.jvm.classloader.MyPerson
class loader name:loader2
C:/Users/lifeline张/Desktop/com/jvm/classloader/MyPerson.class
false
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.jvm.classloader.MyTest21.main(MyTest21.java:19)
Caused by: java.lang.ClassCastException: com.jvm.classloader.MyPerson cannot be cast to com.jvm.classloader.MyPerson
at com.jvm.classloader.MyPerson.setMyPerson(MyPerson.java:7)
... 5 more
原因:两个对象都会自己加载MyPerson,但是分别是两个不同的命名空间,所以反射得到的两个类不能相互访问。
总结:
同一个命名空间内的类是互相可见的。
子加载器的命名空间包含所有父加载器的命名空间。因此由子加载器加载的类能看见父加载器加载的类。例如系统类加载器加载的类能看见根类加载器加载的类。
由父加载器加载的类不能看见子加载器加载的类。
如果两个加载器之间没有直接或者间接的父子关系,那么他们各自加载的类相互不可见。
这个异常很有意思:Caused by: java.lang.ClassCastException: com.jvm.classloader.MyPerson cannot be cast to com.jvm.classloader.MyPerson
表面上看竟然是两个类不能相互转换。