Java類加載的生命週期共分爲7個階段,分別是:加載、驗證、準備、解析、初始化、使用、卸載。其中驗證、準備、解析三部分稱爲鏈接。以下圖:java
public static int value = 111;
中在準備階段value
設置的值爲0。public static final int value = 122;
,那麼在準備階段value常量設置的值爲122。JDK9以前,Java應用主要由三種類加載器相互配合來完成加載的。程序員
啓用類加載器——Bootstrap ClassLoader
該類加載器主要是使用C/C++實現,並不在Java類庫中。
該加載器主要加載<JAVA_HOME>\Lib目錄下或被-Xbootclasspath
參數制定路徑中存放的而且Java虛擬機可以識別的類庫。安全
Java虛擬機安裝名字識別,如rt.jar、tools.jar, 其中名字不符合放在lib文件下也不會被加載
sun.misc.Launcher$ExtClassLoader
中以Java代碼的形式實現。應用程序類加載器——Application ClassLoader
該類是在sun.misc.Launcher$AppClassLoader
中以Java代碼的形式實現。
主要負責加載用戶類路徑(ClassPath)上的全部類庫。數據結構
上述三種類加載器的關係以下圖:ide
雙親委派模型要求除了頂層的啓動類加載器外,其他的類加載器都應有本身的父類加載器。不過這裏類加載器的父子關係並不是類的繼承關係,而是加載器之間的關係。模塊化
若是一個類加載器收到了類加載的請求,它不會首先本身嘗試加載這個類,而是把請求委託給父加載器來完成,每一層的類加載器都是如此,所以全部的加載請求最終都應傳到最頂層的啓動類加載器中。只有當父加載器沒法加載請求類時(在加載器搜索範圍內未找到類)。子加載器纔會嘗試本身完成加載。this
源碼解析:編碼
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded // 首先,檢查本類加載器是否已經加載該類 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { // 委託父類加載 c = parent.loadClass(name, false); } else { // 啓動類加載器進行加載 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); // 經過上面委託父類都沒有加載到,就本加載器在範圍內搜索類並加載 c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
public class MyClassLoader extends ClassLoader { private String path; public MyClassLoader(String path) { this.path = path; } /** * 讀取字節流 * @param name * @return * @throws IOException */ private byte[] getClass(String name) throws IOException { String filePath = name.replaceAll("\\.", "/"); FileInputStream fileInputStream = new FileInputStream(path + "/" + filePath + ".class"); int len = fileInputStream.available(); byte[] data = new byte[len]; fileInputStream.read(data); fileInputStream.close(); return data; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] aClass = new byte[0]; try { aClass = getClass(name); } catch (IOException e) { e.printStackTrace(); } return defineClass(name, aClass, 0, aClass.length); } }
public class MyClassLoader extends ClassLoader { private String path; public MyClassLoader(String path) { this.path = path; } /** * 讀取字節流 * @param name * @return * @throws IOException */ private byte[] getClass(String name) throws IOException { String filePath = name.replaceAll("\\.", "/"); FileInputStream fileInputStream = new FileInputStream(path + "/" + filePath + ".class"); int len = fileInputStream.available(); byte[] data = new byte[len]; fileInputStream.read(data); fileInputStream.close(); return data; } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); if (!name.startsWith("classload")) { c = this.getParent().loadClass(name); } else { c = findClass(name); } // this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] aClass = new byte[0]; try { aClass = getClass(name); } catch (IOException e) { e.printStackTrace(); } return defineClass(name, aClass, 0, aClass.length); } }
JDK9以後類的加載機制發生了一些變化。spa
JDK9以後類加載委派關係的工做流程:
當平臺及應用程序類加載器收到類加載請求後,在委派給父類以前,要先判斷該類是否可以歸屬於系統的一個模塊中,若是有這樣的歸屬關係,則優先委派給相關模塊的加載器進行加載。code
其委派關係以下圖: