public class TT { public static void main(String[] args) throws Exception { String[] ss = new String[3]; //String是boot加載器加載的。 System.out.println(ss.getClass().getClassLoader());//null, int[] tr = new int[3]; //若是元素是原生類型是沒有類加載器的。 System.out.println(tr.getClass().getClassLoader());//null。這裏的null是由於原生類型,上面的null是由於boot加載器。 TT[] t = new TT[3]; //數組對象的getClassLoader()返回值跟數組元素的類加載器是同樣的 System.out.println(t.getClass().getClassLoader());//AppClassLoader@73d16e93 Integer[] gg = new Integer[3]; //若是元素是原生類型是沒有類加載器的。 System.out.println(gg.getClass().getClassLoader());//null } }
ClassLoader抽象類,用於加載類,不是加載對象。給定類的二進制名字,java
"java.lang.String"mysql
"javax.swing.JSpinner$DefaultEditor" 內部類名字spring
"java.security.KeyStore$Builder$FileBuilder$1" 內部類FileBuilder中的第一個匿名內部類名字sql
"java.net.URLClassLoader$3$1" 第三個匿名內部類中的第一個匿名內部類(內部類沒有名字,就用數字表示)編程
也能夠從網絡獲取。bootstrap
Class類裏面有private final ClassLoader classLoader;數組
數組類的Class對象不是類加載器建立的,是由虛擬機運行時自動建立的。只有數組的特殊的。安全
數組對象的getClassLoader()返回值跟數組元素的類加載器是同樣的; 若是元素是原生類型是沒有類加載器的。網絡
默認是雙親委託方式,這是爲了安全起見,若是要改變這種方式,就要本身實現類加載器。併發
每一個ClassLoader都有一個private final ClassLoader parent;就是他的父類加載器,因此加載器的父類加載器是包含關係。
不一樣的類加載器都是加載不一樣環境變量指定目錄下的類。
public class T { public static void main(String[] args) { System.out.println(System.getProperty("sun.boot.class.path"));//bootStrap啓動.根 類加載器加載的路徑: //C:\Program Files\Java\jdk1.8.0_181\jre\lib\endorsed\rt_debug.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar; //C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar; //C:\Program Files\Java\jre1.8.0_181\lib\endorsed\rt_debug.jar System.out.println(System.getProperty("java.ext.dirs"));//擴展類加載器加載的路徑: //C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext; //C:\Windows\Sun\Java\lib\ext System.out.println(System.getProperty("java.class.path"));//app.應用.系統 類加載器加載的路徑: //H:\2019326spring\螞蟻課堂\0005-(每特教育&每特學院&螞蟻課堂)-3期-併發編程專題-線程池原理分析\0005- //(每特教育&每特學院&螞蟻課堂)-3期-併發編程專題-線程池原理分析\上課代碼\thread_day_day06_test\bin } //手動把class文件放在bootstarp類加載器的目錄下,那麼這個class文件就由根加載器加載。 }
Loader1和Loader2是2個不一樣的類加載器對象,雖然是同一個類加載器類。Class1和class2是同一個包名和類名的類的Class對象。Loader1和Loader2構成了2個命名空間。
同一個命名空間加載同一個包和類名的類,是失敗的,由於他說已經加載了這個類。
2個不一樣的類加載器loader1和loader2能夠加載同一個包名和類名的類,所以class1和class2雖然包名和類名同樣,可是再虛擬機中是能夠共存的。
同一個包名和類名的類對象,因爲是不一樣的類加載加載,因此在不一樣的命名空間,是不可見的,不能使用,更不能賦值和轉換。Object1不能轉換成object2。
類加載器找的是class對象。
子加載器能夠看到父加載器的,父加載器看不到子加載器的。
一個java類是由類的徹底限定名和加載這個類的定義類加載器共同決定的。
數組不是類加載器加載的,是jvm運行時候建立的。
擴展類加載器和應用類加載器是由啓動類加載器加載的,啓動類加載器是由jvm加載的。
Ideal自帶的反編譯器反編譯的。
Jdbc是一個標準,oracle廠商會根據這個標準去實現,jdbc是一個標準,原生的在jdk中,Connection和Statement接口在rt.jar裏面,由bootStrap加載器加載。可是Connection和Statement的實現是由廠商實現的,那麼就是將廠商提供的jar包放在應用的類路徑下,因此廠商的實現就不能由bootstrap加載,由於bootstarp不會去掃描應用的類路徑,只能由系統加載器appclassloader加載。
Connection接口是啓動類加載器加載的,Connection的實現啓動類加載器加載不了,只能由系統加載器加載。Connection接口看不到他的實現,這是雙親委託出現的問題。
在jdbc,jndi,xml解析都會出現,就是說在SPI場合都有這個問題。
SPI:Service Provider Inteferce。jdbc,jndi都是SPI。Jdk服務提供者僅僅提供一些標準和接口,具體的實現由廠商來實現的。父加載器的類看不到子加載器的,子加載器的類能夠看到父加載器的。
父加載器可使用當前線程Thread.currentThread().getContextClassLoader()所指定的類加載器加載的類。這就改變了父加載器不能使用子加載器的類的狀況。就改變了雙親委託模型。
線程上下文類加載器就是當前線程的類加載器。
SPI:經過給當前線程設置上下文類加載器,就能夠由線程的上下文類加載器來加載接口的實現類。
框架開發和組件的開發有用到線程上下文類加載器。
package com.ssss; public class T { /*當前類加載器: 每一個類都會使用加載自身的類加載器,去加載所引用的類。 線程上下文類加載器從jdk1.2引入。 Thread.currentThread().setContextClassLoader() Thread.currentThread().getContextClassLoader()分別用於設置和獲取上下文類加載器。 默認下,線程繼承父線程的上下文類加載器,啓動應用線程的上下文加載器是系統加載器。因此默認是AppClassLoder。 在線程中運行的代碼能夠經過這個類加載器加載類和資源。 jdbc提供的是接口,米有提供實現。實現是廠商提供的,mysql和oracle不一樣的廠商來提供。 JDBC,JNDI,JAXP:這些spi都是利用的是線程上下文類加載器。 TOMCAT爲每個應用一個類加載器,使用到了類加載器的隔離,而且違反了雙親委派原則。 */ public static void main(String[] args) { System.out.println(Thread.currentThread().getContextClassLoader());//AppClassLoader,線程上下文加載器, System.out.println(Thread.class.getClassLoader());//null,Thread是根加載器加載的, } }
package com.ssss; public class rrr implements Runnable { private Thread t; public rrr() { t = new Thread(this); t.start(); } public void run() { ClassLoader cl = this.t.getContextClassLoader();//這個線程的上下文類加載器是App。 System.out.println(cl.getClass());//AppClassLoader System.out.println(cl.getParent().getClass());//ExtClassLoader } public static void main(String[] args) { new rrr(); } }
package com.ssss; /* 線程上下文使用模式:獲取-------使用-------還原 類A的依賴類也是加載A的加載器加載。 線程上下文就是爲了破壞java的委託機制。默認線程上下文加載器是系統app加載器,spi接口的代碼中會使用線程上下文加載器 加載spi的實現類。 高層提供統一的接口讓底層去實現,高層要加載底層的實現類時候,就呀經過線程上下文加載器來幫助高層加載實現類。 本質是由於高層的加載器和底層加載器不同,而高層加載器不能加載底層的類。 運行期間放置在了線程中,不管當前程序是在啓動類加載器範圍仍是在擴展加載器範圍內,均可以經過Thread.currentThread().getContextClassLoader() 獲取應用類加載器。ThreadLocal是拿空間換時間,每一個線程都有一個拷貝。 ClassLoader.getSystemClassLoader()也能夠獲取app加載器。 */ public class Cat { public static void main(String[] args) { ClassLoader cl = Thread.currentThread().getContextClassLoader();//獲取 try { Thread.currentThread().setContextClassLoader(target); incokeMeath();//使用線程上下文的類加載器 }finally { Thread.currentThread().setContextClassLoader(cl);//還原 } System.out.println(ClassLoader.getSystemClassLoader());//AppClassLoader } }