Java程序員的必備知識-類加載機制詳解

類加載器的概念

類加載器是一個用來加載類文件的類。

Java源代碼經過javac編譯器編譯成類文件。而後JVM來執行類文件中的字節碼來執行程序。類加載器負責加載文件系統、網絡或其餘來源的類文件。javascript

JVM中類加載器的樹狀層次結構

Java 中的類加載器大體能夠分紅兩類,一類是系統提供的,另一類則是由 Java 應用開發人員編寫的。html

引導類加載器(bootstrap class loader):

它用來加載 Java 的核心庫(jre/lib/rt.jar),是用原生C++代碼來實現的,並不繼承自java.lang.ClassLoader。java

加載擴展類和應用程序類加載器,並指定他們的父類加載器,在java中獲取不到。 bootstrap

擴展類加載器(extensions class loader):

它用來加載 Java 的擴展庫(jre/ext/*.jar)。Java 虛擬機的實現會提供一個擴展庫目錄。該類加載器在此目錄裏面查找並加載 Java 類。 安全

系統類加載器(system class loader):

它根據 Java 應用的類路徑(CLASSPATH)來加載 Java 類。通常來講,Java 應用的類都是由它來完成加載的。能夠經過 ClassLoader.getSystemClassLoader()來獲取它。markdown

自定義類加載器(custom class loader):

除了系統提供的類加載器之外,開發人員能夠經過繼承 java.lang.ClassLoader類的方式實現本身的類加載器,以知足一些特殊的需求。網絡

Java類加載器基於三個機制:委託、可見性和單一性

委託機制

是指將加載一個類的請求交給父類加載器,若是這個父類加載器不可以找到或者加載這個類,那麼再加載它。app

可見性的原理

是子類的加載器能夠看見全部的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。ide

單一性原理

是指僅加載一個類一次,這是由委託機制確保子類加載器不會再次加載父類加載器加載過的類。測試

類加載過程詳解

這裏寫圖片描述

JVM將類加載過程分爲三個步驟:

裝載(Load),連接(Link)和初始化(Initialize)

1) 裝載:

  查找並加載類的二進制數據;

2)連接:

  驗證:確保被加載類信息符合JVM規範、沒有安全方面的問題。

  準備:爲類的靜態變量分配內存,並將其初始化爲默認值。

  解析:把虛擬機常量池中的符號引用轉換爲直接引用。

3)初始化:

  爲類的靜態變量賦予正確的初始值。

解析部分解釋

Java 中,虛擬機會爲每一個加載的類維護一個常量池【不一樣於字符串常量池,這個常量池只是該類的字面值(例如類名、方法名)和符號引用的有序集合。 而字符串常量池,是整個JVM共享的】這些符號(如int a = 5;中的a)就是符號引用,而解析過程就是把它轉換成指向堆中的對象地址的相對地址。

類的初始化步驟:

1)若是這個類尚未被加載和連接,那先進行加載和連接

2)假如這個類存在直接父類,而且這個類尚未被初始化(注意:在一個類加載器中,類只能初始化一次),那就初始化直接的父類(不適用於接口)

3)若是類中存在static標識的塊,那就依次執行這些初始化語句。

類加載器的工做原理

委託機制

當一個類加載和初始化的時候,類僅在有須要加載的時候被加載。假設你有一個應用須要的類叫做Abc.class,首先加載這個類的請求由Application類加載器委託給它的父類加載器Extension類加載器,而後再委託給Bootstrap類加載器。Bootstrap類加載器會先看看rt.jar中有沒有這個類,由於並無這個類,因此這個請求由回到Extension類加載器,它會查看jre/lib/ext目錄下有沒有這個類,若是這個類被Extension類加載器找到了,那麼它將被加載,而Application類加載器不會加載這個類;而若是這個類沒有被Extension類加載器找到,那麼再由Application類加載器從classpath中尋找。記住classpath定義的是類文件的加載目錄,而PATH是定義的是可執行程序如javac,java等的執行路徑。

可見性機制

根據可見性機制,子類加載器能夠看到父類加載器加載的類,而反之則不行。因此下面的例子中,當Abc.class已經被Application類加載器加載過了,而後若是想要使用Extension類加載器加載這個類,將會拋出java.lang.ClassNotFoundException異常。

單一性機制

根據這個機制,父加載器加載過的類不能被子加載器加載第二次。雖然重寫違反委託和單一性機制的類加載器是可能的,但這樣作並不可取。你寫本身的類加載器的時候應該嚴格遵照這三條機制。

java.lang.ClassLoader類介紹

java.lang.ClassLoader類的基本職責就是根據一個指定的類的名稱,找到或者生成其對應的字節代碼,而後從這些字節代碼中定義出一個Java 類,即 java.lang.Class類的一個實例。

ClassLoader提供了一系列的方法,比較重要的方法如:

這裏寫圖片描述

自定義類加載器

public class MyClassLoader extends ClassLoader{

    private String rootPath;

    public MyClassLoader(String rootPath){
        this.rootPath = rootPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        //check if the class have been loaded
        Class<?> c = findLoadedClass(name);        
        if(c!=null){
            return c;
        }
        //load the class
        byte[] classData = getClassData(name);
        if(classData==null){
            throw new ClassNotFoundException();
        }
        else{
            c = defineClass(name,classData, 0, classData.length);
            return c;
        }    
    }

    private byte[] getClassData(String className){
        String path = rootPath+"/"+className.replace('.', '/')+".class";

        InputStream is = null;
        ByteArrayOutputStream bos = null;
        try {
            is = new FileInputStream(path);
            bos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int temp = 0;
            while((temp = is.read(buffer))!=-1){
                bos.write(buffer,0,temp);
            }
            return bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                is.close();
                bos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }            
        }

        return null;        
    }    

}

測試自定義的類加載器

建立一個測試類HelloWorld

package testOthers;

public class HelloWorld {

}

在D盤根目錄建立一個testOthers文件夾,編譯HelloWorld.java,將獲得的class文件放到testOthers文件夾下。

利用以下代碼進行測試

public class testMyClassLoader {
    @Test
    public void test() throws Exception{
        MyClassLoader loader = new MyClassLoader("D:");
        Class<?> c = loader.loadClass("testOthers.HelloWorld");
        System.out.println(c.getClassLoader());
    }
}

說明HelloWorld類是被咱們的自定義類加載器MyClassLoader加載的

參考:
http://www.importnew.com/6581.html
http://www.cnblogs.com/sunniest/p/4574080.html

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
相關文章
相關標籤/搜索