類加載器的作用
- 通過一個(gè)類的全限定名稱來獲取此類的二進(jìn)制字節(jié)流,并加載到內(nèi)存中(需要使用類加載器)
- 將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
- 在堆中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口
類緩存
標(biāo)準(zhǔn)的JavaSE類加載器可以按要求查找類,但一旦某個(gè)類被加載到類加載器中,它將維持加載(緩存)一段時(shí)間。不過,JVM垃圾收集器可以回收這些Class對(duì)象
類加載器的層級(jí)結(jié)構(gòu)(樹狀結(jié)構(gòu))
1.引導(dǎo)類加載器(Bootstrap ClassLoader)
它用來加載Java的核心庫(kù)(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.Path路徑下的內(nèi)容),是用原生代碼來實(shí)現(xiàn)的,并不繼承自java.lang.classloader。
加載擴(kuò)展類和應(yīng)用程序類加載器,并指定他們的父類加載器。
啟動(dòng)類加載器無法被Java程序直接引用
2.擴(kuò)展類加載器(Extension ClassLoader)
- 用來加載Java的擴(kuò)展庫(kù)(JAVA_HOME/jre/ext/*.jar或java.ext.dirs路徑下的內(nèi)容)。 Java虛擬機(jī)的實(shí)現(xiàn)會(huì)提供一個(gè)擴(kuò)展庫(kù)目錄。該類加載器在此目錄里面查找并加載Java類。
- 由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn)。
**3.應(yīng)用程序類加載器(Application ClassLoader) **
- 它根據(jù)Java應(yīng)用的類路徑(classpath,java.class.path類。 一般來說,Java應(yīng)用的類都是由它來完成加載的。
- 由sun.misc.Launcher$AppClassLoader實(shí)現(xiàn)。
4.自定義類加載器
- 開發(fā)人員可以用過繼承java.lang.ClassLoader類的方式實(shí)現(xiàn)自己的類加載器,以滿足一些特殊的要求
雙親委派模式
雙親委派模型
從JDK1.2開始,java虛擬機(jī)規(guī)范推薦開發(fā)者使用雙親委派模式(ParentsDelegation Model)進(jìn)行類加載,其加載過程如下:
- 如果一個(gè)類加載器收到了類加載請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把類加載請(qǐng)求委派給父類加載器去完成。
- 每一層的類加載器都把類加載請(qǐng)求委派給父類加載器,直到所有的類加載請(qǐng)求都應(yīng)該傳遞給頂層的啟動(dòng)類加載器。
- 如果頂層的啟動(dòng)類加載器無法完成加載請(qǐng)求,子類加載器嘗試去加載,如果連最初發(fā)起類加載請(qǐng)求的類加載器也無法完成加載請(qǐng)求時(shí),將會(huì)拋出ClassNotFoundException,而不再調(diào)用其子類加載器去進(jìn)行類加載。
雙親委派 模式的類加載機(jī)制的優(yōu)點(diǎn)是java類它的類加載器一起具備了一種帶優(yōu)先級(jí)的層次關(guān)系,越是基礎(chǔ)的類,越是被上層的類加載器進(jìn)行加載,保證了java程序的穩(wěn)定運(yùn)行。
注意:
- 并不是所有的類記載其都采用雙親委托機(jī)制
- tomcat服務(wù)器類加載器也是用代理模式,所不同的是它首先嘗試去加載某個(gè)類,如果找不到再找代理給父類加載器。這與一般類加載器的順序是相反的。
我們可以簡(jiǎn)單地自定義一個(gè)類加載器,用于加載某個(gè)class
public class FileSystemClassLoader extends ClassLoader {
//文件的根目錄
private String rootDir;
public FileSystemClassLoader(String rootDir){
this.rootDir=rootDir;
}
//重寫findClass方法
@Override
protected Class<?> findClass(String s) throws ClassNotFoundException {
Class c=findLoadedClass(s);
if (c!=null){
return c;
}else {
ClassLoader parent=this.getParent();
//parent獲取不到class時(shí)會(huì)拋出異常,為了繼續(xù)執(zhí)行使用try catch包裹
try{
c=parent.loadClass(s);
}catch (Exception e){
}
if (c!=null){
return c;
}else {
byte[] classData=getClassData(s);
if (classData==null){
throw new ClassNotFoundException();
}else {
//將字節(jié)數(shù)組轉(zhuǎn)為Class
c=defineClass(s,classData,0,classData.length);
}
}
}
return c;
}
//將文件轉(zhuǎn)為字節(jié)數(shù)組
private byte[] getClassData(String className) {
//改為文件地址
String path=rootDir+"/"+className.replace(".","/")+".class";
System.out.println(path);
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
InputStream inputStream=null;
try {
inputStream=new FileInputStream(path);
byte[] buffer=new byte[1024];
int temp=0;
while ((temp=inputStream.read(buffer))!=-1){
byteArrayOutputStream.write(buffer,0,temp);
}
return byteArrayOutputStream.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
if (inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (byteArrayOutputStream!=null){
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
使用
public class UseCustomClassLoader {
public static void main(String[]args){
FileSystemClassLoader loader=new FileSystemClassLoader("/home/xjk");
FileSystemClassLoader loader2=new FileSystemClassLoader("/home/xjk");
try {
Class clazz1=loader.findClass("com.jk.bean.Emp");//本項(xiàng)目自定義的類調(diào)用AppClassLoader
System.out.println(clazz1.getClassLoader());
Class clazz2=loader.findClass("java.lang.String");//rt.jar里的類調(diào)用BootstrapClassLoader
System.out.println(clazz2.getClassLoader());
Class clazz3=loader.findClass("com.company.Main");//項(xiàng)目外的類調(diào)用自定義的FileSystemClassLoader
System.out.println(clazz3.getClassLoader());
Class clazz4=loader2.findClass("com.company.Main");//使用不同類加載器,Class對(duì)象不一致
System.out.println(clazz4.getClassLoader());
System.out.println(clazz3==clazz4);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
輸出結(jié)果
sun.misc.Launcher$AppClassLoader@18b4aac2
null
com.jk.jvm.FileSystemClassLoader@1d44bcfa
com.jk.jvm.FileSystemClassLoader@6f94fa3e
false
因?yàn)锽ootstrapClassLoader無法被Java程序直接引用,所以顯示為空