ClassLoader的雙親託付模式:classloader 按級別分爲三個級別:最上級 : bootstrap classLoader(根類載入器) ; 中間級:extension classLoader (擴展類載入器) 最低級 app classLoader(應用類載入器)。java
根(Bootstrap)類載入器:該載入器沒有父載入器。它負責載入虛擬機的核心類庫,如java.lang.*等。好比java.lang.Object就是由根類載入器載入的。根類載入器從系統屬性sun.boot.class.path所指定的文件夾中載入類庫。bootstrap
根類載入器的實現依賴於底層操做系統。屬於虛擬機的實現的一部分,它並無繼承java.lang.ClassLoader類。api
擴展(Extension)類載入器:它的父載入器爲根類載入器。它從java.ext.dirs系統屬性所指定的文件夾中載入類庫,或者從JDK的安裝文件夾的jre/lib/ext子文件夾(擴展文件夾)下載入類庫,假設把用戶建立的JAR文件放在這個文件夾下,也會本身主動由擴展類載入器載入。擴展類載入器是純Java類,是java.lang.ClassLoader類的子類。安全
系統(System)類載入器:也稱爲應用類載入器。它的父載入器爲擴展類載入器。markdown
它從環境變量classpath或者系統屬性java.class.path所指定的文件夾中載入類,它是用戶本身定義的類載入器的默認父載入器。app
系統類載入器是純Java類。是java.lang.ClassLoader類的子類。ide
父子載入器並非繼承關係。也就是說子載入器不必定是繼承了父載入器。post
對於Java來講,java 虛擬機要將被用到的java類文件經過classLoader 載入到JVM內存中。而這個classloader就是bootstrap classloader。而對於APP而言,首先請求app 級來載入,繼而請求extension classLoader,最後啓動bootstrap classLoader 。this
spa
public class MyClassLoader extends ClassLoader {
//類載入器名稱
private String name;
//載入類的路徑
private String path = "D:/";
private final String fileType = ".class";
public MyClassLoader(String name){
//讓系統類載入器成爲該 類載入器的父載入器
super();
this.name = name;
}
public MyClassLoader(ClassLoader parent, String name){
//顯示指定該類載入器的父載入器
super(parent);
this.name = name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String toString() {
return this.name;
}
private byte[] loaderClassData(String name){
InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
this.name = this.name.replace(".", "/");
try {
is = new FileInputStream(new File(path + name + fileType));
int c = 0;
while(-1 != (c = is.read())){
baos.write(c);
}
data = baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
is.close();
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return data;
}
@Override
public Class<?> findClass(String name){ byte[] data = loaderClassData(name); return this.defineClass(name, data, 0, data.length); } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { //loader1的父載入器爲系統類載入器 MyClassLoader loader1 = new MyClassLoader("loader1"); loader1.setPath("D:/lib1/"); //loader2的父載入器爲loader1 MyClassLoader loader2 = new MyClassLoader(loader1, "loader2"); loader2.setPath("D:/lib2/"); //loader3的父載入器爲根類載入器 MyClassLoader loader3 = new MyClassLoader(null, "loader3"); loader3.setPath("D:/lib3/"); Class clazz = loader2.loadClass("Sample"); Object object = clazz.newInstance(); } } public class Sample { public Sample(){ System.out.println("Sample is loaded by " + this.getClass().getClassLoader()); new A(); } } public class A { public A(){ System.out.println("A is loaded by " + this.getClass().getClassLoader()); } }
每一個本身定義ClassLoader都必須繼承ClassLoader這個抽象類,而每一個ClassLoader都會有一個parent ClassLoader。咱們可以看一下ClassLoader這個抽象類中有一個getParent()方法,這種方法用來返回當前 ClassLoader的parent,注意,這個parent不是指的被繼承的類,而是在實例化該ClassLoader時指定的一個 ClassLoader,假設這個parent爲null,那麼就默認該ClassLoader的parent是bootstrap classloade。
上面解說了一下ClassLoader的做用以及一個最主要的載入流程,接下來咱們說說ClassLoader使用了雙親託付模式進行類載入。
通俗的講,就是某個特定的類載入器在接到載入類的請求時,首先將載入任務託付給父類載入器。依次遞歸。假設父類載入器可以完畢類載入任務,就成功返回;僅僅有父類載入器沒法完畢此載入任務時,才本身去載入。
爲了更好的理解雙親託付模式,咱們先本身定義一個ClassLoader,假設咱們使用這個本身定義的ClassLoader載入 java.lang.String,那麼這裏String是否會被這個ClassLoader載入呢?
其實java.lang.String這個類並不會被咱們本身定義的classloader載入。而是由bootstrap classloader進行載入,爲何會這樣?實際上這就是雙親託付模式的緣由,因爲在不論什麼一個本身定義ClassLoader載入一個類以前,它都會先 託付它的父親ClassLoader進行載入,僅僅有當父親ClassLoader沒法載入成功後,纔會由本身載入。
而在上面的樣例中,因爲 java.lang.String是屬於java核心API的一個類,因此當使用本身定義的classloader載入它的時候。該 ClassLoader會先託付它的父親ClassLoader進行載入(bootstrap classloader),因此並不會被咱們本身定義的ClassLoader載入。
咱們來看一下ClassLoader的一段源代碼:
protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException{
// 首先檢查該name指定的class是否有被載入
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
//假設parent不爲null,則調用parent的loadClass進行載入
c = parent.loadClass(name, false);
}else{
//parent爲null,則調用BootstrapClassLoader進行載入
c = findBootstrapClass0(name);
}
}catch(ClassNotFoundException e) {
//假設仍然沒法載入成功,則調用自身的findClass進行載入
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
那麼咱們使用雙親託付模式有什麼長處呢?