JDK 13 的 12 個新特性,真心漲姿勢了
804
2025-03-31
類加載過程
加載->連接->初始化。連接過程又可分為三步:驗證->準備->解析。
類加載器分類
JVM 中內置了三個重要的 ClassLoader,除了 BootstrapClassLoader 其他類加載器均由 Java 實現且全部繼承自java.lang.ClassLoader:
啟動類加載器(Bootstrap ClassLoader)此類加載器負責將存放在
擴展類加載器(Extension ClassLoader)這個類加載器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實現的。它負責將
應用程序類加載器(Application ClassLoader)這個類加載器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現的。由于這個類加載器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般稱為系統類加載器。它負責加載用戶類路徑(ClassPath)上所指定的類庫,開發者可以直接使用這個類加載器,如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。
雙親委派模型
每一個類都有一個對應它的類加載器。系統中的 ClassLoder 在協同工作的時候會默認使用?雙親委派模型?。
1、在類加載的時候,系統會首先判斷當前類是否被加載過。已經被加載的類會直接返回,否則才會嘗試加載。
2、加載的時候,首先會把該請求委派該父類加載器的?loadClass()?處理,因此所有的請求最終都應該傳送到頂層的啟動類加載器?BootstrapClassLoader?中。當父類加載器無法處理時,才由自己來處理。
3、當父類加載器為null時,會使用啟動類加載器?BootstrapClassLoader?作為父類加載器。
好處
使得 Java 類隨著它的類加載器一起具有一種帶有優先級的層次關系,從而使得基礎類得到統一。
例如 java.lang.Object 存放在 rt.jar 中,如果編寫另外一個 java.lang.Object 并放到 ClassPath 中,程序可以編譯通過。
由于雙親委派模型的存在,所以在 rt.jar 中的 Object 比在 ClassPath 中的 Object 優先級更高,這是因為 rt.jar 中的 Object 使用的是啟動類加載器,而 ClassPath 中的 Object 使用的是應用程序類加載器。
rt.jar 中的 Object 優先級更高,那么程序中所有的 Object 都是這個 Object。
保證了Java程序的穩定運行,可以避免類的重復加載(JVM 區分不同類的方式不僅僅根據類名,相同的類文件被不同的類加載器加載產生的是兩個不同的類),也保證了 Java 的核心 API 不被篡改。
如果沒有使用雙親委派模型,而是每個類加載器加載自己的話就會出現一些問題,比如我們編寫一個稱為?java.lang.Object?類的話,那么程序運行的時候,系統就會出現多個不同的?Object?類。
注意
這個雙親翻譯的容易讓人誤解,一般理解雙親都是父母,這里的雙親表達的是“父母一輩”的人,并不是說真的有一個 Mother ClassLoader 和一個 Father ClassLoader 。另外,類加載器之間的“父子”關系也不是通過繼承來體現的,是由“優先級”來決定。官方API文檔對這部分的描述如下:
The Java platform uses a delegation model for loading classes.?The basic idea is that every class loader has a "parent" class loader.?When loading a class, a class loader first "delegates" the search for the class to its parent class loader before attempting to find the class itself.
源碼分析
private final ClassLoader parent;
protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先檢查請求的類是否已經被加載過
Class> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//父加載器不為空,調用父加載器loadClass()方法處理
c = parent.loadClass(name, false);
} else {
//父加載器為空,使用啟動類加載器 BootstrapClassLoader 加載
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
//父類加載器無法完成加載請求
}
if (c == null) {
long t1 = System.nanoTime();
//自己嘗試加載
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
自定義加載器
除了?BootstrapClassLoader?其他類加載器均由 Java 實現且全部繼承自java.lang.ClassLoader。如果我們要自定義自己的類加載器,很明顯需要繼承?ClassLoader。
示例:自定義一個NetworkClassLoader,用于加載網絡上的class文件
package classloader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
/**
* 加載網絡class的ClassLoader
*/
public class NetworkClassLoader extends ClassLoader {
private String rootUrl;
public NetworkClassLoader(String rootUrl) {
this.rootUrl = rootUrl;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
Class clazz = null;//this.findLoadedClass(name); // 父類已加載
//if (clazz == null) { //檢查該類是否已被加載過
byte[] classData = getClassData(name); //根據類的二進制名稱,獲得該class文件的字節碼數組
if (classData == null) {
throw new ClassNotFoundException();
}
clazz = defineClass(name, classData, 0, classData.length); //將class的字節碼數組轉換成Class類的實例
//}
return clazz;
}
private byte[] getClassData(String name) {
InputStream is = null;
try {
String path = classNameToPath(name);
URL url = new URL(path);
byte[] buff = new byte[1024*4];
int len = -1;
is = url.openStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while((len = is.read(buff)) != -1) {
baos.write(buff,0,len);
}
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
return null;
}
private String classNameToPath(String name) {
return rootUrl + "/" + name.replace(".", "/") + ".class";
}
}
JAR Java
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。