JVM執行和類載入全過程
一、 為什麼研究類載入全過程
- 有助於瞭解JVM執行過程
- 更深入瞭解java動態性(瞭解熱部署、動態載入)提高程式的靈活性。
二、 類載入機制
JVM把class檔案載入到記憶體,並對資料進行校驗、解析和初始化、最終形成JVM可以直接使用的Java型別的過程。
—、載入
將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉化成方法區中的執行時資料結果,在堆中生成一個代表這個類的java.lang.Class物件,作為方法區類資料的訪問入口,這個過程需要類載入器參與。
二、連結
將Java類的二進位制程式碼合併到JVM的執行狀態之中的過程
驗證:
確保載入的類資訊符合JVM規範,沒有安全方面的問題。
準備:
正式為類變數的(static變數) 分配記憶體並設定類變數初始值的階段,這些記憶體都將在方法區中進行分配
解析:
虛擬機器常量池內的符號引用替換為直接引用的過程。
初始化
·初始化階段的階段是執行類構造器()方法的過程。類構造器()方法是由編譯器自動收集類中的所有類變數的賦值動作和靜態語句塊(static塊)中的語句合併產生的
·當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化
·虛擬機器會保證一個類的()方法在多執行緒環境中被正確加鎖和同步
·當訪問一個java類的靜態域時,只有真正宣告這個域的類才會被初始化。
package com.lyy.test;
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
A a = new A();
System.out.println(A.width);
}
}
class A {
public static int width=100; //靜態變數 ,靜態域 field
static{
System.out.println("靜態初始化類A");
width=300 ;
}
public A(){
System.out.println("建立A類的物件");
}
}
類的主動引用(一定會發生類的初始化)
- New一個類的物件
- 呼叫類的靜態成員(除了final常量)和靜態方法
- 使用java.lang.reflect包的方法對類進行反射呼叫
- 當虛擬機器啟動,java Hello ,則一定會初始化Hello類,說白了就是先啟動main方法所在的類
- 當初始化一個類,如果其父類沒有被初始化,則先會初始化他的父類
類的被動引用(不會發生類的初始化)
- 訪問一個靜態域時,只有真正聲名這個域的類才會被初始化
- 通過子類引用父類的靜態變數,不會導致子類初始化
- 通過陣列定義類引用,不會觸發此類的初始化
- 引用常量不會觸發此類的初始化(常量在編譯階段就存入呼叫類的常量池中了)
package com.lyy.test;
public class Demo1 {
static{
System.out.println("靜態初始化Demo1");
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("Demo1的main方法");
// A a = new A();
// System.out.println(A.width);
// A a2 = new A();
//主動引用
new A();
System.out.println(A.width);
Class.forName("com.lyy.test.A");
//被動引用
// System.out.println(A.MAX);
// A[] as = new A[10];
System.out.println(B.width);
}
}
class B extends A{
static{
System.out.println("靜態初始化B");
}
}
class A extends A_Father{
public static int width=100; //靜態變數 ,靜態域 field
public static final int MAX=100;
static{
System.out.println("靜態初始化類A");
width=300;
}
public A(){
System.out.println("建立A類的物件");
}
}
class A_Father extends Object{
static {
System.out.println("靜態初始化A_Father");
}
}