第七章 虛擬機類加載機制
html
一、類加載的時機java
虛擬機的類加載機制:程序員
類初始化的五種狀況:數組
被動引用:安全
父類:數據結構
package com.ecut.classload; public class SuperClass{ static { System.out.println("super init!"); } static int value = 1; }
子類:多線程
package com.ecut.classload; public class SubClass extends SuperClass{ static { System.out.println("sub init!"); } }
package com.ecut.classload; /** * 經過子類引用父類的靜態字段,不會致使初始化 */ public class NotInitialization1 { public static void main(String[] args) { System.out.println(SubClass.value); } }
運行結果以下:jvm
........
[Loaded com.ecut.classload.SuperClass from file:/D:/code/java/jvm-test/out/production/jvm-test/] [Loaded com.ecut.classload.SubClass from file:/D:/code/java/jvm-test/out/production/jvm-test/] super init! 1
.......
package com.ecut.classload; /** * 經過數組定義來引用類,不會觸發此類的初始化 * -XX:+TraceClassLoading */ public class NotInitializatio2 { public static void main(String[] args) { SuperClass[] superClasses = new SuperClass[10]; } }
運行結果以下:ide
....... [Loaded java.net.Proxy$Type from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded com.ecut.classload.SuperClass from file:/D:/code/java/jvm-test/out/production/jvm-test/] [Loaded java.util.ArrayList$Itr from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded sun.net.NetHooks from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded java.net.Inet6Address$Inet6AddressHolder from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded java.lang.Shutdown from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] [Loaded java.lang.Shutdown$Lock from C:\Program Files\Java\jdk1.8.0_121\jre\lib\rt.jar] ........
常量類:測試
package com.ecut.classload; public class ConstClass { public static final String HELLOWORD = "hello world"; static { System.out.println("const init !"); } }
測試類:
package com.ecut.classload; /** * 常量在編譯階段會存入調用類的常量池中,本質上並無直接引用到定義的常量的類,所以不會觸發定義常量的類初始化 */ public class NotInitializatio3 { public static void main(String[] args) { System.out.println(ConstClass.HELLOWORD); } }
雖然在源碼中引用了ConstClass類中的常量但其實在編譯階段已將此常量值存儲在了main方法所在的常量池中,對常量的引用裝換成了對自身常量池的引用。
二、類加載的過程
加載:
驗證:
準備:
解析:
初始化:
三、類加載器
類與類加載器:
package com.ecut.classload; import java.io.IOException; import java.io.InputStream; public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { ClassLoader myClassLoder = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream is = getClass().getResourceAsStream(fileName); if (is == null) { return super.loadClass(name); } byte[] b = new byte[is.available()]; is.read(b); return this.defineClass(name, b, 0, b.length); } catch (IOException e) { throw new ClassNotFoundException(name); } } }; Object object = myClassLoder.loadClass("com.ecut.classload.ClassLoaderTest").newInstance(); System.out.println(object instanceof com.ecut.classload.ClassLoaderTest); } }
運行結果以下:
false
類加載器的分類:
雙親委派模型:
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); } protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先,檢查類是否已經加載。 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false);//看父類加載器有沒有加載該類(父委託機制) } else { c = findBootstrapClassOrNull(name);//父類加載器爲空,看根加載器(Bootstrap Loader)有沒有加載 } } catch (ClassNotFoundException e) { //若是類沒有發現拋出ClassNotFoundException } if (c == null) { //若是仍然沒有找到,而後調用findClass爲了找到類。 long t1 = System.nanoTime(); c = findClass(name); // 這是定義類裝入器;記錄統計數據 sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } } protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }
先檢查是否被加載過,若沒有被加載過調用父類的loadclass方法,若父加載器爲空則默認使用啓動類加載器爲父加載器,若是父加載器加載失敗就拋出ClassNotFoundException ,再調用本身的findClass方法進行加載。
破壞雙親委派機制模型:
轉載請於明顯處標明出處