参考《探秘Java9之类加载》:https://yq.aliyun.com/articles/518315
类加载器的组织结构
BootClassLoader
1、由C/C++语言编写的一个类。
2、启动类加载器,用于加载启动的基础模块类。
PlatformClassLoader
1、平台类加载器,用于加载一些平台相关的模块,双亲是BootClassLoade。
2、java语言编写
AppClassLoader
1、应用模块加载器,用于加载应用级别的模块(包含用户自定义的类),双亲是PlatformClassLoader。
加载器之间是上下级的关系,不是子父类的关系。
Java9之前和Java9使用ClassLoader的方式对比:
package test;
import java.lang.reflect.InvocationTargetException;
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
/** Java9之前的使用方式 */
Class clazz = loader.loadClass("test.Car");
Object obj = clazz.newInstance();
Car car = (Car) obj;
car.run();
} catch (ClassNotFoundException e) {
System.err.println(e);
} catch (InstantiationException e) {
System.err.println(e);
} catch (IllegalAccessException e) {
System.err.println(e);
}
try {
/** Java9的使用方式 */
Class clazz = loader.loadClass("test.Car");
Object obj = clazz.getDeclaredConstructor(String.class).newInstance("Benz");
Car car = (Car) obj;
car.run();
} catch (ClassNotFoundException e) {
System.err.println(e);
} catch (NoSuchMethodException e) {
System.err.println(e);
} catch (SecurityException e) {
System.err.println(e);
} catch (InstantiationException e) {
System.err.println(e);
} catch (IllegalAccessException e) {
System.err.println(e);
} catch (IllegalArgumentException e) {
System.err.println(e);
} catch (InvocationTargetException e) {
System.err.println(e);
}
}
}
Car类:
package test;
public class Car {
public Car(String brand) {
this.brand = brand;
}
private String brand;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void run() {
System.out.println("run....");
}
}
类加载器常用方法:
class NetworkClassLoader extends ClassLoader {
String host;
int port;
public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
}
private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}
类加载器加载机制:双亲委托机制
- 如果一个类加载器收到了类加载的请求,
- 它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,
- 因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,
- 只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,
- 子加载器才会尝试自己去加载该类。
自定义类加载器
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class MyClassLoader extends ClassLoader{
/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String srcPath = args[0];
String destDir = args[1];
FileInputStream fis = new FileInputStream(srcPath);
String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);
String destPath = destDir + "\\" + destFileName;
FileOutputStream fos = new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
private static void cypher(InputStream ips ,OutputStream ops) throws Exception{
int b = -1;
while((b=ips.read())!=-1){
ops.write(b ^ 0xff);
}
}
private String classDir;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
String classFileName = classDir + "\\" + name.substring(name.lastIndexOf('.')+1) + ".class";
try {
FileInputStream fis = new FileInputStream(classFileName);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
cypher(fis,bos);
fis.close();
System.out.println("aaa");
byte[] bytes = bos.toByteArray();
return defineClass(bytes, 0, bytes.length);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public MyClassLoader(){
}
public MyClassLoader(String classDir){
this.classDir = classDir;
}
}
调用自定义类加载器加载指定的类:
Class clazz = new MyClassLoader("类所在的文件夹名称,相对路径").loadClass("ClassLoaderAttachment");
//注意如果要加载的类与写这个语句的类在同一目录下,类名前要加包名,优先使用父加载器进行加载。
//如果是在当前工作目录下的一个文件夹里,可以直接写类名。此时父加载器找不到这个类,则会使用自定义的加载器进行加载。