類加載機制:
jvm把class文件加載到內存,並對數據進行校驗、解析和初始化,最終形成jvm可以直接使用的java類型的過程。
(1)加載
將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個代表這個類的java.lang.Class對象,作為方法區類數據的訪問入口。
(2)鏈接 將java類的二進制代碼合並到jvm的運行狀態之中的過程
2.1 驗證
確保加載的類信息符合jvm規范,沒有安全方面的問題。
2.2 准備
正式為類變量(static變量)分配內存並設置類變量初始值的階段,這些內存都將在方法區中進行分配。
2.3 解析
虛擬機常量池內的符號引用替換為直接引用的過程。(比如String s ="aaa",轉化為 s的地址指向“aaa”的地址)
(3)初始化
初始化階段是執行類構造器方法的過程。類構造器方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊(static塊)中的語句合並產生的。
當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先初始化其父類的初始化
虛擬機會保證一個類的構造器方法在多線程環境中被正確加鎖和同步
當訪問一個java類的靜態域時,只有真正聲明這個靜態變量的類才會被初始化。
類加載過程分為:類的主動引用和類的被動引用
類的主動引用(一定會發生類的初始化)
--new一個類的對象
--調用類的靜態成員(除了final常量)和靜態方法
--使用java.lang.reflect包的方法對類進行反射調用
--當初始化一個類,如果其父類沒有被初始化,則先初始化他的父類
--當要執行某個程序時,一定先啟動main方法所在的類
類的被動引用(不會發生類的初始化)
--當訪問一個靜態變量時,只有真正生命這個靜態變量的類才會被初始化(通過子類引用父類的靜態變量,不會導致子類初始化)
--通過數組定義類應用,不會觸發此類的初始化 A[] a = new A[10];
--引用常量(final類型)不會觸發此類的初始化(常量在編譯階段就存入調用類的常量池中了)
類加載器的層次結構(樹狀結構)
引導類加載器(bootstrap class loader)
--他用類加載java 的核心庫(String 、Integer、List。。。)在jre/lib/rt.jar路徑下的內容,是用C代碼來實現的,並不繼承自java.lang.ClassLoader。
--加載擴展類和應用程序類加載器。並指定他們的父類加載器。
擴展類加載器(extensions class loader)
--用來加載java的擴展庫(jre/ext/*.jar路徑下的內容)java虛擬機的實現會自動提供一個擴展目錄。該類加載器在此目錄里面查找並加載java類。
應用程序類加載器(application class loader)
--他根據java應用的類路徑(classpath路徑),一般來說,java應用的類都是由他來完成加載的
自定義類加載器
--開發人員可以通過繼承java.lang.ClassLoader類的方式實現自己的類加載器,以滿足一些特殊的需求。
擴展類加載器、應用程序類加載器、自定義類加載器均是由java實現,都繼承java.lang.ClassLoader類。
類加載器的代理模式:雙親委托機制
--就是某個特定的類加載器在接收到加載類的請求后,首先將加載任務委托給父類加載器,一次追溯,直到最高的爺爺輩的,如果父類加載器可以完成類加載任務,就成功返回;只要父類加載器無法完成次加載任務時,才自己加載。
--雙親機制是為了保證java核心庫的類型安全,不會出現用戶自己能定義java.lang.Object類的情況。
雙親委托機制是代理模式的一種,並不是所有的類加載器都采用雙親委托機制,tomcat服務器類加載器也使用代理模式,所不同的是他是首先嘗試自己去加載某個類,如果找不到在代理給父類加載器。