類的生命周期:
- 加載(Loading):找Class文件
- 驗證(Verification):驗證格式,依賴
- 準備(Preparation):靜態(tài)字段,方法表
- 解析(Resolution):符號解析為引用
- 初始化(Initialization):構(gòu)造器、靜態(tài)變量賦值、靜態(tài)代碼塊
- 使用(Using)
- 卸載(Unloading)

類的生命周期
類的初始化時機
- 當虛擬機啟動時,初始化用戶指定的主類,就是啟動執(zhí)行的 main 方法所在的類;
- 當遇到用以新建目標類實例的 new 指令時,初始化 new 指令的目標類,就是 new 一個類的時候要初始化;
- 當遇到調(diào)用靜態(tài)方法的指令時,初始化該靜態(tài)方法所在的類;
- 當遇到訪問靜態(tài)字段的指令時,初始化該靜態(tài)字段所在的類;
- 子類的初始化會觸發(fā)父類的初始化;
- 如果一個接口定義了 default 方法,那么直接實現(xiàn)或者間接實現(xiàn)該接口的類的初始化, 會觸發(fā)該接口的初始化;
- 使用反射 API 對某個類進行反射調(diào)用時,初始化這個類,其實跟前面一樣,反射調(diào)用 要么是已經(jīng)有實例了,要么是靜態(tài)方法,都需要初始化;
- 當初次調(diào)用 MethodHandle 實例時,初始化該 MethodHandle 指向的方法所在的 類。
MethodHandle 是什么?https://blog.csdn.net/ShuSheng0007/article/details/107066856
不會初始化(可能會加載)
- 通過子類引用父類的靜態(tài)字段,只會觸發(fā)父類的初始化,而不會觸發(fā)子類的初始化。
- 定義對象數(shù)組,不會觸發(fā)該類的初始化。
- 常量在編譯期間會存入調(diào)用類的常量池中,本質(zhì)上并沒有直接引用定義常量的類,不 會觸發(fā)定義常量所在的類。
- 通過類名獲取 Class 對象,不會觸發(fā)類的初始化,Hello.class 不會讓 Hello 類初始 化。
- 通過 Class.forName 加載指定類時,如果指定參數(shù) initialize 為 false 時,也不會觸 發(fā)類初始化,其實這個參數(shù)是告訴虛擬機,是否要對類進行初始化。Class.forName (“jvm.Hello”)默認會加載 Hello 類。
- 通過 ClassLoader 默認的 loadClass 方法,也不會觸發(fā)初始化動作(加載了,但是 不初始化)。
類加載器:

類加載查找路徑

類加載器繼承關(guān)系
類加載器三大特點:
- 雙親委派模型:如果一個類加載器收到了類加載的請求,首先不會自己去嘗試加載這個類,而是把這個請求委派給雙親加載器去完成,每一個層次的類加載器都是如此。只有當父類加載器反饋自己無法完成這個加載請求時(它的搜索范圍內(nèi)沒有找到所需的類時),子加載器才會嘗試自己完成加載。
- 負責依賴:加載類的同時要將他依賴的類,父類,接口等也要加載
- 緩存加載:類加載器加載過一次,便會緩存下來,之后可以直接獲取到
如何顯示當前ClassLoader加載了哪些jar包?
public class JvmClassLoaderPrintPath {
public static void main(String[] args) {
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
System.out.println("啟動類加載器");
for (URL url : urls) {
System.out.println(" --> " + url.toExternalForm());
}
printClassLoader("擴展類加載器", JvmClassLoaderPrintPath.class.getClassLoader().getParent());
printClassLoader("應用類加載器", JvmClassLoaderPrintPath.class.getClassLoader());
}
public static void printClassLoader(String name, ClassLoader CL) {
if (CL != null) {
System.out.println(name + " ClassLoader -> " + CL.toString());
printUrlForClassLoader(CL);
} else {
System.out.println(name + " ClassLoader -> null");
}
}
public static void printUrlForClassLoader(ClassLoader CL) {
Object ucp = insightField(CL, "ucp");
Object path = insightField(ucp, "path");
ArrayList ps = (ArrayList) path;
for (Object p : ps) {
System.out.println(" --> " + p.toString());
}
}
private static Object insightField(Object obj, String fName) {
try{
Field f = null;
if (obj instanceof URLClassLoader) {
f = URLClassLoader.class.getDeclaredField(fName);
} else {
f = obj.getClass().getDeclaredField(fName);
}
f.setAccessible(true);
return f.get(obj);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
添加引用類的幾種方式:
1、放到 JDK 的 lib/ext 下,或者-Djava.ext.dirs
2、 java –cp/classpath 或者 class 文件放到當前路徑
3、自定義 ClassLoader 加載
4、拿到當前執(zhí)行類的 ClassLoader,反射調(diào)用 addUrl 方法添加 Jar 或路徑(JDK9 無效)。

4、代碼