深入理解和探究Java類載入機制-

来源:http://www.cnblogs.com/shouce/archive/2016/04/11/5376936.html
-Advertisement-
Play Games

深入理解和探究Java類載入機制 1.java.lang.ClassLoader類介紹 java.lang.ClassLoader類的基本職責就是根據一個指定的類的名稱,找到或者生成其對應的位元組代碼,然後從這些位元組代碼中定義出一個Java 類,即 java.lang.Class類的一個實例。 Cla ...


深入理解和探究Java類載入機制----

1.java.lang.ClassLoader類介紹

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

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

 

 

2.JVM中類載入器的樹狀層次結構

Java 中的類載入器大致可以分成兩類,一類是系統提供的,另外一類則是由 Java 應用開發人員編寫的。 

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

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

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

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

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

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

它根據 Java 應用的類路徑(CLASSPATH)來載入 Java 類。一般來說,Java 應用的類都是由它來完成載入的。可以通過 ClassLoader.getSystemClassLoader()來獲取它。

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

除了系統提供的類載入器以外,開發人員可以通過繼承 java.lang.ClassLoader類的方式實現自己的類載入器,以滿足一些特殊的需求。

 

以下測試代碼可以證明此層次結構:

複製代碼
public class testClassLoader {
    @Test
    public void test(){
        //application class loader
        System.out.println(ClassLoader.getSystemClassLoader());
        //extensions class loader
        System.out.println(ClassLoader.getSystemClassLoader().getParent());
        //bootstrap class loader
        System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
    }
}
複製代碼

輸出為:

 

可以看出ClassLoader類是由AppClassLoader載入的。他的父親是ExtClassLoader,ExtClassLoader的父親無法獲取是因為它是用C++實現的。

 

3.雙親委派機制

  某個特定的類載入器在接到載入類的請求時,首先將載入任務委托交給父類載入器,父類載入器又將載入任務向上委托,直到最父類載入器,如果最父類載入器可以完成類載入任務,就成功返回,如果不行就向下傳遞委托任務,由其子類載入器進行載入。

雙親委派機制的好處:

  保證java核心庫的安全性(例如:如果用戶自己寫了一個java.lang.String類就會因為雙親委派機制不能被載入,不會破壞原生的String類的載入)

代理模式

  與雙親委派機制相反,代理模式是先自己嘗試載入,如果無法載入則向上傳遞。tomcat就是代理模式。

 

4.自定義類載入器

複製代碼
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載入的

 

5.類載入過程詳解

JVM將類載入過程分為三個步驟:裝載(Load),鏈接(Link)和初始化(Initialize)

1) 裝載

  查找並載入類的二進位數據;

2)鏈接

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

  準備:為類的靜態變數分配記憶體,並將其初始化為預設值。

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

3)初始化

  為類的靜態變數賦予正確的初始值。

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

 

類的初始化步驟:

1)如果這個類還沒有被載入和鏈接,那先進行載入和鏈接

2)假如這個類存在直接父類,並且這個類還沒有被初始化(註意:在一個類載入器中,類只能初始化一次),那就初始化直接的父類(不適用於介面)

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

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這個寫的很不錯 http://www.cnblogs.com/Mr-xu/archive/2012/08/07/2626973.html 什麼叫引用? 引用就是對某對象的另一個名字。 主要用途: 為了描述函數的參數和返回值,特別是運算符的重載。 用法: X var; X& r = var; 那麼r是 ...
  • Spring這類的框架給我們開髮帶來非常大的好處,讓我們更加快速、有效的開發。 所以我們在開發中通常都會用到各種框架,每個框架都有很多jar包,每個jar都有各自不同的功能。開發不同的功能用到的jar也不盡相同,所以當我們用到相關框架的時候,並不是把它所有的jar都引入系統。那麼怎麼確定自己將會用到 ...
  • 準備先利用之前整理的python自帶的unittest框架 整合excel 實現介面自動化測試功能 先看看excel表格設置: 下來是對excel獲取的代碼: 之後是unittest框架 利用迴圈執行所有用例 現在只要在excel里添加介面測試用例 運行腳本 即可 ...
  • PHP及網頁使用UTF-8編碼,資料庫是sql server2008,使用預設編碼(936,即GBK編碼) 當讀取資料庫數據時,使用php自帶的json_encode()返回到前端,結果中文不顯示。 解決辦法: <?php header("Content-Type: text/html;charse ...
  • 淺談DP演算法(一) ——如何用一維數組解決01背包問題 DP演算法(Dynamic Programming,俗稱動態規劃)是最經典演算法之一.本筆記以耳熟能詳的數塔問題為引子,深入討論01背包的解決方法. 首先,如下圖所示,要求從頂層走到底層,若每一步只能走到相鄰的結點,則經過的結點的數字之和最大是多少 ...
  • 1.重命名:os.rename(old, new) 2.刪除:os.remove(file) 3.列出目錄下的文件:os.listdir(path) 4.獲取當前工作目錄:os.getcwd() 5.改變工作目錄:os.chdir(newdir) 6.創建多級目錄:os.makedirs(r"c:\ ...
  • windows定時執行PHP相信不少讀者(PHP愛好者)在工作、學習的過程中經常抱怨:在WIN如何讓PHP定時自動發信呢??如何讓MYSQL實現自動備份而無後顧之憂呢??如 果完全依靠手工進行當然也可以實現,但操作上似乎過於繁瑣了一點!彆著急,利用系統的任務計劃程式(Windows 98稱之為計劃任 ...
  • 基本數據結構:鏈表(list) 談到鏈表之前,先說一下線性表。線性表是最基本、最簡單、也是最常用的一種數據結構。線性表中數據元素之間的關係是一對一的關係,即除了第一個和最後一個數據元素之外,其它數據元素都是首尾相接的。線性表有兩種存儲方式,一種是順序存儲結構,另一種是鏈式存儲結構。 順序存儲結構就是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...