作用:加載.class文件到jvm中
觸發(fā)節(jié)點:
- 執(zhí)行new操作
- Class.forName
- classloader.loadclass
Java內置ClassLoader
BootstrapClassloader
最頂層的類加載器姥份,主要用來加載Java核心類,如rt.jar喳资、resources.jar钥平、charsets.jar等嫉晶。它不是 java.lang.ClassLoader的子類天吓,而是由JVM自身實現的該類c 語言實現,Java程序訪問不到該加載器祭陷。
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i].toExternalForm());
}
ExtClassloader
擴展類加載器,主要負責加載Java的擴展類庫趣席,默認加載JAVA_HOME/jre/lib/ext/目下的所有jar包或者由java.ext.dirs系統(tǒng)屬性指定的jar包兵志。放入這個目錄下的jar包對所有AppClassloader都是可見的(后面會知道ExtClassloader是AppClassloader的父加載器)。那么ext都是在那些地方加載類內:
System.getProperty("java.ext.dirs")
AppClassloader
它負責在JVM啟動時宣肚,加載來自 CLASSPATH下的Jar包想罕。調用ClassLoader.getSystemClassLoader()可以獲取該類加載器。如果沒有特別指定霉涨,則用戶自定義的任何類加載器都將該類加載器作為它的父加載器,這點通過ClassLoader的無參構造函數可以知道如下:
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
執(zhí)行以下代碼即可獲得classpath加載路徑:
System.out.println(System.getProperty("java.class.path"));
內置Classloader關系
一般我們都認為ExtClassloader的父類加載器是BootStarpClassloader按价,但是其實他們之間根本是沒有父子關系的,只是在ExtClassloader找不到要加載類時候會去委托BootStrap加載器去加載笙瑟。
通過如下代碼可以知道父加載器為null
ClassLoader.getSystemClassLoader().getParent().getParent()
Classloader原理
Java類加載器使用的是委托機制楼镐,也就是子類加載器在加載一個類時候會讓父類來加載,保證了安全往枷。例如框产,String已經在啟動時就被引導類加載器(Bootstrcp ClassLoader)加載凄杯,所以用戶自定義的ClassLoader永遠也無法加載一個自己寫的String。
class ClassLoader {
// name是要加載的類全名
protected Class<?> loadClass(String name, boolean resolve) {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) { // 如果該類已經被加載就直接return
if (parent != null) {
c = parent.loadClass(name, false); // 尋找父classLoader加載
} else {
c = findBootstrapClassOrNull(name); // 沒有父classLoader就委派給bootstrap類加載加載
}
}
if (c == null) {
// 如果所有父類加載器都無法加載秉宿,再通過當前加載器定義的findClass方法進行加載戒突。
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();
}
return c;
}
}
}
從上面源碼知道要想修改類加載委托機制,實現自己的載入策略 可以通過覆蓋ClassLoader的findClass方法或者覆蓋loadClass方法來實現描睦。
JVM初始化類加載器
class Launcher {
public Launcher() { // 構造方法
localExtClassLoader = ExtClassLoader.getExtClassLoader(); // 創(chuàng)建ExtClassLoader
//然后以ExtClassloader作為父加載器創(chuàng)建了AppClassLoader
this.loader = AppClassLoader.getAppClassLoader(localExtClassLoader);
// 默認的線程上下文類加載器是AppClassLoader
Thread.currentThread().setContextClassLoader(this.loader);
}
private static File[] getExtDirs() {
// ExtClassloader要加載的jar包路徑
String str = System.getProperty("java.ext.dirs");
File[] arrayOfFile;
if (str != null)
{
StringTokenizer localStringTokenizer = new StringTokenizer(str, File.pathSeparator);
int i = localStringTokenizer.countTokens();
arrayOfFile = new File[i];
for (int j = 0; j < i; j++) {
arrayOfFile[j] = new File(localStringTokenizer.nextToken());
}
}
else
{
arrayOfFile = new File[0];
}
return arrayOfFile;
}
public static ClassLoader getAppClassLoader(final ClassLoader paramClassLoader)
throws IOException
{ //可知AppClassLoader類加載路徑為java.class.path
String str = System.getProperty("java.class.path");
final File[] arrayOfFile = str == null ? new File[0] : Launcher.getClassPath(str);
(ClassLoader)AccessController.doPrivileged(new PrivilegedAction()
{
public Launcher.AppClassLoader run()
{
URL[] arrayOfURL = this.val$s == null ? new URL[0] : Launcher.pathToURLs(arrayOfFile);
return new Launcher.AppClassLoader(arrayOfURL, paramClassLoader);
}
});
}
}
ContextClassLoader
- 當父類加載器需要加載子類加載器中的資源時候可以通過設置和獲取線程上下文類加載器來實現膊存。
- 一個類加載器要使用不在當前類加載器類查找路徑路徑中的情況,這種情況下可以新建一個在指定路徑查找類的類加載器