国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

java 類加載器

2019-11-14 22:18:01
字體:
來源:轉載
供稿:網友
java 類加載器類加載器說明

類加載器負責將.class文件加載到內存中,并為類生成一個java.lang.Class實例。

一旦一個類被加載入JVM中,同一個類就不會被再次加入了。在JVM中用來判斷類的唯一性標識是:類名、類所在的包名和類加載器。

當JVM啟動時,會形成由三個類加載器組成的初始類加載器層次結構:

  • BootStrap ClassLoader:根類加載器;
  • Extension ClassLoader:擴展類加載器;
  • System ClassLoader:系統類加載器。

根類加載器,負責加載java的核心類(即JAVA_HOME/jre/lib下的jar包)。當使用-Xbootclasspath選項或使用-D選項指定sun.boot.class.path系統屬性也可以指定加載附加的類。根類加載器比較特殊,他并不是java.lang.ClassLoader的子類,而是由JVM自身實現的。

擴展類加載器,負責加載JRE的擴展目錄(即JAVA_HOME/jre/lib/ext目錄)下的jar包。

系統類加載器,負責在JVM啟動時,加載來自命令java中的-classpath選項或java.class.path系統屬性,或CLASSPATH環境變量所指定的jar包和類路徑,或者是當前類路徑。程序可以通過ClassLoader的靜態方法getSystemClassLoader()方法獲取系統類加載器。

除了Java提供的這三種類加載器,開發者也可以實現自己的類加載器,自定義的類加載器通過繼承ClassLoader來實現。

JVM中這四種類加載器的層次結構如圖:

image

如圖所示:根類加載器是擴展類加載器的父類加載器;擴展類加載器是系統類加載器的父類加載器;如果沒有特別指定,用戶自定義的類加載器都以系統類加載器作為父加載器。

需要提下,類加載器之間的父子關系并不是類繼承上的父子關系,而是類加載器實例之間的關系。

類加載機制

JVM的類加載機制主要有如下三種機制:

  • 全盤負責:全盤負責機制是說當一個類加載器負責加載某個類的時候,該類所依賴的和引用的其他類也將由該類加載器負責載入,除非顯式使用另一個類加載器來載入;
  • 父類委托:父類委托機制是說當一個類加載器負責加載某個類的時候,先讓父類加載器嘗試載入該類,若無法載入,才嘗試從自己的類路徑中載入;
  • 緩存機制:緩存機制會保證所有被加載過的類都會被緩存。當一個類加載器負責加載某個類的時候,會先從緩存中搜尋該類,只有當緩存中不存在該類對象時,才會載入該類并存放于緩存中。這也是為什么我們修改了某個類以后需要重啟動JVM,所做的調整才會生效的原因。

類加載器加載類大致要經過8個步驟:

  1. 檢測目標類是否有載入過(即在緩存中是否有這個類),如果有,則直接進入第八步;
  2. 如果父加載器不存在(父加載器不存在有兩種情形,一是當前加載器的parent是根加載器,二是當前加載器就是根加載器),則跳到第四步執行;如果父加載器存在,則執行第三步;
  3. 請求父加載器載入目標類,如果成功跳到第八步,不成功則跳到第五步;
  4. 請求使用根加載器加載目標類,如果成功跳到第八步,不成功則跳到第七步;
  5. 尋找.class文件(從相關的類加載器的加載路徑中查找),如果找到則執行第六部,找不到則執行第七步;
  6. 從文件中載入類,成功載入則跳到第八步;
  7. 拋出ClassNotFoundException;
  8. 返回類。
自定義類加載器

JVM中除了根類加載器之外的類加載器都是ClassLoader的子類的實例。開發者可以通過繼承ClassLoader,并重寫ClassLoader的方法來實現自定義類加載器。

ClassLoader類有三個關鍵方法:

  • loadClass(String name, boolean resolve):該方法為ClassLoader的入口點,根據指定的二進制名稱來加載類;
  • findClass(String name):根據指定的二進制名稱來查找類;
  • defineClass(String name, byte[] b, int off, int len):將指定類的字節碼文件(即.class文件)讀入字節數組byte[] b內,并把它轉為Class對象,該字節碼文件可以來源于網絡或磁盤。

要實現自定義的ClassLoader,可以通過重寫loadClass或findClass來實現。不過一般推薦重寫findClass,而不是loadClass,看一下loadClass的執行步驟:

  • 用findLoadClass來檢查是否已經加載類,如果已經加載則直接返回;
  • 在父類加載器上調用loadClass方法。如果父類加載器為null,則使用根類加載器來加載;
  • 調用findClass方法來查找類。

