類加載的時(shí)機(jī)
主動(dòng)引用(有且只有下面的五種)
使用new關(guān)鍵字創(chuàng)建對(duì)象時(shí)扒秸,訪問類的靜態(tài)字段時(shí)敛苇;
使用java.lang.reflect包的方法進(jìn)行反射調(diào)用的時(shí)候逗威;
初始化一個(gè)類時(shí)构眯,如果父類沒有被初始化寥裂,則需要先初始化朱监;
當(dāng)虛擬機(jī)啟動(dòng)的時(shí)候岸啡,虛擬機(jī)先會(huì)初始化主類(main()方法所在的類)
JDK1.7動(dòng)態(tài)語言支持;
被動(dòng)引用(類不會(huì)加載)
子類引用父類的靜態(tài)字段赫编,不會(huì)導(dǎo)致子類的的初始化巡蘸。
通過數(shù)組定義引用類奋隶。
訪問類中的常量;
類加載流程
-
加載
1.1 加載二進(jìn)制字節(jié)流到內(nèi)存
1.2 生成方法區(qū)數(shù)據(jù)結(jié)構(gòu)
1.3 生成java.lang.Class對(duì)象
-
連接
2.1 驗(yàn)證
- 文件格式驗(yàn)證: 版本號(hào)悦荒,是不是
- 元數(shù)據(jù)驗(yàn)證:是否基礎(chǔ)了final類唯欣?是不是有父類?是不是實(shí)現(xiàn)了抽象類的方法搬味?
- 字節(jié)碼驗(yàn)證:跳轉(zhuǎn)指令是否合法境氢?
- 字符引用驗(yàn)證:引用的類是否存在?類及接口是否有訪問權(quán)限碰纬?
2.2 準(zhǔn)備
類變量(即靜態(tài)變量而不是實(shí)例變量)分配內(nèi)存萍聊,并為類設(shè)置初始值(方法區(qū))- public static int v=1;
- 在準(zhǔn)備階段中個(gè),v會(huì)被設(shè)置為0
- 在初始化的<clinit>中才會(huì)被設(shè)置為1
- 對(duì)于static final類型悦析,在準(zhǔn)備階段就會(huì)被賦上準(zhǔn)確的值
- public static final int v=1
2.3 解析
- 字符引用轉(zhuǎn)換為直接引用:com.XXX.sudent這個(gè)就是個(gè)符號(hào)寿桨,直接引用就是它在內(nèi)存的地址
-
初始化
執(zhí)行類構(gòu)造器<clinit>方法,<clinit>方法由靜態(tài)代碼塊强戴、靜態(tài)變量構(gòu)成和賦值語句構(gòu)成亭螟。- <clinit>方法線程安全: 基于它可以實(shí)現(xiàn)線程安全單例,參考單例模式
- <clinit>方法執(zhí)行時(shí)骑歹,發(fā)現(xiàn)父類的沒有執(zhí)行先執(zhí)行父類的<clinit>方法
使用
卸載
類加載器
類加載器分類
啟動(dòng)類加載器
擴(kuò)展加載器
應(yīng)用類加載器
自定義類加載器
備注:類加載器之間不是繼承的關(guān)系预烙,而是組合的關(guān)系。
雙親委派模式
類加載器:字底向上查找是否已經(jīng)加載類道媚,自頂向下加載類扁掸。
雙親委派模式:為了維護(hù)類加載的安全。
破壞雙親委派模式
-
接口在rt.jar實(shí)現(xiàn)在應(yīng)用程序最域,啟動(dòng)類加載器如何加載應(yīng)用類加載器的類也糊,例如:SPI機(jī)制
線程上線文類加載器可以解決這個(gè)問題,它模式的類加載器是應(yīng)用類加載器羡宙。
熱替換
問題 1:
static{
a = 300;
System.out.println(a);//這里會(huì)報(bào)錯(cuò),聲明語句之前只能賦值不能引用
}
public static int a = 10;
問題 2:
類加載中只用類變量及靜態(tài)代碼執(zhí)行掐隐,那么構(gòu)造方法狗热、普通代碼塊啥時(shí)候執(zhí)行呢,其實(shí)這個(gè)不屬于類加載虑省,它屬于類的實(shí)例化匿刮,在類的實(shí)例化是執(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)建對(duì)象時(shí)被調(diào)用伪节,每次創(chuàng)建對(duì)象都會(huì)被調(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