java中的類大體分爲三種:
1).系統類
2).擴展類
3).由程序員自定義的類
類裝載方式,有兩種
1).隱式裝載, 程序在運行過程當中當碰到經過new 等方式生成對象時,隱式調用類裝載器加載對應的類到jvm中
2).顯式裝載, 經過class.forname()等方法,顯式加載須要的類
類加載的動態性體現
一個應用程序老是由n多個類組成,Java程序啓動時,並非一次把全部的類所有加載後再
運行,它老是先把保證程序運行的基礎類一次性加載到jvm中,其它類等到jvm用到的時候再加載,這樣的好處是節省了內存的開銷,由於java最先就是爲嵌入式系統而設計的,內存寶貴,這是一種能夠理解的機制,而用到時再加載這也是java動態性的一種體現
java類裝載器
Java中的類裝載器實質上也是類,功能是把類載入jvm中,值得注意的是jvm的類裝載器並非一個,而是三個,層次結構以下:
Bootstrap Loader - 負責加載系統類
|
- - ExtClassLoader - 負責加載擴展類
|
- - AppClassLoader - 負責加載應用類
爲何要有三個類加載器,一方面是分工,各自負責各自的區塊,另外一方面爲了實現委託模型,下面會談到該模型java
可見性機制
可見性的原理是子類的加載器能夠看見全部的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。
單一性機制
根據這個機制,父加載器加載過的類不能被子加載器加載第二次。雖然重寫違反委託和單一性機制的類加載器是可能的,但這樣作並不可取。程序員
委託機制緩存
1. 當前ClassLoader首先從本身已經加載的類中查詢是否此類已經加載,若是已經加載則直接返回原來已經加載的類。
每一個類加載器都有本身的加載緩存,當一個類被加載了之後就會放入緩存,等下次加載的時候就能夠直接返回了。
2. 當前classLoader的緩存中沒有找到被加載的類的時候,委託父類加載器去加載,父類加載器採用一樣的策略,首先查看本身的緩存,而後委託父類的父類去加載,一直到bootstrp ClassLoader.
3. 當全部的父類加載器都沒有加載的時候,再由當前的類加載器加載,並將其放入它本身的緩存中,以便下次有加載請求的時候直接返回。安全
類裝載過程:jvm
1) 裝載:查找並加載類的二進制數據;
2)連接:
驗證:確保被加載類的正確性;
準備:爲類的靜態變量分配內存,並將其初始化爲默認值;
解析:把類中的符號引用轉換爲直接引用;spa
(符號引用就是字符串,這個字符串包含足夠的信息,以供實際使用時能夠找到相應的位置。你好比說某個方法的符號引用,如:「java/io/PrintStream.println:(Ljava/lang/String;)V」。裏面有類的信息,方法名,方法參數等信息。設計
當第一次運行時,要根據字符串的內容,到該類的方法表中搜索這個方法。運行一次以後,符號引用會被替換爲直接引用,下次就不用搜索了。直接引用就是偏移量,經過偏移量虛擬機能夠直接在該類的內存區域中找到方法字節碼的起始位置。)對象
3)初始化:爲類的靜態變量賦予正確的初始值;
那爲何我要有驗證這一步驟呢?首先若是由編譯器生成的class文件,它確定是符合JVM字節碼格式的,可是萬一有高手本身寫一個class文件,讓JVM加載並運行,用於惡意用途,就不妙了,所以這個class文件要先過驗證這一關,不符合的話不會讓它繼續執行的,也是爲了安全考慮吧。
準備階段和初始化階段看似有點牟盾,實際上是不牟盾的,若是類中有語句:private static int a = 10,它的執行過程是這樣的,首先字節碼文件被加載到內存後,先進行連接的驗證這一步驟,驗證經過後準備階段,給a分配內存,由於變量a是static的,因此此時a等於int類型的默認初始值0,即a=0,而後到解析(後面在說),到初始化這一步驟時,才把a的真正的值10賦給a,此時a=10。接口
類何時初始化
類何時才被初始化:
1)建立類的實例,也就是new一個對象
2)訪問某個類或接口的靜態變量,或者對該靜態變量賦值
3)調用類的靜態方法
4)反射(Class.forName("com.lyj.load"))
5)初始化一個類的子類(會首先初始化子類的父類)
6)JVM啓動時標明的啓動類,即文件名和類名相同的那個類
只有這6中狀況纔會致使類的類的初始化。
類的初始化步驟:
1)若是這個類尚未被加載和連接,那先進行加載和連接
2)假如這個類存在直接父類,而且這個類尚未被初始化(注意:在一個類加載器中,類只能初始化一次),那就初始化直接的父類(不適用於接口)
3)加入類中存在初始化語句(如static變量和static塊),那就依次執行這些初始化語句。
內存