The default implementation of this method searches for classes in the following order:
- Invoke findLoadedClass(String) to check if the class has already been loaded.
- Invoke the loadClass method on the parent class loader. If the parent is null the class loader built-in to the virtual machine is used, instead.
- Invoke the findClass(String) method to find the class.
稍微理解下:
- 首先調(diào)用findLoadedClass(String),如果被加載過直接返回true,沒有執(zhí)行下一個步驟
- 調(diào)用父級類加載器的loaderClass方法,如果父級類加載器為空,就使用JVM默認的類加載器
- 調(diào)用findClass方法
這個是模版方法設(shè)計模式的運用。
編寫自定義類加載器
需要繼承ClassLoader類,重寫findClass方法。
package lorenzo;
public class MyClassLoader extends ClassLoader {
private String rootDir;
public MyClassLoader(String rootDir) {
this.rootDir = rootDir;
}
private byte[] getClassData(String className) {
String path = classNameToPath(className);
try {
InputStream ins = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = ins.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private String classNameToPath(String className) {
return rootDir + File.separatorChar
+ className.replace('.', File.separatorChar) + ".class";
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("自定義類加載器");
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, classData, 0, classData.length);
}
}
}
編寫需要加載的類
package lorenzo;
public class MyClass {
@Override
public String toString() {
return "Hello Lorenzo!";
}
}
編寫測試類
package lorenzo;
public class Client {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
MyClassLoader myClassLoader = new MyClassLoader("/data");
Class clazz = myClassLoader.loadClass("lorenzo.MyClass");
System.out.println(clazz.newInstance().toString());
System.out.println(myClassLoader.getClass().getClassLoader());
}
}
注意:以上三個類都在/data/lorenzo文件夾下面
mac控制臺輸入:
cd /data/lorenzo
javac MyClassLoader.java
javac MyClass.java
javac Client.java
cd ..
java ava lorenzo/Client
output:
Hello Lorenzo!
sun.misc.Launcher$AppClassLoader@2a139a55
分析結(jié)果:
根據(jù)類加載器樹形圖可知

MyClassLoader.java和Client.java以及MyClass.java都在classpath目錄下,他們由AppClassLoader類加載器加載。根據(jù)loadClass(String)的模版方法可知,MyClassLoader的父級類加載器AppClassLoader可以加載binary name 為lorenzo.MyClass的類,程序不會執(zhí)行到loadClass的第三步,所以是AppClassLoader而不是MyClassLoader加載MyClass。
修改代碼
把MyClass.class移動到data1/lorenzo的文件夾下
修改Client中代碼:
MyClassLoader myClassLoader = new MyClassLoader("/data");
修改后的代碼為
MyClassLoader myClassLoader = new MyClassLoader("/data1");
mac控制臺輸入:
cd /data/lorenzo
javac Client.java
cd ..
java lorenzo/Client
output:
自定義類加載器
Hello Lorenzo!
sun.misc.Launcher$AppClassLoader@2a139a55
注意確保/data/lorenzo目錄沒有MyClass.class。
把自定義的類加載器掛載到ExtClassLoader
- 把MyClassLoader.class打成jar包
cd /data
jar cvf lorenzo.jar lorenzo/MyClassLoader.class
- 把lorenzo.jar復(fù)制到../jre/lib/ext/目錄下
java lorenzo/Client
output:
自定義類加載器
Hello Lorenzo!
sun.misc.Launcher$ExtClassLoader@75b84c92
寫在最后
加密的類加載器思路,首先編譯生成class文件,加密class文件,在自定義的類加載器解密。