做爲的學習筆記,類的加載機制主要從類的加載,連接,以及初始化 ,類加載器除了根類加載器之外,其他類加載器都是用java編寫的,所以我們程序員可以開發自己的類加載器,通過使用自定義類加載器,可以完成一些特定的功能。
1 jvm 和類的認識
當我們通過java命令運行java程序時,這個命令就會啓動一個java虛擬機進程,我們的java程序就處於這個進程中,java程序中的所有線程,所有變量都處於jvm的同一個進程裏,它們都使用該jvm進程的內存區。
當系統出現一下情況時,jvm進程將被終止
》程序運行到最後正常結束
》程序所在平臺強制結束了jvm進程
》 程序執行過程中遇到未捕獲的異常或者錯誤而結束
》 程序運行到使用System.exit() 或者Runtime.getRuntime().exit()代碼處結束程序
java程序結束,jvm進程結束,該進程在內存中的狀態將會失去,下面舉例子說明:
& 首先創建一個靜態變量 q
public class TestQ {
//定義一個靜態變量
public static int q = 4;
}
& 定義一個測試類 創建TestQ實例,並訪問該實例的靜態變量q
public class TestTQ {
public static void main(String[] args) {
TestQ t = new TestQ();
t.q++;
System.out.println(t.q);
}
}
& 創建第二測試類 並訪問類變量q
public static void main(String[] args) {
TestQ t2 = new TestQ();
System.out.println(t2.q);
}
}
我們首先運行第一個測試類 訪問q 自增了1 程序給出的結果是5 相信大家對這個結果沒有什麼疑問把,而我們再來運行第二個測試類的結果是多少呢,依然是4 ,爲什麼呢? 原因是兩次運行java程序處於2個不同的jvm進程中,第一次運行jvm結束後,它對q所做的修改將全部失去,兩個jvm之間不會共享數據。
2 類的加載
類的加載: 當程序主動使用 一個類時 ,如果該類沒有被加載時,則jvm會通過 加載 連接 初始化三個步逐來對類進行初始化操作 ,三個操作一次完成,把這三個步逐統稱爲類加載或類初始化,類加載就是將類的class文件讀入內存,併爲之創建一個java.lang.Class對象 。
類的加載又類加載器完成的,類加載器通常是由jvm提供,這些類加載器也是程序運行的基礎,類加載器分爲2類,一是jvm提供的系統類加載器,另一個是 開發者可以通過繼承ClassLoader基類來創建自己的類加載器,
通過使用不同的類加載器。可以從不同來源加載類的二進制數據,通常有如下幾種來源:
》 從本地系統文件加載class文件
》從jar包加載class文件
》通過網絡加載class 文件
》把一個java 源文件動態編譯,並執行加載
類的加載不一定都是在”第一次使用“ 才進行加載,java虛擬機規範允許系統預先加載一些類。
3 類的連接
當類被加載了之後,系統會爲之生成一個對應的class對象,接着將會進入連接階段,連接階段負責把類的二進制數據合併到jre中,而類的連接又分爲三個階段
》 驗證: 驗證階段用於檢測被加載的類是否又正確的內部結構,並且和其他類協調一致
》 準備 :類準備階段則負責爲類變量分配內存,並設置默認初始值
》 解析:將類的二進制數據中的符號引用替換成直接引用
4 類的初始化
虛擬機負責對類進行初始化,主要就是對類變量進行初始化,在java類中對類變量指定初始值有2種方式:1是 聲明變量時指定初始值 2是 使用靜態初始化塊爲類變量指定初始值 靜態初始化塊都將被當成類的初始化語句,jvm會按照這些語句在程序中的排序順序依次執行它們。
public class Test01 {
static{
int b = 8;
System.out.println("-------");
}
static int a = 3;
static int b = 12;
static int c;
public static void main(String[] args) {
System.out.println(Test01.b);
System.out.println(Test01.c);
}
}
其中b的值爲12 ,c 的值爲0 ,b在第二次賦值會把其中的值替換掉,沒有賦予值的int 類型,默認值爲0。
jvm 初始化一個類包含如下幾個步逐
》 如果這個類還沒有被加載和連接。則程序先加載並且連接該類
》 如果該類的直接父類還沒有被初始化,則先初始化其直接父類(jvm 最先初始化的總是java.lang.Object類)
》如果類中有初始化語句,則系統一次執行這些初始化語句
當程序主動使用一個類時,系統會保證該類以及所用父類都會被初始化。
5 類初始化的時機
》 調用一個類的靜態方法
》訪問一個類或接口的類變量,或爲類變量賦值
》 使用反射方式來強制創建一個類或者接口對應的java.lang.Class對象 (Class.forName("Person"))
》初始化一個類的子類
》直接使用java.exe命令來運行一主類 程序先初始化主類