ClassLoader介紹
ClassLoader在Java 1.0的時候就有了,為了滿足Java Applet運行時遠程加載Java類的需要陨溅。
所謂ClassLoader蒿囤,就是將.java文件編譯之后產(chǎn)生的.class字節(jié)文件加載到運行時數(shù)據(jù)區(qū)(JVM的方法區(qū))中的過程纳令。而這個加載過程一般都是按需加載的,就是第一次用到某Class的時候JVM才會去加載其對應的.class文件。
系統(tǒng)提供三個ClassLoader:
- Bootstrap ClassLoader
負責加載java核心類庫(位于<JAVA_HOME>/jre/lib
的內(nèi)容)庆械,由本地代碼(如C
)編寫津肛。 - Extensions ClassLoader
負責加載java核心類庫(位于<JAVA_HOME>/jre/ext
的內(nèi)容),由sun.misc.Launcher$ExtClassLoader
實現(xiàn)搜锰。 - System ClassLoader (App ClassLoader)
負責加載應用類伴郁,一般通過java.class.path
或CLASSPATH
環(huán)境變量來加載 Java 類,由sun.misc.Launcher$ExtClassLoader
實現(xiàn)蛋叼。
自定制類加載器
用戶可通過繼承java.lang.ClassLoader
自行實現(xiàn)定制的類加載器焊傅。自定制類加載器可以做如下工作:
- 運行時裝載或卸載類剂陡,這常用于:
實現(xiàn)腳本語言
用于bean生成器
允許用戶定義的擴展性
允許命名空間之間的通信。這是CORBA / RMI協(xié)議的基礎狐胎。- 改變Java字節(jié)碼的裝入鸭栖,例如,可用于Java類字節(jié)碼的加密裝入握巢。
- 修改已裝入的字節(jié)碼weaving of aspects when using aspect-oriented programming)晕鹊。
自定制類加載器實現(xiàn)示例
public class FileClassLoader extends ClassLoader {
private String rootDir;
public FileClassLoader(String rootDir) {
this.rootDir = rootDir;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null) {
throw new ClassNotFoundException();
}
return defineClass(name, classData, 0, classData.length);
}
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('.', '/') + ".class";
}
}
java.lang.ClassLoader
類的方法loadClass()
封裝了前面提到的代理模式的實現(xiàn)。該方法會首先調(diào)用findLoadedClass()
方法來檢查該類是否已經(jīng)被加載過暴浦;如果沒有加載過的話溅话,會調(diào)用父類加載器的loadClass()
方法來嘗試加載該類;如果父類加載器無法加載該類的話歌焦,就調(diào)用findClass()
方法來查找該類飞几。因此,為了保證類加載器都正確實現(xiàn)代理模式独撇,在開發(fā)自己的類加載器時屑墨,最好不要覆寫loadClass()
方法,而是覆寫findClass()
方法纷铣。
雙親委派機制:
某個類加載器接到加載類的請求時绪钥,會遞歸地將請求委托給父類加載器,只有父類加載器無法加載的時候自己才會去加載該類关炼。該機制的作用是防止同一個類被加載多次程腹。
自定製類加載器的雙親是System ClassLoader,System ClassLoader的雙親是Extensions ClassLoader儒拂,Extensions ClassLoader的雙親是Bootstrap ClassLoader寸潦。
Namespace
每個類加載器加載的類都會被分配一個裝載其的ClassLoader對應的唯一namspace。namespace就像一道墻社痛,不同namespace被加載的類彼此之間一般無法交互见转,甚至彼此都不知道彼此的存在。
如果要求某個ClassLoader去加載某個類型蒜哀,最終另外一個被委派的ClassLoader加載并返回了這個類型斩箫,那么這兩個ClassLoader共享該類型。這就是用戶可以無視namespace無障礙調(diào)用java api的原因撵儿。
加載流程
- loading
Classloader通過雙親委派機制查找.class文件(TYPE)并載入 - linking 分三步
2.1. verification
檢查引入TYPE文件正確性
2.2. preparation
給class變量分配內(nèi)存(在方法區(qū))并賦值default value:(boolean:false, int:0, reference:null)
2.3. resolution
將symbolic reference轉(zhuǎn)為direct reference 乘客,通常延后觸發(fā)。所謂symbolic reference就是fully qualified name(全限定名)淀歇,拿在com.mylib
包下的Utility
類來說易核,其全限定名是com.mylib.Utility
。 - initialization
觸發(fā)代碼提供的賦值語句浪默,通常延后觸發(fā)牡直。
加載流程示例:
public class Hello{
public int age;
public String name;
public static String species;
{
age = 10;
name = "Jack";
System.out.println("Non-static block");
}
static{
species = "human";
System.out.println("Static block");
}
public Hello(){
System.out.println("initialization");
}
public static void testStatic() {
System.out.println("static method invoke");
}
public void test() {
System.out.println("instance method invoke");
}
}
測試類:
import java.lang.reflect.Method;
public class LoadClass {
public static void main(String [] args) throws Exception {
LoadClass lc = new LoadClass();
ClassLoader cl = lc.getClass().getClassLoader();
System.out.println(cl.getClass().getName());
Class<Hello> HL = (Class<Hello>) cl.loadClass("Hello");
System.out.println("Hello loaded");
Method m =HL.getMethod("testStatic", null);
m.invoke(null, null);
Hello hl = HL.newInstance();
}
}
Console打印結果:
sun.misc.Launcher$AppClassLoader
Hello loaded
Static block
static method invoke
Non-static block
initialization
由上例看出加載自定義的類的默認ClassLoader是AppClassLoader
缀匕。在類Hello
被加載結束后并沒有給class變量賦值;在調(diào)用static method的時候static block才被觸發(fā)碰逸;在調(diào)用normal method的時候non-static block才被觸發(fā)乡小。
Reference
https://zh.wikipedia.org/wiki/Java類加載器
https://blog.csdn.net/sureyonder/article/details/5564181
https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html