一、類裝載器的工做機制html
類裝載器就是類的字節碼文件並構造出類在JVM內部表示對象的組件。在Java中,類裝載器把一個類裝入JVM中,須要如下步驟:java
(1)裝載:查找和導入Class文件安全
(2)連接:執行校驗、準備和解析步驟,其中解析步驟是能夠選擇的。jsp
校驗:檢查載入Class文件數據的正確性。ide
準備:給類的靜態變量分配存儲空間。工具
解析:將符號引用轉換成直接引用。this
(3)初始化:對類的靜態變量、靜態代碼塊執行初始化工做。lua
類裝載工做由ClassLoader及其子類負責。ClassLoader是一個重要的Java運行時系統組件,它負責在運行時查找和裝入Class字節碼文件。JVM在運行時會產生3個ClassLoader:根裝載器、ExtClassLoader(擴展類裝載器)和AppClassLoader(應用類裝載器)。其中根裝載器不是ClassLoader的子類,它使用C++語言編寫,於是在Java中看不到它,根裝載器負責裝載JRE的核心類庫,如JRE目標下的rt.jar、charsets.jar等。ExtClassLoader和AppClassLoader都是ClassLoader的子類,其中ExtClassLoader負責裝載JRE擴展目錄ext中的JAR類包;AppClassLoader負責裝載Classpath路徑下的類包。spa
這三個類裝載器之間存在父子層級關係,即根裝載器是ExtClassLoader的父裝載器,ExtClassLoader是AppClassLoader的父裝載器。在默認狀況下,使用AppClassLoader裝載應用程序的類。.net
實驗代碼:
public static void main(String[] args) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); System.out.println("current loader:" + loader); System.out.println("parent loader:" + loader.getParent()); System.out.println("grandparent loader:" + loader.getParent().getParent()); }
輸出:
current loader:sun.misc.Launcher$AppClassLoader@18b4aac2 parent loader:sun.misc.Launcher$ExtClassLoader@66d3c617 grandparent loader:null
說明:
根裝載器在Java中訪問不到,因此返回null
擴展:
JVM裝載類時使用「全盤負責委託機制」
全盤負責:是指當一個ClassLoader裝載一個類是,除非顯式地使用另外一個ClassLoader,該類所依賴的及引用的類也由這個ClassLoader載入;
委託機制:是指先委託父裝載器尋找目標類,只有在找不到的狀況下才能從本身的類路徑下查找並裝載目標類。
這樣的設計是從安全角度考慮的,試想,若是有人編寫了一個惡意的基礎類(如java.lang.String)並裝載到JVM中,將會引發多麼可怕的後果?
實戰經驗:
在此我提供一個工具類,經過改類能夠方便的查看JVM從哪一個類包中加載指定類:
訪問方式:http://localhost:8080/srcAdd.jsp?className=java.net.URL
工具類頁面:
<%@page contentType="text/html; charset=GBK" %> <%@page import="java.security.*,java.net.*,java.io.*" %> <%! public static URL getClassLocation(final Class cls) { if (cls == null) throw new IllegalArgumentException("null input: cls"); URL result = null; final String clsAsResource = cls.getName().replace('.', '/').concat(".class"); final ProtectionDomain pd = cls.getProtectionDomain(); // java.lang.Class contract does not specify if 'pd' can ever be null; // it is not the case for Sun's implementations, but guard against null // just in case: if (pd != null) { final CodeSource cs = pd.getCodeSource(); // 'cs' can be null depending on the classloader behavior: if (cs != null) result = cs.getLocation(); if (result != null) { // Convert a code source location into a full class file location // for some common cases: if ("file".equals(result.getProtocol())) { try { if (result.toExternalForm().endsWith(".jar") || result.toExternalForm().endsWith(".zip")) result = new URL("jar:".concat(result.toExternalForm()) .concat("!/").concat(clsAsResource)); else if (new File(result.getFile()).isDirectory()) result = new URL(result, clsAsResource); } catch (MalformedURLException ignore) { } } } } if (result == null) { // Try to find 'cls' definition as a resource; this is not // document.d to be legal, but Sun's implementations seem to //allow this: final ClassLoader clsLoader = cls.getClassLoader(); result = clsLoader != null ? clsLoader.getResource(clsAsResource) : ClassLoader.getSystemResource(clsAsResource); } return result; } %> <html> <head> <title>srcAdd.jar</title> </head> <body bgcolor="#ffffff"> 使用方法,className參數爲類的全名,不須要.class後綴,如 srcAdd.jsp?className=java.net.URL <% try { String classLocation = null; String error = null; String className = request.getParameter("className"); classLocation = "" + getClassLocation(Class.forName(className)); if (error == null) { out.print("類" + className + "實例的物理文件位於:"); out.print("<hr>"); out.print(classLocation); } else { out.print("類" + className + "沒有對應的物理文件。<br>"); out.print("錯誤:" + error); } } catch (Exception e) { out.print("異常。" + e.getMessage()); } %> </body> </html>
工具類:
在IDEA斷點調試的時候,按Alt+F8,彈出Evluate Expression對話框,在Expression處輸入:ClassLocationUtils.where(<類名>.class) 便可獲知當前類是從哪一個jar包中加載的。
/** * tools to find which jar does the class come from * * @author : chenxh,ascend */ public class ClassLocationUtils { /** * find the location of the class come from * * @param cls Class * @return String */ public static String where(final Class cls) { if (cls == null) throw new IllegalArgumentException("null input: cls"); URL result = null; final String clsAsResource = cls.getName().replace('.', '/').concat(".class"); final ProtectionDomain pd = cls.getProtectionDomain(); if (pd != null) { final CodeSource cs = pd.getCodeSource(); if (cs != null) result = cs.getLocation(); if (result != null && "file".equals(result.getProtocol())) { try { if (result.toExternalForm().endsWith(".jar") || result.toExternalForm().endsWith(".zip")) result = new URL("jar:".concat(result.toExternalForm()) .concat("!/").concat(clsAsResource)); else if (new File(result.getFile()).isDirectory()) result = new URL(result, clsAsResource); } catch (MalformedURLException ignore) { } } } if (result == null) { final ClassLoader clsLoader = cls.getClassLoader(); result = clsLoader != null ? clsLoader.getResource(clsAsResource) : ClassLoader.getSystemResource(clsAsResource); } return result.toString(); } }
碼字不易,尊重原創:http://www.cnblogs.com/adeng/p/7596818.html