JVM内置的三大类加载器
JVM提供了三大类加载器,不同的加载器负责将不同的类加载到JVM内存中,并且它们之间严格遵守父委托机制。
根类加载器
根加载器又称Bootstrap类加载器,是最顶层的加载器由C++编写,主要负责虚拟机核心库的加载,比如java.lang包都是根加载加载的,根加载器的加载路径可以通过“sun.boot.class.path”获取。
public static void main(String[] args) {
System.out.println(System.getProperty("sun.boot.class.path"));
}
输出
C:\Program Files\Java\jre1.8.0_221\lib\resources.jar;
C:\Program Files\Java\jre1.8.0_221\lib\rt.jar;
C:\Program Files\Java\jre1.8.0_221\lib\sunrsasign.jar;
C:\Program Files\Java\jre1.8.0_221\lib\jsse.jar;
C:\Program Files\Java\jre1.8.0_221\lib\jce.jar;
C:\Program Files\Java\jre1.8.0_221\lib\charsets.jar;
C:\Program Files\Java\jre1.8.0_221\lib\jfr.jar;
C:\Program Files\Java\jre1.8.0_221\classes
扩展类加载器
扩展类加载器的父加载器时根加载器,主要用于加载JAVA_HOME下的jre\lb\ext子目录里的类库,由java语言编写,是java.lang.URLClassLoader的子类,加载路径可以通过“java.ext.dirs”获取。
public static void main(String[] args) {
System.out.println(System.getProperty("java.ext.dirs"));
}
输出
C:\Program Files\Java\jre1.8.0_221\lib\ext;
C:\WINDOWS\Sun\Java\lib\ext
系统类加载器
负责加载classpath下的类库资源,父加载器时扩展类加载器,同时也是我们自定义的类加载器的默认父加载器,加载路径可以通过“java.class.path”获取。
自定义类加载器
所有的自定义类加载器都是ClassLoader的直接子类或者间接子类,java.lang.ClassLoader是一个抽象类,里面没有抽象方法,但是有findClass方法,务必实现该方法,否则会抛出Class找不到异常。
自定义类加载器代码
package com.pys.classloader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class MyClassLoader extends ClassLoader{
//默认的class存放路径
private final static Path DEFAULT_CLASS_DIR = Paths.get("F://eclipse-jee-2018-12-R-win32-x86_64","ClassLoader");
private final Path classDir;
//使用默认路径
public MyClassLoader() {
super();
this.classDir=DEFAULT_CLASS_DIR;
}
//传入指定路径
public MyClassLoader(String classDir) {
super();
this.classDir=Paths.get(classDir);
}
//传入指定路径,指定父加载器
public MyClassLoader(String classDir,ClassLoader parent) {
super(parent);
this.classDir=Paths.get(classDir);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classBytes = this.readClassBytes(name);
if(classBytes == null || classBytes.length==0) {
throw new ClassNotFoundException("can not loade the class "+name);
}
return this.defineClass(name,classBytes, 0,classBytes.length);
}
//将class文件读入内存
private byte[] readClassBytes(String name) throws ClassNotFoundException {
String classPath = name.replace(".", "/");
Path classFullPath = classDir.resolve(Paths.get(classPath+".class"));
if(!classFullPath.toFile().exists()) {
throw new ClassNotFoundException("the class "+name+" not found");
}
//try() 创建的对象在使用完后自动关闭 不用写finally关闭
try(ByteArrayOutputStream baos = new ByteArrayOutputStream()){
Files.copy(classFullPath, baos);
return baos.toByteArray();
}catch (IOException e) {
throw new ClassNotFoundException("loade the class "+name+" error",e);
}
}
@Override
public String toString() {
return "my ClassLoader 加载器";
}
}
需要加载类代码
public class MyHello {
static {
System.out.println("hello world class is my classLoader initialized");
}
public String getString() {
return " my classLoader success";
}
}
测试类代码
package com.pys.classloader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException,
IllegalAccessException,
NoSuchMethodException,
SecurityException,
IllegalArgumentException,
InvocationTargetException {
MyClassLoader myclassLoader = new MyClassLoader();
//指定路径
//MyClassLoader myclassLoader = new MyClassLoader("F:\\eclipse-jee-2018-12-R-win32-x86_64\\testclass");
Class<?> aclass= myclassLoader.loadClass("com.pys.classloader.MyHello");
System.out.println("getClassLoader "+aclass.getClassLoader());
Object hellow = aclass.newInstance();
System.out.println("Object hellow "+hellow);
Method we = aclass.getMethod("getString");
String resut = (String) we.invoke(hellow);
System.out.println("resut "+ resut);
}
}
输出
getClassLoader my ClassLoader 加载器
hello world class is my classLoader initialized
Object hellow com.pys.classloader.MyHello@5c647e05
resut my classLoader success
将编译好的class文件放到F:\eclipse-jee-2018-12-R-win32-x86_64\ClassLoader\com\pys\classloader目录下(根据自己情况定),删除源目录下的class文件和java文件。删除文件是因为加载器的双亲委托机制,当前加载器会调用父加载器尝试加载知道最顶层的加载器,所以不删除文件会被系统加载器所加载。
若不想删除class和java文件可以尝试下面的两种方式:
- 绕过系统类加载器,直接将扩展类加载器作为MyClassLoader的父加载器。
- 指定父类加载器为null.
因为在扩展类加载器和根加载器中都找不到MyHello类,所以最后会由当前类加载器进行加载。