你想寫類加載器?或者你遇到了ClassCastException異常,或者你遇到了奇怪的LinkageError狀態約束異常。應該仔細看看java類的加載處理了。java
一個Java類是由java.lang.ClassLoader類的一個實例加載的。因爲java.lang.ClassLoader本身自己是一個抽象類因此一個類加載器只可以是java.lang.ClassLoader類的具體子類的實例。若是是這種狀況,那麼哪個類加載器來加載java.lang.ClassLoader這個類?(經典的"誰將會加載加載者"引導的問題)。事實證實JVM有一個內置的引導類加載器。引導加載器加載java.lang.ClassLoader和許多其餘java平臺類。sql
要加載一個具體的java類,例如com.acme.Foo,JVM調用java.lang.ClassLoader類的loadClass方法(事實上,JVM查找loadClassInternal方法-若是發現loadClassInternal方法則用loadClassInternal方法,不然JVM使用loadClass方法,而loadClassInternal方法會調用loadClass方法)。loadClass方法接收類名來加載類返回表示加載的類的java.lang.Class實例。事實上loadClass方法找到.class文件(或者URL)的實際字節,並調用defineClass方法來構造出java.lang.Class類的字節數組。加載器上調用loadClass方法的加載器稱之爲初始化加載器(即,JVM啓動加載使用這個加載器).可是,啓動加載器不是直接加載類的-而是可能委託給另一個類加載器(例如,它的父加載器)-它本身也可能委派給另一個加載器去加載等等。最終在委託鏈中的某些類加載器對象調用defineClass方法加載有關的類(com.acme.Foo)。
這個特殊的類加載器叫作com.acme.Foo的確切加載器。在運行時,一個java類是由類的徹底限定類名和和類加載器肯定其惟一性的。若是指定相同的類名(即,相同的徹底限定類名)的類是由兩個不一樣的類加載器加載的,那麼這些類是不一樣的-即便這些.class的字節碼是相同的而且都是從相同的位置進行加載的(相同的URL)。數組
即使是一個簡單的"hell world"java程序,也有至少3種類加載器。緩存
引導類加載器oracle
讓咱們假設你正在運行一個"hello world" java程序。咱們來看一下類的加載流程。JVM用應用類加載器加載主方法(main)所在的類。若是你運行下面的程序spa
class Main { public static void main(String[] args) { System.out.println(Main.class.getClassLoader()); javax.swing.JFrame f = new javax.swing.JFrame(); f.setVisible(true); SomeAppClass s = new SomeAppClass(); }
它會打印以下內容
sun.misc.Launcher$AppClassLoader@17943a4code
每當一些其它的類引用在Main類中被解析時,JVM用Main所在類的明確的加載器-應用類加載器-作爲初始化加載器。在上面的列子中,爲了加載javax.swing.JFrame類JVM將使用應用類加載器作爲一個初始化加載器。即,JVM將用應用類應用作爲初始化加載器。即。JVM將調用loadClass()方法(loadClassInternal方法)在應用類加載器中。應用類加載器委託給擴展類加載器。
擴展加載器檢查這是不是一個啓動類(用私有方法 - ClassLoader.findBootstrapClass),啓動類加載器是否從rt.jar加載過它。
當SomeAppClass的引用類被解析時,JVM有着相同的過程-用應用類加載器作爲初始化加載器。
應用加載器委託給擴展加載器,擴展加載器檢查啓動加載器,啓動加載器找不到"SomeAppClass"類,
因而擴展加載器檢查"SomeAppClass"類是否在擴展jars裏,結果發現不在。
因而應用類加載器檢查在應用的CLASSPATH下的.class字節,若是找到了則進行加載,若是沒有找到,將會拋出NoClassDefFoundError異常。對象
Class是由具體的類加載器與類的徹底限定類名惟必定義的。blog
若是具體的類加載器不一樣,即便.class字符是從文件系統中的相同位置進行加載的Classes也是不一樣的。get
類加載器委託給父加載器進行加載。
加載Bar類中引用的Foo類,JVM使用Bar類的確切的類加載器作爲初始化加載器。JVM會在Bar類的確切加載器上會調用loadClass()方法加載Foo類。
JVM緩存->運行時的類每次初始化加載都將被記錄。JVM將會緩存用於之後的解析。即,loadClass()方法不會對於每一次引用都調用。這能確保時間的不變性-即,一個類加載器不容許加載相同類名但字節碼不一樣的類。
他是由緩存來實現的。好的類加載器應該經過調用ClassLoader得call()方法來檢查緩存。
Understanding Java class loading https://blogs.oracle.com/sundararajan/entry/understanding_java_class_loading