Klass模型
Java的每個(gè)類咬腕,在JVM中都有一個(gè)對(duì)應(yīng)的Klass與之對(duì)應(yīng) ,存儲(chǔ)類的元信息如:常量池葬荷、屬性信息涨共、方法信息……
Java中創(chuàng)建普通Java對(duì)象類對(duì)應(yīng)在jvm存在形式為: InstanceKlass
Java創(chuàng)建array數(shù)組在對(duì)應(yīng)在jvm存在的行為為 :ArrayKlass
InstanceMirrorKlass:是用來(lái)表示java.lang.Class,java代碼中獲取到的Class對(duì)象宠漩,實(shí)際上就是這個(gè)C++類的實(shí)例举反,存儲(chǔ)在堆區(qū),學(xué)名:鏡像類扒吁。
InstanceRefKlass:用來(lái)表示java.lang.ref.Reference類的子類照筑。
InstanceClassLoaderKlass:用于遍歷某個(gè)加載器加載的類
Java數(shù)組的元信息用ArrayKlass的子類來(lái)表示:
TypeArrayKlass是進(jìn)行存儲(chǔ) 基本類型數(shù)據(jù)
ObjArrayKlass 是進(jìn)行存儲(chǔ)對(duì)象數(shù)據(jù)(引用類型)
備注:java的數(shù)組不是靜態(tài)數(shù)據(jù)類型,是動(dòng)態(tài)數(shù)據(jù)類型,即在jvm運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建的
類的加載過(guò)程
類的生命周期是由7個(gè)階段組成凝危,但是類的加載說(shuō)的是前5個(gè)階段
加載階段:
? ?1.通過(guò)全限定名加載class文件
? ?2. 解析成運(yùn)行時(shí)數(shù)據(jù),即instanceklass數(shù)據(jù)
何時(shí)加載
主動(dòng)使用時(shí)
1晨逝、new蛾默、getstatic、putstatic捉貌、invokestatic
2支鸡、反射
3、初始化一個(gè)類的子類會(huì)去加載其父類
4趁窃、啟動(dòng)類(main函數(shù)所在類)
5牧挣、當(dāng)使用jdk1.7動(dòng)態(tài)語(yǔ)言支持時(shí),如果一個(gè)java.lang.invoke.MethodHandle實(shí)例最后的解析結(jié)果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄醒陆,并且這個(gè)方法句柄所對(duì)應(yīng)的類沒有進(jìn)行初始化瀑构,則需要先出觸發(fā)其初始化
預(yù)加載:包裝類、String刨摩、Thread
校驗(yàn)階段:
? ??1寺晌、文件格式驗(yàn)證
? ? 2、元數(shù)據(jù)驗(yàn)證
? ? 3澡刹、字節(jié)碼驗(yàn)證
? ? 4呻征、符號(hào)引用驗(yàn)證
準(zhǔn)備階段:
? ?為靜態(tài)變量分配內(nèi)存、賦初值
? 實(shí)例變量是在創(chuàng)建對(duì)象的時(shí)候完成賦值的罢浇,沒有賦初值一說(shuō)
? 如果被final修飾陆赋,在編譯的時(shí)候會(huì)給屬性添加ConstantValue屬性,準(zhǔn)備階段直接完成賦值嚷闭,即沒有賦初值這一步
解析階段:
將常量池中的符號(hào)引用轉(zhuǎn)為直接引用
解析后的信息存儲(chǔ)在ConstantPoolCache類實(shí)例中
1攒岛、類或接口的解析
2、字段解析
3凌受、方法解析
4阵子、接口方法解析
?何時(shí)進(jìn)行解析
????思路:
????1、加載階段解析常量池時(shí)
? ? 2胜蛉、用的時(shí)候
????openjdk是第二種思路挠进,在執(zhí)行特定的字節(jié)碼指令之前進(jìn)行解析:
????anewarray、checkcast誊册、getfield领突、getstatic、instanceof案怯、invokedynamic君旦、invokeinterface、invokespecial、????invokestatic金砍、invokevirtual局蚀、ldc、ldc_w恕稠、ldc2_w琅绅、multianewarray、new鹅巍、putfield
初始化階段:
執(zhí)行靜態(tài)代碼塊千扶,完成靜態(tài)變量的賦值
靜態(tài)字段、靜態(tài)代碼段骆捧,字節(jié)碼層面會(huì)生成clinit方法
方法中語(yǔ)句的先后順序與代碼的編寫順序相關(guān)
代碼示例:
? ?public class Test_1 {? ?
public static void main(String[] args) {? ?
? ? System.out.printf(Test_1_B.str);?
? }}
class Test_1_A {? ?
public static String str = "A str";? ?
static {?
? ? ? System.out.println("A Static Block");?
? }}
class Test_1_B extends Test_1_A {?
? static {? ?
? ? System.out.println("B Static Block");?
? }}
1澎羞、輸出是? A Static Block 、A str? 將不輸出B StaticBlocK 敛苇,因?yàn)槭褂脤?duì)象的過(guò)程中并沒有涉及到子類的變量
2妆绞、如果將str 靜態(tài)變量定義在子類中則輸出的信息則為A Static Block、B Static Block接谨、A str? ?因?yàn)檫@是使用的變量為子類的信息摆碉,這是將會(huì)進(jìn)行初始化
3、如果將子類的str類型加final變量修飾脓豪,則只輸出 A str?
4巷帝、如果將子類的str 使用UUID.randomUUID方法賦值 ,則會(huì)輸出A Static Block扫夜、B Static Block楞泼、A str? ,因?yàn)閁UID所使用的發(fā)法需要進(jìn)行實(shí)例化才可進(jìn)行賦初始值笤闯。
則上述示例則可證明jvm使用的是懶加載
public class Test_1 {?
? public static void main(String[] args) {?
? ? ? System.out.println(Test_1_A.a);?
? ? ? System.out.println(Test_1_A.b);?
? }}
class Test_1_A {
public static int a;
public static int b = 1;
public static Test_1_A test1 = getInstance();
? ? public Test_1_A()? {? ?
? ? a++;? ? ? ? b++;? ? }
public static Test_1_A getInstance(){
return new Test_1_A();
}}
1】輸出結(jié)果為 1堕阔,2 因?yàn)樵趫?zhí)行實(shí)例化對(duì)象之前 a默認(rèn)為0 b默認(rèn)為 1 執(zhí)行實(shí)例化后進(jìn)行默認(rèn)+1操作
2】如果將public static int b = 1; 放在實(shí)例化方法之后 則輸出為1,1 因?yàn)樵趫?zhí)行為初始化之后才執(zhí)行的 調(diào)用b 則會(huì)將b值進(jìn)行重新賦值這個(gè)結(jié)果就驗(yàn)證了 在類加載【方法中語(yǔ)句的先后順序與代碼的編寫順序相關(guān)】