類加載的時機
主動引用(有且只有下面的五種)
使用new關(guān)鍵字創(chuàng)建對象時,訪問類的靜態(tài)字段時;
使用java.lang.reflect包的方法進行反射調(diào)用的時候;
初始化一個類時,如果父類沒有被初始化,則需要先初始化;
當(dāng)虛擬機啟動的時候,虛擬機先會初始化主類(main()方法所在的類)
JDK1.7動態(tài)語言支持;
被動引用(類不會加載)
子類引用父類的靜態(tài)字段,不會導(dǎo)致子類的的初始化。
通過數(shù)組定義引用類。
訪問類中的常量;
類加載流程
-
加載
1.1 加載二進制字節(jié)流到內(nèi)存
1.2 生成方法區(qū)數(shù)據(jù)結(jié)構(gòu)
1.3 生成java.lang.Class對象
-
連接
2.1 驗證
- 文件格式驗證: 版本號,是不是
- 元數(shù)據(jù)驗證:是否基礎(chǔ)了final類?是不是有父類?是不是實現(xiàn)了抽象類的方法?
- 字節(jié)碼驗證:跳轉(zhuǎn)指令是否合法?
- 字符引用驗證:引用的類是否存在?類及接口是否有訪問權(quán)限?
2.2 準(zhǔn)備
類變量(即靜態(tài)變量而不是實例變量)分配內(nèi)存,并為類設(shè)置初始值(方法區(qū))- public static int v=1;
- 在準(zhǔn)備階段中個,v會被設(shè)置為0
- 在初始化的<clinit>中才會被設(shè)置為1
- 對于static final類型,在準(zhǔn)備階段就會被賦上準(zhǔn)確的值
- public static final int v=1
2.3 解析
- 字符引用轉(zhuǎn)換為直接引用:com.XXX.sudent這個就是個符號,直接引用就是它在內(nèi)存的地址
-
初始化
執(zhí)行類構(gòu)造器<clinit>方法,<clinit>方法由靜態(tài)代碼塊、靜態(tài)變量構(gòu)成和賦值語句構(gòu)成。- <clinit>方法線程安全: 基于它可以實現(xiàn)線程安全單例,參考單例模式
- <clinit>方法執(zhí)行時,發(fā)現(xiàn)父類的沒有執(zhí)行先執(zhí)行父類的<clinit>方法
使用
卸載
類加載器
類加載器分類
啟動類加載器
擴展加載器
應(yīng)用類加載器
自定義類加載器
備注:類加載器之間不是繼承的關(guān)系,而是組合的關(guān)系。
雙親委派模式
類加載器:字底向上查找是否已經(jīng)加載類,自頂向下加載類。

雙親委派模式:為了維護類加載的安全。
破壞雙親委派模式
-
接口在rt.jar實現(xiàn)在應(yīng)用程序,啟動類加載器如何加載應(yīng)用類加載器的類,例如:SPI機制
線程上線文類加載器可以解決這個問題,它模式的類加載器是應(yīng)用類加載器。
熱替換
問題 1:
static{
a = 300;
System.out.println(a);//這里會報錯,聲明語句之前只能賦值不能引用
}
public static int a = 10;
問題 2:
類加載中只用類變量及靜態(tài)代碼執(zhí)行,那么構(gòu)造方法、普通代碼塊啥時候執(zhí)行呢,其實這個不屬于類加載,它屬于類的實例化,在類的實例化是執(zhí)行。
public class Line {
static {
System.out.println("靜態(tài)代碼塊執(zhí)行:loading line");
}
public static String s = getString();
private static String getString() {
System.out.println("給靜態(tài)變量賦值的靜態(tài)方法執(zhí)行:loading line");
return "ss";
}
public static void test() {
System.out.println("普通靜態(tài)方法執(zhí)行:loading line");
}
public Line() {
System.out.println("構(gòu)造方法執(zhí)行:loading line");
}
{
System.out.println("構(gòu)造代碼塊執(zhí)行");
}
}
public class CodeBlockTest {
public static void main(String[] args) {
System.out.println("主方法");
{
System.out.println("main方法中最開始的,普通代碼塊執(zhí)行");
}
Line line = new Line();
System.out.println("...............");
Line line1 = new Line();
System.out.println("...............");
{
System.out.println("main方法中結(jié)尾事的,普通代碼塊執(zhí)行");
}
}
}
結(jié)果大家自己執(zhí)行,注意下:構(gòu)造代碼塊在創(chuàng)建對象時被調(diào)用,每次創(chuàng)建對象都會被調(diào)用,并且構(gòu)造代碼塊的執(zhí)行次序優(yōu)先于類構(gòu)造函數(shù)。
問題 3:
public class Singleton {
private static Singleton mInstance = new Singleton();// 位置1
public static int counter1;
public static int counter2 = 0;
// private static Singleton mInstance = new Singleton();// 位置2
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getInstance() {
return mInstance;
}
}
class InitDemo {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
System.out.println("counter1: " + singleton.counter1);
System.out.println("counter2: " + singleton.counter2);
}
}
位置1:
counter1=1
counter2=0
位置2:
counter1=1
counter2=1