Java 自定義 ClassLoader 實現 JVM 類加載

定義需要加載的類

為了能夠實現類加載,并展示效果,定義一個Hello類,再為其定義一個sayHello()方法,加載Hello類之后,調用它的sayHello()方法。

public class Hello {
    public static void sayHello(){
        System.out.println("Hello,I am ....");
    }
}

定義類加載器

自定義加載器,需要繼承ClassLoader,并重寫里面的protected Class<?> findClass(String name) throws ClassNotFoundException方法。

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;

public class MyClassLoader extends ClassLoader {
    /**
     * 重寫父類方法,返回一個Class對象
     * ClassLoader中對于這個方法的注釋是:
     * This method should be overridden by class loader implementations
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        Class clazz = null;
        String classFilename = name + ".class";
        File classFile = new File(classFilename);
        if (classFile.exists()) {
            try (FileChannel fileChannel = new FileInputStream(classFile)
                    .getChannel();) {
                MappedByteBuffer mappedByteBuffer = fileChannel
                        .map(MapMode.READ_ONLY, 0, fileChannel.size());
                byte[] b = mappedByteBuffer.array();
                clazz = defineClass(name, b, 0, b.length);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (clazz == null) {
            throw new ClassNotFoundException(name);
        }
        return clazz;
    }
    
    public static void main(String[] args) throws Exception{
        MyClassLoader myClassLoader = new MyClassLoader();
        Class clazz = myClassLoader.loadClass(args[0]);
        Method sayHello = clazz.getMethod("sayHello");
        sayHello.invoke(null, null);
    }
}

編譯需要加載的類文件

類加載的時候加載的是字節碼文件,所以需要預先把定義的Hello類編譯成字節友文件。

javac Hello.java

驗證字節碼文件是否編譯成功,利用二進制文件查看器查看我們編譯之后的文件,樣式如下:

0000000 177312 137272 000000 032000 016000 000012 000006 004416
0000020 007400 010000 000010 005021 011000 011400 000007 003424
0000040 012400 000001 036006 067151 072151 000476 001400 024450
0000060 000526 002000 067503 062544 000001 046017 067151 047145
0000100 066565 062542 052162 061141 062554 000001 071410 074541
0000120 062510 066154 000557 005000 067523 071165 062543 064506
0000140 062554 000001 044012 066145 067554 065056 073141 006141
0000160 003400 004000 000007 006026 013400 014000 000001 044017
0000200 066145 067554 044454 060440 020155 027056 027056 000007
0000220 006031 015000 015400 000001 044005 066145 067554 000001
0000240 065020 073141 027541 060554 063556 047457 065142 061545
0000260 000564 010000 060552 060566 066057 067141 027547 074523
0000300 072163 066545 000001 067403 072165 000001 046025 060552
0000320 060566 064457 027557 071120 067151 051564 071164 060545
0000340 035555 000001 065023 073141 027541 067551 050057 064562
0000360 072156 072123 062562 066541 000001 070007 064562 072156
0000400 067154 000001 024025 065114 073141 027541 060554 063556
0000420 051457 071164 067151 035547 053051 020400 002400 003000
0000440 000000 000000 001000 000400 003400 004000 000400 004400
0000460 000000 016400 000400 000400 000000 002400 133452 000400
0000500 000261 000000 000001 000012 000000 000006 000001 000000
0000520 000002 000011 000013 000010 000001 000011 000000 000045
0000540 000002 000000 000000 131011 001000 001422 000266 130404
0000560 000000 000400 005000 000000 005000 001000 000000 002000
0000600 004000 002400 000400 006000 000000 001000 006400
0000616

編譯自定義的類加載器并支行程序

?編譯代碼
javac MyClassLoader.java
?當然我們也可以同時編譯我們所有的java源文件
javac *.java

執行成功之后,我們用下面的語句執行代碼,測試是否成功,并查看結果

java MyClassLoader Hello
?運行結果
Hello,I am ....

當程序按照預期顯示,就證明我自定義類加載器成功了。

總結

通過上面的程序代碼,簡單的實現JVM的類加載過程,知道了程序運行的一點流程。但是在編寫的時候有如下坑需要注意

  • 類文件不需要指定包,否則加載的時候我們需要額外的處理,把包中的"."替換成文件系統的路徑"/"。
  • 需要加載的Hello類中的反射調用的方法要用static修飾,這樣invoke的時候第一個參數才可以使用null關鍵字代替,否則需要創建一個對應的類實例。
    官方文檔中有這樣一句話<blockquote>If the underlying method is static, then the specified obj argument is ignored. It may be null.</blockquote>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,951評論 19 139
  • 一、JVM內幕:Java虛擬機詳解(java se 7規范) 直接上圖,再逐步解釋。 上圖顯示的組件分兩個章節解釋...
    屈小勇閱讀 1,919評論 6 22
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,766評論 18 399
  • JVM類加載機制 概述 類加載過程 加載 通過類的全限定名獲取類的二進制流 將靜態存儲結構轉化為方法區的運行時數據...
    東溪95閱讀 3,081評論 0 15
  • 這篇文章解釋了Java 虛擬機(JVM)的內部架構。下圖顯示了遵守Java SE 7 規范的典型的 JVM 核心內...
    飲墨饗書閱讀 722評論 0 1