public class Book {
public static void main(String[] args) {
staticFunction();
}
static Book book = new Book();
static {
System.out.println("書的靜態代碼塊");
}
{
System.out.println("書的普通代碼塊");
}
Book() {
System.out.println("書的構造方法");
System.out.println("price=" + price + ",amount=" + amount);
}
public static void staticFunction() {
System.out.println("書的靜態方法");
System.out.println(amount);
}
int price = 110;
static int amount = 112;
}
從上面一個例子的結果進行分析。
先看執行結果:
書的普通代碼塊
書的構造方法
price=110,amount=0
書的靜態代碼塊
書的靜態方法
112
兩道面試題,帶你解析Java類加載機機制這篇文章里寫的很詳細。
這里大體寫一下過程。
Java類加載7個過程
Java類加載分7個過程。分別是,加載,驗證,準備,解析,初始化,使用,卸載
。
類加載
- 加載
加載就是將class文件載入jvm中。 - 驗證
主要校驗載入的class是否符合jvm規范。比如魔數校驗,版本號校驗,邏輯驗證。 - 準備
主要是為類變量分片內存并給類變量設置初始值。即只分配static修飾的值。例如:private static int var=12
;這行代碼在準備階段內存已經分配完成,此時的var變量為0. 那么什么時候賦給12呢? 在類初始化的時候。即執行<clinit>()時。
也有例外。private static final int var = 12
.此時的var變量為12. - 解析
這個階段的主要任務是將其在常量池中的符號引用替換成直接其在內存中的直接引用。 - 初始化
一般來說當 JVM 遇到下面 5 種情況的時候會觸發初始化:
? 遇到 new、getstatic、putstatic、invokestatic 這四條字節碼指令時,如果類沒有進行過初始化,則需要先觸發其初始化。生成這4條指令的最常見的Java代碼場景是:使用new關鍵字實例化對象的時候、讀取或設置一個類的靜態字段(被final修飾、已在編譯器把結果放入常量池的靜態字段除外)的時候,以及調用一個類的靜態方法的時候。
? 使用 java.lang.reflect 包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先觸發其初始化。
? 當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。
? 當虛擬機啟動時,用戶需要指定一個要執行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
? 當使用 JDK1.7 動態語言支持時,如果一個 java.lang.invoke.MethodHandle實例最后的解析結果 REF_getstatic,REF_putstatic,REF_invokeStatic 的方法句柄,并且這個方法句柄所對應的類沒有進行初始化,則需要先出觸發其初始化。
初始化主要是執行<clinit>()方法的過程。
<clinit>()方法過程如下:
(1)首先收集類變量,包括static修飾的變量和static修飾的代碼塊。順序是源碼中的順序。
(2)如果子類初始化的時候,發現父類沒有初始化,則先初始化父類。
其中,除了類初始化,還有對象初始化。即new出一個對象。其執行的<init>()方法。
VM 會按照收集成員變量的賦值語句、普通代碼塊,最后收集構造方法,將它們組成對象構造器,最終由 JVM 執行。
整個過程是先執行<clinit>(),再執行<init>().
按照上面的規則。我們來走讀一下例子里的代碼。
1、首先,main方法是入口,需要先初始化main方法所在的類。
類構造方法<clinit>()里執行的代碼如下:
static Book book = new Book();
static {
System.out.println("書的靜態代碼塊");
}
static int amount = 112;
其中new Book()
進行對象初始化<init>().
執行的代碼如下:
{
System.out.println("書的普通代碼塊");
}
int price = 110;
Book() {
System.out.println("書的構造方法");
System.out.println("price=" + price + ",amount=" + amount);
}
兩段代碼合起來的執行過程如下:
{
System.out.println("書的普通代碼塊");
}
int price = 110;
Book() {
System.out.println("書的構造方法");
System.out.println("price=" + price + ",amount=" + amount);
}
static {
System.out.println("書的靜態代碼塊");
}
static int amount = 112;
staticFunction();
從這里看到打印的結果:
書的普通代碼塊
書的構造方法
//此時amout還沒有賦值,還是初始值。
price=110,amount=0
書的靜態代碼塊
書的靜態方法
112