淺談Java的類加載機(jī)制

類是怎么被加載的

.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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容