類加載可以看這個部落格
http://www.importnew.com/25295.html
雙親委派模型
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX3tGRPlXR65UMZRUT4VkMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2LcRHelR3LcJzLctmch1mclRXY39zM2gTM0UDNwEzMwgDM4EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
啟動類加載器(Bootstrap ClassLoader):負責加載 JAVA_HOME\lib 目錄中的,或通過-Xbootclasspath參數指定路徑中的,且被虛拟機認可(按檔案名識别,如rt.jar)的類。
擴充類加載器(Extension ClassLoader):負責加載 JAVA_HOME\lib\ext 目錄中的,或通過java.ext.dirs系統變量指定路徑中的類庫。
應用程式類加載器(Application ClassLoader):負責加載使用者路徑(classpath)上的類庫。
抄的上面那個部落格。。
當一個類加載器收到類加載任務,會先交給其父類加載器去完成,是以最終加載任務都會傳遞到頂層的啟動類加載器,隻有當父類加載器無法完成加載任務時,才會嘗試執行加載任務。
來看類加載的代碼
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
//檢視類是否已經被加載
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//父加載器不為空,調用父
c = parent.loadClass(name, false);
} else {
//為空,調用BootStrap加載器
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
//父類沒有加載成功,自己本身進行加載
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();
}
}
if (resolve) {
//此處為類加載的解析階段
resolveClass(c);
}
return c;
}
}
大緻過程
1.檢視這個類是否已經被加載
2.若未加載,則檢視父類加載器是否為空,為空就直接調用啟動類加載器,不為空則調用父類加載器
3.若第二步沒有成功,則自己進行加載, 也就是調用自己的findclass方法
實作自己的類加載器
1.首先寫一個類
package cn.dengbin97.myclass;
public class Solution {
public static void main(String[] args) {
}
}
2.對類進行編譯,放到一個目錄
//此處由于指定了包,是以不能直接在目前目錄編譯
//package cn.dengbin97.myclass;
//像上面這樣的就要傳回到cn目錄進行編譯
C:\temp\cn>javac C:\temp\cn\dengbin97\myclass\Solution.java
3.定義自己的類加載器,重寫findclass方法
若要去破壞雙親委派原則,可以重寫loadclass方法,最好還是不要
package main;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
//擷取檔案位元組流
byte[] bytes = getClassBytes(new File("C:\\temp\\cn\\dengbin97\\myclass\\Solution.class"));
//把位元組流轉化為Class對象
Class<?> c = this.defineClass(name, bytes, , bytes.length);
return c;
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
/**
* 擷取檔案的位元組流
* */
private byte[] getClassBytes(File file) {
byte[] tempbytes = new byte[];
byte[] res = null;
InputStream in = null;
try {
in = new FileInputStream(file);
//此處為了友善,直接一次讀取了,可以進行改進
int read = in.read(tempbytes);
res = new byte[read];
//把讀取結果拷貝的新的數組,保證新數組不留白,因為後面要用到位元組流的大小,可以從數組擷取
System.arraycopy(tempbytes, , res, , read);
} catch (Exception e1) {
e1.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e1) {
}
}
}
return res;
}
}
4.進行測試
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
MyClassLoader loader = new MyClassLoader();
//使用Class.forName進行類加載
//指定類和使用的加載器
Class<?> clazz = Class.forName("cn.dengbin97.myclass.Solution", true, loader);
Object newInstance = clazz.newInstance();
//擷取類執行個體
System.out.println(newInstance);
//擷取類
System.out.println(newInstance.getClass());
//擷取使用的加載器
System.out.println(newInstance.getClass().getClassLoader());
}
//運作結果
cn.dengbin97.myclass.Solution@70dea4e
class cn.dengbin97.myclass.Solution
//此處可以看到,類加載器使用的是我們自己的
main.MyClassLoader@7852e922
loadClass:檢查類是否已經加載,調用父類或自己本身進行類的加載
findClass:真正實作類的加載,擷取位元組碼檔案的位元組流,調用defineClass得到class對象
defineClass:将讀取的二進制位元組碼檔案轉化為Class對象