從上面看來,采用重寫findClass的方法可以避免覆蓋默認類加載器的父類委托和緩存機制兩種策略。

以下是一個自定類加載器的實例:

package com.zhyea.test;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Method;/** * 自定義類加載器實例 * @author robin * */public class MyClassLoader extends ClassLoader {    /**     * 讀取類文件     *      * @param filePath     *            類文件路徑     * @return     * @throws IOException     */    PRivate byte[] getBytes(String filePath) throws IOException {        InputStream in = null;        try {            File file = new File(filePath);            long length = file.length();            in = new FileInputStream(file);            byte[] buffer = new byte[(int) length];            int hasRead = in.read(buffer);            if (hasRead != length) {                throw new IOException("無法讀取全部文件:" + hasRead + "!=" + length);            }            return buffer;        } finally {            if (null != in)                in.close();        }    }    /**     * 編譯Java文件     *      * @param javaFile     *            Java文件     * @return     * @throws IOException     * @throws InterruptedException     */    private boolean compile(String javaFile) throws IOException,            InterruptedException {        // 調用系統javac命令        Process p = Runtime.getRuntime().exec("javac " + javaFile);        // 強制其他線程等待這個線程完成        p.waitFor();        // 獲取javac線程的退出值        int rtn = p.exitValue();        // 返回編譯是否成功        return rtn == 0;    }            /**     * 重寫ClassLoader的findClass類     */    @Override    protected Class<?> findClass(String name) throws ClassNotFoundException{                Class<?> clazz = null;                String javaPath = name.replace(".", "/");        String javaFileName = javaPath + ".java";        String classFileName = javaPath = ".class";                File javaFile = new File(javaFileName);        File classFile = new File(classFileName);                if(javaFile.exists() && (!classFile.exists() || javaFile.lastModified()>classFile.lastModified())){            try{                if(!compile(javaFileName) || !classFile.exists()){                    throw new ClassNotFoundException("Class not found : " + javaFileName);                }            }catch(Exception e){                e.printStackTrace();            }        }                if(classFile.exists()){            try{                byte[] raw = getBytes(classFileName);                clazz = defineClass(name, raw, 0, raw.length);            }catch(Exception e){                e.printStackTrace();            }        }                if(null==clazz){            throw new ClassNotFoundException(name);        }                return clazz;    }        public static void main(String[] args) throws Exception{        String javaName = "com.zhyea.test.MyTest";        MyClassLoader mcl = new MyClassLoader();        Class<?> clazz = mcl.loadClass(javaName);        Method main = clazz.getMethod("main", (new String[0]).getClass());        Object[] arrs = {args};         main.invoke(null, arrs);    }    }

再附上演示用的測試類,其實很簡單了:

package com.zhyea.test;/** * 自定義類加載器演示用的測試類 * @author robin * */public class MyTest {    public static void main(String[] args) {        System.out.println("This is a Test!");    }}

使用自定義的類加載器,可以實現如下功能:

  • 執行代碼前自動檢驗數字簽名;
  • 根據用戶提供的密碼解密代碼,從而可以實現代碼混淆器避免反編譯class文件;
  • 根據用戶需求來動態地加載類;
  • 根據應用需求把其他數據以字節碼的形式加載到應用中。
URLClassLoader類

URLClassLoader是擴展類加載器類和系統類加載器類的父類。URLClassLoader功能比較強大,可以從本地獲取java文件來加載類,也可獲取遠程文件來加載類。

演示下:

package com.zhyea.test;import java.net.URL;import java.net.URLClassLoader;public class UCLTest {    public static void main(String[] args) throws Exception {        URL[] urls = {new URL("file:com.zhyea.test.MyTest.java")};        URLClassLoader ucl = new URLClassLoader(urls);        MyTest mt = (MyTest) ucl.loadClass("com.zhyea.test.MyTest").newInstance();        mt.test();        ucl.close();    }}package com.zhyea.test;/** * URLClassLoader演示用的測試類 * @author robin * */public class MyTest {    public void test(){        System.out.println("This is a Test 2!");    }}

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 通化县| 监利县| 泸西县| 金塔县| 博兴县| 兴国县| 石楼县| 那曲县| 黄冈市| 朝阳区| 鹤壁市| 沐川县| 吉隆县| 祁东县| 仙桃市| 武城县| 红安县| 三都| 桃园县| 莎车县| 资阳市| 山东省| 龙海市| 新绛县| 班戈县| 普兰店市| 博湖县| 鄂尔多斯市| 建昌县| 建始县| 尼勒克县| 义乌市| 乳源| 娱乐| 乌什县| 当雄县| 浦城县| 通城县| 正宁县| 崇礼县| 张家港市|