類是怎么被加載的
.java--->.class--->二進(jìn)制字節(jié)流--->類加載器--->轉(zhuǎn)換成 java.lang.Class類的一個(gè)實(shí)例
只要是二進(jìn)制字節(jié)流都可以不一定是.class,所以這就有了多種加載方式
從zip包中讀取:war、ear、jar
從網(wǎng)絡(luò)讀取applet
由其他文件生成jsp
運(yùn)行時(shí)計(jì)算生成,proxy具體的東西還沒(méi)看...
然后加載了之后才有后面一系列的連接(Linking)、初始化、使用、卸載這些操作
而且正因?yàn)椋杭虞d的時(shí)候只要是二進(jìn)制字節(jié)流都可以,所以為了安全起見需要做驗(yàn)證
<font color="red">注意一下,類加載不等于初始化</font>(舉個(gè)例子,首次new一個(gè)類之后就會(huì)對(duì)這個(gè)類進(jìn)行初始化clinit方法)
類加載階段需要做的事情
- 通過(guò)一個(gè)類的全限定名來(lái)獲取定義這個(gè)類的二進(jìn)制字節(jié)流
- 把這個(gè)二進(jìn)制字節(jié)流代表的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化成方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
- 在內(nèi)存中生成一個(gè)本類的Class類對(duì)象作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問(wèn)入口
- 加載和驗(yàn)證有可能是交替進(jìn)行的
而加載的過(guò)程就是由類加載器來(lái)完成的。
類加載器
類加載器(ClassLoader),就是把類加載到JVM里用的。當(dāng)JVM啟動(dòng)的時(shí)候,Java會(huì)按以下順序自頂而下地啟動(dòng)三類加載器并加載相應(yīng)的類,而且他們依次呈繼承關(guān)系:
啟動(dòng)(Bootstrap)類加載器
啟動(dòng)類加載器是用本地代碼實(shí)現(xiàn)的類加載器,用C++編寫,是JVM自帶的類裝載器,負(fù)責(zé)Java負(fù)責(zé)Java平臺(tái)核心庫(kù),用來(lái)裝載核心類(比如Object類,String類等)。由于引導(dǎo)類加載器涉及到虛擬機(jī)本地實(shí)現(xiàn)細(xì)節(jié),所以不允許直接通過(guò)引用進(jìn)行操作,不可被直接訪問(wèn)。
擴(kuò)展(Extension)類加載器
擴(kuò)展類加載器是由Sun的由Sun的ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實(shí)現(xiàn)的
負(fù)責(zé)將 <JAVA_HOME >/lib/ext或者由系統(tǒng)變量-Djava.ext.dir指定位置中的類庫(kù) 加載到內(nèi)存中。開發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類加載器。
系統(tǒng)(System)類加載器
由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實(shí)現(xiàn)的
將用戶寫的類加載到內(nèi)存中。開發(fā)者可以直接使用系統(tǒng)類加載器。也叫應(yīng)用加載器。
用代碼驗(yàn)證繼承關(guān)系:
//獲取系統(tǒng)類加載器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
//獲取系統(tǒng)類加載器的父類----擴(kuò)展類加載器
System.out.println(classLoader.getParent());
//獲取擴(kuò)展類加載器的父類----引導(dǎo)類加載器
System.out.println(classLoader.getParent().getParent());
輸出結(jié)果:

最后一個(gè)輸出是
null,因?yàn)橐龑?dǎo)類加載器是無(wú)法被直接訪問(wèn)的。
關(guān)于數(shù)組類
非數(shù)組類都是類加載器來(lái)加載的,你可以自己寫一個(gè)類加載器來(lái),也可用應(yīng)用加載器來(lái)
但是數(shù)組類的話是由JVM直接創(chuàng)建的,深入理解JVM書上有個(gè)例子,這個(gè)類里面就包含了邊界檢查等等的方法......
類是怎么比較的
這里就說(shuō)一句:不同的類加載器加載的類是沒(méi)法比較的,必定返回false
我們來(lái)看下面這段代碼:
package classLoading;
import java.io.IOException;
import java.io.InputStream;
public class ClassLoaderTest {
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
// TODO Auto-generated method stub
ClassLoader myLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf('.') + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(fileName);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
}
};
// 用myLoader加載自己然后獲得實(shí)例?
Object obj = myLoader.loadClass("classLoading.ClassLoaderTest").newInstance();
System.out.println(obj.getClass());
System.out.println(obj instanceof classLoading.ClassLoaderTest);
}
}
這個(gè)程序干的事情其實(shí)就是:自己創(chuàng)建了一個(gè)加載器然后加載了自己,然后和通過(guò)系統(tǒng)加載器加載的自己比
由于一個(gè)在user classloader里,一個(gè)在app classloader里,所以是返回false的
這就涉及到了一個(gè)問(wèn)題:如何避免相同的類因?yàn)榧虞d器的原因在內(nèi)存中被重復(fù)加載???
雙親委派模型
一句話概括:子類拿到了一個(gè)新的要被加載的字節(jié)流就交給父類,父類如果可以加載就加載了,不行的話返回null然后子類來(lái)加載,Ps:Bootstrap Classloader由于是頂層,不用遵循這個(gè)規(guī)則
這樣的好處就是,比如你亂寫了一個(gè)Java.lang.Object的話,他是不會(huì)被加載的,因?yàn)锽ootStrap ClassLoader已經(jīng)加載了它了
ClassLoader類的方法
| 方法 | 說(shuō)明 |
|---|---|
getParent() |
返回該類加載器的父類加載器。 |
loadClass(String name) |
加載名稱為 name的類,返回的結(jié)果是 java.lang.Class類的實(shí)例。 |
findClass(String name) |
查找名稱為 name的類,返回的結(jié)果是 java.lang.Class類的實(shí)例。 |
findLoadedClass(String name) |
查找名稱為 name的已經(jīng)被加載過(guò)的類,返回的結(jié)果是 java.lang.Class類的實(shí)例。 |
defineClass(String name, byte[] b, int off, int len) |
把字節(jié)數(shù)組 b中的內(nèi)容轉(zhuǎn)換成 Java 類,返回的結(jié)果是 java.lang.Class類的實(shí)例。這個(gè)方法被聲明為 final的。 |
resolveClass(Class<?> c) |
鏈接指定的 Java 類。 |
每個(gè) Java 類的Class類都維護(hù)著一個(gè)指向定義它的類加載器的引用,通過(guò) getClassLoader()方法就可以獲取到此引用。
Class.forName()方法
除了defineClass以外在Class類中還有一個(gè)靜態(tài)方法來(lái)加載類:Class.forName("xxx.xxx.xxx")
這就是使用當(dāng)前類的類加載器來(lái)加載這個(gè)類
比如A用的是用戶自定義W類加載器,然后在A里通過(guò)Class.forName加載B,那么B的類加載器就是W類加載器
這里看個(gè)例子:
System.out.println(Person.class.getClassLoader());
System.out.println(Class.forName("java.lang.Object").getClassLoader());
輸出結(jié)果:

文章參考來(lái)源:https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html