類的加載:
- 類的加載的最終產品是位於堆區中的
Class
對象 Class
對象封裝了類在方法區內的數據結構,並且向JAVA程序員提供了訪問方法區內的數據結構的接口- 類加載器的種類:
- java虛擬機自帶的加載器
(1)根類加載器(Bootstrap),由C++編寫,無法在java代碼中獲取該類
(2)擴展類加載器(Extension),使用Java代碼實現
(3)系統(或者應用)類加載器(System),使用Java代碼實現 - 用戶自定義的類加載器
(1)java.lang.ClassLoader的子類
(2)用戶可以定義類的加載方式
3.類加載器並不需要等到某個類被“首次使用”時再加載它:
(1) jvm規範允許類加載在預料某個類將要被使用時就預先加載它,如果在預先加載的過程中遇到了.Class
文件缺失或者存在錯誤,類加載器必須在程序首次主動使用該類時才報告錯誤(LinkageError錯誤)。
(2) 如果一個類一直沒有被程序主動使用,那麼類加載器就不會報告錯誤
(3) 類被加載後,就進入連接階段。連接就是將已經讀入到內存的類的二進制數據合併到虛擬機的運行時環境中去。
- java虛擬機自帶的加載器
類的驗證:
- 類文件的結構檢查:確保類文件遵從java文件的固定格式
- 語義檢查:確保類本身符合java語言的語法規定,比如校驗final類型的類沒有子類以及final類型的方法沒有被覆蓋(可防止惡意用戶的惡意操作)
- 字節碼驗證:確保字節碼流可以被java虛擬機安全地執行。字節碼流代表java方法(包括靜態方法和實例方法),他是由被稱做操作碼的單字節指令組成的序列,每一個操作碼後都跟着一個或者多個操作數。字節碼驗證步驟會檢查每個操作碼是否合法,即是否有着合法的操作數。
- 二進制兼容性的驗證:確保相互引用的類之間協調一致。例如在
Worker
類的goWork()
方法中會調用Car
類的run()
方法。java虛擬機在驗證Worker
類時,會檢查方法在方法區內是否存在Car
類的run()
方法,假如不存在(當Worker
類和Car
類的版本不兼容,就會出現這種問題,就會拋出NoSuchMethodError
錯誤)
類的準備:
在準備階段,java虛擬機爲類的靜態變量分配內存,並設置默認的初始值。例如對於以下Simple
類,在準備階段,將int
類型的靜態變量a
分配4
個字節的內存空間,並且賦予默認值0
。爲long
類型的靜態變量b
分配8
個字節的內存空間,並且賦予默認值0
public class Simple {
private static int a=1;
public static long b;
static {
b=2;
}
}
類的解析階段:
在解析階段,java虛擬機會把類的二進制數據中的符號引用替換爲直接引用,例如在Worker
類的goWork()
方法中會引用Car
類的run()
方法.
public void goWork(){
ca.run();//表示符號引用
}
在Worker
類的二進制數據中包含了一個對Car
的run()
方法的符號引用,它由run()
方法的全類名和相關描述符組成。在解析階段,虛擬機會把這個符號引用直接替換爲一個指針,該指針指向Car
類的run()
方法在方法區內的內存位置,這個指針就是直接引用。
類的初始化:
1.在初始化階段,java虛擬機執行類的初始化語句,爲類的靜態變量賦予初始值。在程序中,靜態變量初始化有兩種途徑:
- 在靜態變量的聲明處進行初始化
- 在靜態代碼塊中進行初始化。
例如在以下代碼中,靜態變量a
和b
都被顯式初始化,而c
沒有被顯式初始化,它將默認保持默認值0
public class Simple {
private static int a=1;
public static long b;
public static long c;
static {
b=2;
}
}
2.類的初始化的步驟:
- 假如這個類還
沒有加載和連接
,那就先進行加載和連接 - 假如類存在直接的父類,並且這個父類還沒有被初始化,那就先初始化直接的父類
- 假如類中存在直接的初始化語句,那就依次執行這些初始化語句。
3.類的初始化的時機:
當java虛擬機初始化一個類時,要求它的所有父類都已經被初始化,但是這條規則並不適用於接口。
- 在初始化一個類時,並不會先初始化它所實現的接口
- 在初始化一個接口時,並不會先初始化它的父接口
因此,一個父接口並不會因爲它的子接口或者實現類的初始化而初始化,只有當程序首次使用特定接口的靜態變量時,纔會導致接口的初始化。
使用階段:
在類的使用過程中依然存在三步:對象實例化、垃圾收集、對象終結。
- 對象實例化:就是執行類中構造函數的內容,如果該類存在父類JVM會通過顯示或者隱示的方式先執行父類的構造函數,在堆內存中爲父類的實例變量開闢空間,並賦予默認的初始值,然後在根據構造函數的代碼內容將真正的值賦予實例變量本身,然後,引用變量獲取對象的首地址,通過操作對象來調用實例變量和方法 。
- 垃圾收集:當對象不再被引用的時候,就會被虛擬機標上特別的垃圾記號,在堆中等待GC回收
- 對象的終結:對象被GC回收後,對象就不再存在,對象的生命也就走到了盡頭
卸載階段:
即類的生命週期走到了最後一步,程序中不再有該類的引用,該類也就會被JVM執行垃圾回收,從此生命結束…