生活随笔
收集整理的這篇文章主要介紹了
类加载的时机
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Java虛擬機把描述類的數據從Class文件加載到內存,并對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這就是虛擬機的加載機制。
? ?類從被加載到虛擬機內存中開始,到卸載出內存為止,它的整個生命周期包括了:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸載(Unloading)七個階段。其中驗證、準備和解析三個部分統稱為連接(Linking),這七個階段的發生順序如下圖所示:
? ? 如上圖所示,加載、驗證、準備、初始化和卸載這五個階段的順序是確定的,類的加載過程必須按照這個順序來按部就班地開始,而解析階段則不一定,它在某些情況下可以在初始化階段后再開始。
? ? 類的生命周期的每一個階段通常都是互相交叉混合式進行的,通常會在一個階段執行的過程中調用或激活另外一個階段。
? ? Java虛擬機規范沒有強制性約束在什么時候開始類加載過程,但是對于初始化階段,虛擬機規范則嚴格規定了有且只有四種情況必需立即對類進行“初始化”(而加載、驗證、準備階段則必需在此之前開始),這四種情況歸類如下:
? ? 1.遇到new、getstatic、putstatic或invokestatic這4條字節碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。生成這4條指令最常見的Java代碼場景是:使用new關鍵字實例化對象時、讀取或者設置一個類的靜態字段(被final修飾、已在編譯器把結果放入常量池的靜態字段除外)時、以及調用一個類的靜態方法的時候。
? ? 2.使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先觸發其初始化。
? ? 3.當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要觸發父類的初始化。
? ? 4.當虛擬機啟動時,用戶需要指定一個執行的主類(包含main()方法的類),虛擬機會先初始化這個類。
? ? 對于這四種觸發類進行初始化的場景,在java虛擬機規范中限定了“有且只有”這四種場景會觸發。這四種場景的行為稱為對類的主動引用,除此以外的所有引用類的方式都不會觸發類的初始化,稱為被動引用。
? ? 下面通過三個實例來說明被動引用:
示例一
父類SuperClass.java
?
Java代碼 ?
package?com.chenzhou.classloading;????????????public?class?SuperClass?{??????static{??????????System.out.println(”SuperClass?init!”);??????}??????public?static?int?value?=?123;??}??
package com.chenzhou.classloading;
/*** ClassName:SuperClass <br/>* Function: 被動使用類:通過子類引用父類的靜態字段,不會導致子類初始化. <br/>* Date: 2012-7-18 上午09:37:06 <br/>* @author chenzhou* @version * @since JDK 1.6* @see */
public class SuperClass {static{System.out.println("SuperClass init!");}public static int value = 123;
}
子類SubClass.java
Java代碼 ?
package?com.chenzhou.classloading;????public?class?SubClass?extends?SuperClass?{??????static{??????????System.out.println(”SubClass?init!”);??????}??}??
package com.chenzhou.classloading;public class SubClass extends SuperClass {static{System.out.println("SubClass init!");}
}
主類NotInitialization.java
Java代碼 ?
package?com.chenzhou.classloading;????????????public?class?NotInitialization?{??????public?static?void?main(String[]?args)?{??????????System.out.println(SubClass.value);??????}??}??
package com.chenzhou.classloading;
/*** ClassName:NotInitialization <br/>* Function: 非主動使用類字段演示. <br/>* Date: 2012-7-18 上午09:41:14 <br/>* @author chenzhou* @version * @since JDK 1.6* @see */
public class NotInitialization {public static void main(String[] args) {System.out.println(SubClass.value);}
}
?輸出結果:
Txt代碼 ?
SuperClass?init!??123??
SuperClass init!
123
?由結果可以看出只輸出了“SuperClass init!”,沒有輸出“SubClass init!”。這是因為對于靜態字段,只有直接定義該字段的類才會被初始化,因此當我們通過子類來引用父類中定義的靜態字段時,只會觸發父類的初始化,而不會觸發子類的初始化。
?
?
示例二
父類SuperClass.java如上一個示例一樣
主類NotInitialization.java
?
Java代碼 ?
package?com.chenzhou.classloading;????????????public?class?NotInitialization?{??????public?static?void?main(String[]?args)?{??????????SuperClass[]?scs?=?new?SuperClass[10];??????}??}??
package com.chenzhou.classloading;
/*** ClassName:NotInitialization <br/>* Function: 通過數組定義來引用類,不會觸發此類的初始化. <br/>* Date: 2012-7-18 上午09:41:14 <br/>* @author chenzhou* @version * @since JDK 1.6* @see */
public class NotInitialization {public static void main(String[] args) {SuperClass[] scs = new SuperClass[10];}
}
輸出結果為空
? ?沒有輸出“SuperClass init!”說明沒有觸發類com.chenzhou.classloading.SuperClass的初始化階段,但是這段代碼會觸發“[Lcom.chenzhou.classloading.SuperClass”類的初始化階段。這個類是由虛擬機自動生成的,該創建動作由newarray觸發。
?
示例三
常量類ConstClass.java
?
Java代碼 ?
package?com.chenzhou.classloading;?????????????public?class?ConstClass?{??????static{??????????System.out.println(”ConstClass?init!”);??????}????????????public?static?final?String?HELLOWORLD?=?“hello?world”;??}??
package com.chenzhou.classloading;
/*** ClassName:ConstClass <br/>* Function: 常量在編譯階段會存入調用類的常量池中,本質上沒有直接引用到定義常量的類,因此不會觸發定義常量的類的初始化. <br/>* Reason: TODO ADD REASON. <br/>* Date: 2012-7-18 上午09:46:56 <br/>* @author chenzhou* @version * @since JDK 1.6* @see */
public class ConstClass {static{System.out.println("ConstClass init!");}public static final String HELLOWORLD = "hello world";
}
主類NotInitialization.java
Java代碼 ?
package?com.chenzhou.classloading;????????????public?class?NotInitialization?{??????public?static?void?main(String[]?args)?{??????????System.out.println(ConstClass.HELLOWORLD);??????}??}??
package com.chenzhou.classloading;
/*** ClassName:NotInitialization <br/>* Function: 非主動實用類字段演示. <br/>* Date: 2012-7-18 上午09:41:14 <br/>* @author chenzhou* @version * @since JDK 1.6* @see */
public class NotInitialization {public static void main(String[] args) {System.out.println(ConstClass.HELLOWORLD);}
}
輸出:hello world
? ? 上面的示例代碼運行后也沒有輸出“SuperClass init!”,這是因為雖然在Java源碼中引用了ConstClass類中的常量HELLOWORLD,但是在編譯階段將此常量的值“hello world”存儲到了NotInitialization類的常量池中,對于常量ConstClass.HELLOWORLD的引用實際上都被轉化為NotInitialization類對自身常量池的引用了。實際上NotInitialization的Class文件之中已經不存在ConstClass類的符號引用入口了。
? ?接口的加載過程與類加載的區別在于上面提到的四種場景中的第三種,當類在初始化時要求其父類都已經初始化過了,但是一個接口在初始化時,并不要求其父類都完成了初始化,只有在真正用到父類接口的時候(如引用父接口的常量)才會初始化。
?
?
?
總結
以上是生活随笔為你收集整理的类加载的时机的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。