AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

JVM類加載:如何手寫自定義類加載器,命名空間詳解

來源: 責編: 時間:2024-04-09 09:06:37 147觀看
導讀二進制名字如java.net.URLClassLoader$3$1 表示URLClassLoader的第三個匿名內部類中的第一個匿名內部類。ClassLoader分析A class loader is an object that is responsible for loading classes. The class ClassLoa

Ygf28資訊網——每日最新資訊28at.com

二進制名字

Ygf28資訊網——每日最新資訊28at.com

如java.net.URLClassLoader$3$1 表示URLClassLoader的第三個匿名內部類中的第一個匿名內部類。Ygf28資訊網——每日最新資訊28at.com

ClassLoader分析A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class. A typical strategy is to transform the name into a file name and then read a “class file” of that name from a file system. Every Class object contains a reference to the ClassLoader that defined it. Class objects for array classes are not created by class loaders, but are created automatically as required by the Java runtime. The class loader for an array class, as returned by Class.getClassLoader() is the same as the class loader for its element type; if the element type is a primitive type, then the array class has no class loader.
  • 類加載器是負責加載類的對象。類加載器是一個抽象類。給定類的二進制名,類加載器應該嘗試定位或生成構成類定義的數據(回去查找對應的class文件如果沒有解析成class文件)。一個典型的策略是將名稱轉換為文件名,然后從文件中讀取該名稱的“類文件”系統。(我們編寫的java程序類都是這樣運行)。
  • 每個類對象都包含對定義它的類加載器的引用(getClassLoader()獲取當前類的類加載器)。
  • 數組類的類對象不是由類加載器創建的,而是由類加載器創建的根據Java運行時的要求自動執行(數組類對象是由jvm虛擬機動態創建的)。數組類的類加載器,如 getclassloader()返回的類與它的元素類型的類裝入器相同(數組元素類型是什么類加載器就是什么類型);如果 元素類型是基本(原生8種)類型,因此數組類沒有類裝入器。
  • 應用程序實現的ClassLoader類加載器為了擴展Java虛擬機動態加載的方式類。

示例:Ygf28資訊網——每日最新資訊28at.com

public class Test15 {    public static void main(String[] args) {        String[] strings = new String[2];        System.out.println(strings.getClass().getClassLoader()); //string[]數組元素類型是String 在rt.jar包中是由根類加載器加載的        System.out.println("----------------");        Test15[] test15s = new Test15[2];        System.out.println(test15s.getClass().getClassLoader());//同理 該元素是由AppClassLoader系統類加載器加載的 但是數組本身不是由類加載器加載        System.out.println("----------------");        int[] ints = new int[2];//如果 元素類型是基本(原生8種)類型,因此數組類沒有類裝入器。        System.out.println(ints.getClass().getClassLoader());            }}

打印:Ygf28資訊網——每日最新資訊28at.com

/*null 根類加載器----------------sun.misc.Launcher$AppClassLoader@18b4aac2----------------null 為空 */
  • 安全管理器通常會使用類裝入器來指示安全域(類加載始終伴隨著安全管理器所以類加載是安全的)
  • ClassLoader類使用委托模型進行搜索類和資源。ClassLoader的每個實例都有一個關聯的父類裝入器。當請求查找一個類或資源,一個類加載器實例將委托父類搜索類或資源,然后再嘗試查找類或資源本身。虛擬機的內置類加載器,稱為“啟動/根類加載器”,本身沒有父類,但可以充當類裝入器實例的父類。
  • 支持類的并發加載的類加載器稱為 并行能力類加載器,需要注冊類的初始化時間registerAsParallelCapable。ClassLoader.registerAsParallelCapable這個方法。注意,類裝入器類被注冊為并行類默認為able。但是,它的子類仍然需要注冊它們自己如果他們是并行的能力。
  • 委托模型不嚴格的環境層次結構,類加載器需要能夠并行,否則類加載可能導致死鎖,因為加載器鎖被持有類加載過程的持續時間(參見{@link #loadClass)方法。
  • 通常,Java虛擬機從本地文件加載類平臺相關的系統。例如,在UNIX系統中在CLASSPATH環境變量定義的目錄加載類
  • 然而,有些類可能不是起源于一個文件;他們可能會產生從其他來源,如網絡,或它們可以由一個應用程序(動態代理)。方法{@link #defineClass(String, byte[], int, int)defineClass}將字節數組轉換為類的實例可以使用以下命令創建這個新定義的類的實例 {@link Class#newInstance Class.newInstance}。
  • 類加載器創建的對象的方法和構造函數可以引用其他類。要確定所引用的類即Java虛擬機調用{@link #loadClass loadClass}方法(這個方法解決這個問題)最初創建類的類加載器。
ClassLoader loade = new NetworkClassLoader(host,port);Object main = loader.loadClass("Main", true).newInstance();

自定義加載器子類必須定義兩個方法{@link#findClass findClass}和loadClassData加載類來自網絡。一旦它下載了構成類的字節,應該使用方法{@link #defineClass defineClass} to創建一個類實例。一個示例實現是:Ygf28資訊網——每日最新資訊28at.com

class NetworkClassLoader extends ClassLoader {          String host;          int port;           public Class findClass(String name) {              byte[] b = loadClassData(name);              return defineClass(name, b, 0, b.length);//通過名字將Class對象返回給調用者          }           private byte[] loadClassData(String name) {              // load the class data from the connection             .          }      }

編寫自定義類加載器

自定一 此時因為在ClassPath下 所以會調用父類AppClassLoader的系統類加載器 所以自定義的的findClass不會被執行。Ygf28資訊網——每日最新資訊28at.com

public class Test16 extends ClassLoader {    private String classLoaderName;    private final String fileExtension = ".class";    public Test16(String classLoaderName) {        // this(checkCreateClassLoader(), getSystemClassLoader());        // ClassLoader中當創建新的類加載器返回的的是系統類加載器, 所以當創建新的類加載器 默認父加載器為系統類加載器        super();//可加可不加        this.classLoaderName = classLoaderName;    }    public Test16(ClassLoader parent, String classLoaderName) {        // this(checkCreateClassLoader(), parent);        //ClassLoader中當創建新的類加載器自定義父加載器 如 :        //a繼承b b繼承ClassLoader  此時a可以拿這個構造方法將b作為自己的雙親 不一定都交給系統類加載器        super(parent);        this.classLoaderName = classLoaderName;    }    /**     * 查找指定二進制名字的class 這個方法應該被子類加載器實現重寫,再檢查完對應父加載器之后該方法會被loaderClass()方法調用 ,     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫     * 如:     * java.lang.String     *     * @param className     * @return     * @throws ClassNotFoundException     */    @Override    protected Class<?> findClass(String className) throws ClassNotFoundException {        //對應二進制名字對應的字節數組        byte[] data = this.loadClassData(className);                System.out.println("findClass invoked" + className);        System.out.println("class loader name" + classLoaderName);                //defineClass(類名,字節數據,起,末) 創建類實例        return this.defineClass(className, data, 0, data.length);    }    //獲取文件字節數據    private byte[] loadClassData(String name) {        InputStream is = null;        byte[] data = null;        ByteArrayOutputStream baos = null;        try {            //傳過來的文件名加上后綴            is = new FileInputStream(new File(name + this.fileExtension));            baos = new ByteArrayOutputStream();            int ch = 0;            while (-1 != (ch = is.read())) {                baos.write(ch);            }            data = baos.toByteArray();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                is.close();                baos.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return data;    }    public static void main(String[] args) throws Exception {        Test16 test16 = new Test16("test16");        test(test16);    }    public static void test(ClassLoader classLoader) throws IllegalAccessException, Exception {        //改方法會調用我們重寫之后的findClass方法        Class<?> clasz = classLoader.loadClass("com.example.demo.com.jvm.Test1");        Object o = clasz.newInstance();        System.out.println(o);    }}

打印結果:Ygf28資訊網——每日最新資訊28at.com

//只輸出了com.example.demo.com.jvm.Test1@1eb44e46

基于上例重構 新增自定義路徑將class字節碼路徑放在其他位置 此時父類加載器appClassLoader無法加載 此時就會調用自己的findClass() 需要將classpath 下的需要加載的.class刪除。Ygf28資訊網——每日最新資訊28at.com

public class Test16 extends ClassLoader {    private String classLoaderName;    //路徑    private String path;    private final String fileExtension = ".class";    public Test16(String classLoaderName) {        // this(checkCreateClassLoader(), getSystemClassLoader());        // ClassLoader中當創建新的類加載器返回的的是系統類加載器, 所以當創建新的類加載器 默認父加載器為系統類加載器        super();//可加可不加        this.classLoaderName = classLoaderName;    }    public void setPath(String path) {        this.path = path;    }    public Test16(ClassLoader parent, String classLoaderName) {        // this(checkCreateClassLoader(), parent);        //ClassLoader中當創建新的類加載器自定義父加載器 如 :        //a繼承b b繼承ClassLoader  此時a可以拿這個構造方法將b作為自己的雙親 不一定都交給系統類加載器        super(parent);        this.classLoaderName = classLoaderName;    }    /**     * 查找指定二進制名字的class 這個方法應該被子類加載器實現重新,再檢查完對應父加載器之后該方法會被loaderClass()方法調用 ,     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫     * 如:     * java.lang.String     *     * @param className     * @return     * @throws ClassNotFoundException     */    @Override    protected Class<?> findClass(String className) throws ClassNotFoundException {        //對應二進制名字對應的字節數組        byte[] data = this.loadClassData(className);        System.out.println("findClass invoked" + className);        System.out.println("class loader name= " + classLoaderName);        //defineClass(類名,字節數據,起,末) 創建類實例        return this.defineClass(className, data, 0, data.length);    }    //獲取文件字節數據    private byte[] loadClassData(String className) {        InputStream is = null;        byte[] data = null;        ByteArrayOutputStream baos = null;        className = className.replace(".", "http://");        try {            //傳過來的文件名加上后綴            is = new FileInputStream(new File(this.path + className + this.fileExtension));            baos = new ByteArrayOutputStream();            int ch = 0;            while (-1 != (ch = is.read())) {                baos.write(ch);            }            data = baos.toByteArray();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                is.close();                baos.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return data;    }    public static void main(String[] args) throws Exception {        Test16 test16 = new Test16("test16");//        test16.setPath("D://workspaces//zookeeper//target//classes//");        test16.setPath("E://cx//");        //改方法會調用我們重寫之后的findClass方法        Class<?> clasz = test16.loadClass("com.example.demo.com.jvm.Test1");        System.out.println("class: " + clasz.hashCode());        Object o = clasz.newInstance();//對象內存地址有哈希值        System.out.println(o);    }}
此時findClass中的打印語句執行了 findClass invokedcom.example.demo.com.jvm.Test1class loader name= test16class: 1365202186com.example.demo.com.jvm.Test1@626b2d4a

當我們在編寫完自定義類加載器時重寫了loadClassData和findClass方法。在main方法中實例化Test16對象調用返回系統類加載器的構造函數,因為在classpath路徑以上(雙親委托下并沒有找到對應的.class文件 所以自定義加載器去加載 此時調用classLoader的loadClass方法獲取對應的Class實例 此時自定義類加載器并沒有直接調用findClass方法 而是在loadClass方法中ClassLoader幫我們直接調用了我們自己重寫好的findclass方法。Ygf28資訊網——每日最新資訊28at.com

Ygf28資訊網——每日最新資訊28at.com

調用findClassYgf28資訊網——每日最新資訊28at.com

方法只是拋出了一個異常所有我們在自定義類加載器時必須重寫對應方法。Ygf28資訊網——每日最新資訊28at.com

Ygf28資訊網——每日最新資訊28at.com

重寫方法Ygf28資訊網——每日最新資訊28at.com

當我們調用對應的方法完畢。Ygf28資訊網——每日最新資訊28at.com

Ygf28資訊網——每日最新資訊28at.com

調用完畢Ygf28資訊網——每日最新資訊28at.com

重寫loadClassData方法將獲取對應二進制類名文件字節數組。Ygf28資訊網——每日最新資訊28at.com

Ygf28資訊網——每日最新資訊28at.com

重寫loadClassDataYgf28資訊網——每日最新資訊28at.com

在通過方法獲取對應二進制名稱的Class對象。Ygf28資訊網——每日最新資訊28at.com

Ygf28資訊網——每日最新資訊28at.com

通過defineClassYgf28資訊網——每日最新資訊28at.com

而在ClassLoader中的defineClass方法調用了重載的defineClass方法多加了個ProtectionDomainProtectionDomain 類封裝域的特征,域中包裝一個類集合,在代表給定的主體集合執行這些類的實例時會授予它們一個權限集合。主要是支持支持動態安全策略。Ygf28資訊網——每日最新資訊28at.com

在這個方法里面才是真正獲取對應二進制名字的Class對象。Ygf28資訊網——每日最新資訊28at.com

protected final Class<?> defineClass(String name, byte[] b, int off, int len,                                         ProtectionDomain protectionDomain)        throws ClassFormatError    {        //前置處理        protectionDomain = preDefineClass(name, protectionDomain);        String source = defineClassSourceLocation(protectionDomain);        //此時調用底層本地C++代碼獲取Class         Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);        //后置處理拼接對象后綴名        postDefineClass(c, protectionDomain);        return c;    }

自此程序運行結束 返回Class對象。Ygf28資訊網——每日最新資訊28at.com

ClassLoader 中loadClass 詳解

  • classLoader.lordClass和forName的區別(主動加載和被動加載的區別)。
  • class.forName()除了將類的.class文件加載到jvm中之外,還會對類進行解釋,執行類中的static塊。
  • 而classLoader.lordClass只干一件事情,就是將.class文件加載到jvm中,不會執行static中的內容,只有在newInstance才會去執行static塊。(不初始)。
  • Class.forName(name, initialize, loader)帶參函數也可控制是否加載static塊。并且只有調用了newInstance()方法才用調用構造函數,來創建類的對象(初始)。

ClassLoader 中loadClass 此時獲取的Class還沒有鏈接 只是剛加載到JVM中。Ygf28資訊網——每日最新資訊28at.com

加載指定的二進制名的類此方法的實現會默認按照以下的順序尋找類

  • 調用{@link #findLoadedClass(String)}檢查類是否已經加載(一個類只能被加載一次)。
  • 調用父類的{@link #loadClass(String) loadClass}方法,如果父類是null 就會使用虛擬機內置的根類加載器。
  • 調用{@link #findClass(String)}方法查找。

如果類被發現使用上述步驟,和解析標志為真,此方法將調用{@link#resolveClass(Class)}方法的結果類對象。
子類ClassLoader被鼓勵重寫{@link#findClass(String)},而不是這個方法。
Ygf28資訊網——每日最新資訊28at.com

在整個類裝入過程中除非被覆蓋,否則此方法對的結果進行同步{@link #getClassLoadingLock getClassLoadingLock}方法。Ygf28資訊網——每日最新資訊28at.com

protected Class<?> loadClass(String name, boolean resolve)        throws ClassNotFoundException    {        synchronized (getClassLoadingLock(name)) {            // 檢查類是否已經加載(一個類只能被加載一次)            Class<?> c = findLoadedClass(name);            if (c == null) {                long t0 = System.nanoTime();                try {                    if (parent != null) {                       //如果父類不是null 就會使用虛擬機內置的根類加載器去加載二進制名(name對應的數據),                       //子類ClassLoader被鼓勵重寫                        c = parent.loadClass(name, false);                    } else {                        c = findBootstrapClassOrNull(name);                    }                } catch (ClassNotFoundException e) {                    // ClassNotFoundException thrown if class not found                    // from the non-null parent class loader                }                if (c == null) {                    // If still not found, then invoke findClass in order                    // to find the class.                    long t1 = System.nanoTime();                    c = findClass(name);                    // this is the defining class loader; record the stats                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);                    sun.misc.PerfCounter.getFindClasses().increment();                }            }           // 如果類被發現使用上述步驟,和解析標志為真,此方法將調用{@link#resolveClass(Class)}方法的結果類對象。            if (resolve) {                resolveClass(c);            }            //返回Class            return c;        }    }

基于上例Test16繼續重構。Ygf28資訊網——每日最新資訊28at.com

public class Test16 extends ClassLoader {    private String classLoaderName;    //路徑    private String path;    private final String fileExtension = ".class";    public Test16(String classLoaderName) {        // this(checkCreateClassLoader(), getSystemClassLoader());        // ClassLoader中當創建新的類加載器返回的的是系統類加載器, 所以當創建新的類加載器 默認父加載器為系統類加載器        super();//可加可不加        this.classLoaderName = classLoaderName;    }    public void setPath(String path) {        this.path = path;    }    public Test16(ClassLoader parent, String classLoaderName) {        // this(checkCreateClassLoader(), parent);        //ClassLoader中當創建新的類加載器自定義父加載器 如 :        //a繼承b b繼承ClassLoader  此時a可以拿這個構造方法將b作為自己的雙親 不一定都交給系統類加載器        super(parent);        this.classLoaderName = classLoaderName;    }    /**     * 查找指定二進制名字的class 這個方法應該被子類加載器實現重新,再檢查完對應父加載器之后該方法會被loaderClass()方法調用 ,     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫     * 如:     * java.lang.String     *     * @param className     * @return     * @throws ClassNotFoundException     */    @Override    protected Class<?> findClass(String className) throws ClassNotFoundException {        //對應二進制名字對應的字節數組        byte[] data = this.loadClassData(className);        System.out.println("findClass invoked:" + className);        System.out.println("class loader name: " + classLoaderName);        //defineClass(類名,字節數據,起,末) 創建類實例        return this.defineClass(className, data, 0, data.length);    }    //獲取文件字節數據    private byte[] loadClassData(String className) {        InputStream is = null;        byte[] data = null;        ByteArrayOutputStream baos = null;        className = className.replace(".", "http://");        try {            //傳過來的文件名加上后綴            is = new FileInputStream(new File(this.path + className + this.fileExtension));            baos = new ByteArrayOutputStream();            int ch = 0;            while (-1 != (ch = is.read())) {                baos.write(ch);            }            data = baos.toByteArray();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                is.close();                baos.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return data;    }    public static void main(String[] args) throws Exception {        Test16 test16 = new Test16("test16");//        test16.setPath("D://workspaces//zookeeper//target//classes//");        test16.setPath("E://cx//");        //改方法會調用我們重寫之后的findClass方法        Class<?> clasz = test16.loadClass("com.example.demo.com.jvm.Test1");        System.out.println("class: " + clasz.hashCode());        Object o = clasz.newInstance();//對象內存地址有哈希值        System.out.println(o);        Test16 test162 = new Test16("test17");        Class<?> clasz2 = test16.loadClass("com.example.demo.com.jvm.Test1");        System.out.println("class: " + clasz2.hashCode());        Object o2 = clasz2.newInstance();//對象內存地址有哈希值        System.out.println(o2);    }}
/*當classPath下有對應的加載的.class時 第二次交給父類加載器發現已經加載所以字節拿過來用 所以此時獲取的Class類時一致的class: 515132998com.example.demo.com.jvm.Test1@6504e3b2class: 515132998com.example.demo.com.jvm.Test1@515f550a當classPath下沒有對應的加載的.class 制定了對應的路徑 此時類獲取幾次就會加載幾次 涉及到了命名空間的問題findClass invoked:com.example.demo.com.jvm.Test1class loader name: test16class: 1365202186com.example.demo.com.jvm.Test1@626b2d4a--------------兩個不同的命名空間------------------findClass invoked:com.example.demo.com.jvm.Test1class loader name: test17class: 932583850com.example.demo.com.jvm.Test1@cac736f */

總結:同一個命名空間不會出現兩個完全相同的類,不同的命名空間會出現兩個完全相同的類,父加載器加載的類不可以看到子類加載器加載的類,但是子類加載器加載的類可以看到父類加載器加載的類。Ygf28資訊網——每日最新資訊28at.com

解釋:Ygf28資訊網——每日最新資訊28at.com

Ygf28資訊網——每日最新資訊28at.com

命名空間Ygf28資訊網——每日最新資訊28at.com

上例繼續改造://將loader1作為loader2的父類加載器。Ygf28資訊網——每日最新資訊28at.com

public class Test16 extends ClassLoader {    private String classLoaderName;    //路徑    private String path;    private final String fileExtension = ".class";    public Test16(String classLoaderName) {        // this(checkCreateClassLoader(), getSystemClassLoader());        // ClassLoader中當創建新的類加載器返回的的是系統類加載器, 所以當創建新的類加載器 默認父加載器為系統類加載器        super();//可加可不加        this.classLoaderName = classLoaderName;    }    public void setPath(String path) {        this.path = path;    }    public Test16(ClassLoader parent, String classLoaderName) {        // this(checkCreateClassLoader(), parent);        //ClassLoader中當創建新的類加載器自定義父加載器 如 :        //a繼承b b繼承ClassLoader  此時a可以拿這個構造方法將b作為自己的雙親 不一定都交給系統類加載器        super(parent);        this.classLoaderName = classLoaderName;    }    /**     * 查找指定二進制名字的class 這個方法應該被子類加載器實現重新,再檢查完對應父加載器之后該方法會被loaderClass()方法調用 ,     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫     * 如:     * java.lang.String     *     * @param className     * @return     * @throws ClassNotFoundException     */    @Override    protected Class<?> findClass(String className) throws ClassNotFoundException {        //對應二進制名字對應的字節數組        byte[] data = this.loadClassData(className);        System.out.println("findClass invoked:" + className);        System.out.println("class loader name: " + classLoaderName);        //defineClass(類名,字節數據,起,末) 創建類實例        return this.defineClass(className, data, 0, data.length);    }    //獲取文件字節數據    private byte[] loadClassData(String className) {        InputStream is = null;        byte[] data = null;        ByteArrayOutputStream baos = null;        className = className.replace(".", "http://");        try {            //傳過來的文件名加上后綴            is = new FileInputStream(new File(this.path + className + this.fileExtension));            baos = new ByteArrayOutputStream();            int ch = 0;            while (-1 != (ch = is.read())) {                baos.write(ch);            }            data = baos.toByteArray();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                is.close();                baos.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return data;    }    public static void main(String[] args) throws Exception {        Test16 loader1 = new Test16("loader1");//        test16.setPath("D://workspaces//zookeeper//target//classes//");        loader1.setPath("E://cx//");        //改方法會調用我們重寫之后的findClass方法        Class<?> clasz = loader1.loadClass("com.example.demo.com.jvm.Test1");        System.out.println("class: " + clasz.hashCode());        Object o = clasz.newInstance();//對象內存地址有哈希值        System.out.println(o);//        System.out.println("------------兩個不同的命名空間--------------------");        Test16 loader2 = new Test16(loader1,"loader2");//將loader1作為loader2的父類加載器        loader2.setPath("E://cx//");        Class<?> clasz2 = loader2.loadClass("com.example.demo.com.jvm.Test1");        System.out.println("class: " + clasz2.hashCode());        Object o2 = clasz2.newInstance();//對象內存地址有哈希值        System.out.println(o2);    }}
-------------------------------------------當classPath下沒有對應的加載的.class時Test16 loader2 = new Test16(loader1,"loader2");//將loader1作為loader2的父類加載器findClass invoked:com.example.demo.com.jvm.Test1class loader name: loader1class: 1365202186com.example.demo.com.jvm.Test1@626b2d4a  //此時父加載器loader1已經加載完畢 loader2直接拿來使用class: 1365202186com.example.demo.com.jvm.Test1@5e91993f

通過上例繼續改造: 新增一個類加載器 父類設置為loader2。Ygf28資訊網——每日最新資訊28at.com

public class Test16 extends ClassLoader {    private String classLoaderName;    //路徑    private String path;    private final String fileExtension = ".class";    public Test16(String classLoaderName) {        // this(checkCreateClassLoader(), getSystemClassLoader());        // ClassLoader中當創建新的類加載器返回的的是系統類加載器, 所以當創建新的類加載器 默認父加載器為系統類加載器        super();//可加可不加        this.classLoaderName = classLoaderName;    }    public void setPath(String path) {        this.path = path;    }    public Test16(ClassLoader parent, String classLoaderName) {        // this(checkCreateClassLoader(), parent);        //ClassLoader中當創建新的類加載器自定義父加載器 如 :        //a繼承b b繼承ClassLoader  此時a可以拿這個構造方法將b作為自己的雙親 不一定都交給系統類加載器        super(parent);        this.classLoaderName = classLoaderName;    }    /**     * 查找指定二進制名字的class 這個方法應該被子類加載器實現重新,再檢查完對應父加載器之后該方法會被loaderClass()方法調用 ,     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫     * 如:     * java.lang.String     *     * @param className     * @return     * @throws ClassNotFoundException     */    @Override    protected Class<?> findClass(String className) throws ClassNotFoundException {        //對應二進制名字對應的字節數組        byte[] data = this.loadClassData(className);        System.out.println("findClass invoked:" + className);        System.out.println("class loader name: " + classLoaderName);        //defineClass(類名,字節數據,起,末) 創建類實例        return this.defineClass(className, data, 0, data.length);    }    //獲取文件字節數據    private byte[] loadClassData(String className) {        InputStream is = null;        byte[] data = null;        ByteArrayOutputStream baos = null;        className = className.replace(".", "http://");        try {            //傳過來的文件名加上后綴            is = new FileInputStream(new File(this.path + className + this.fileExtension));            baos = new ByteArrayOutputStream();            int ch = 0;            while (-1 != (ch = is.read())) {                baos.write(ch);            }            data = baos.toByteArray();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                is.close();                baos.close();            } catch (IOException e) {                e.printStackTrace();            }        }        return data;    }    public static void main(String[] args) throws Exception {        Test16 loader1 = new Test16("loader1");//        test16.setPath("D://workspaces//zookeeper//target//classes//");        loader1.setPath("E://cx//");        //改方法會調用我們重寫之后的findClass方法        Class<?> clasz = loader1.loadClass("com.example.demo.com.jvm.Test1");        System.out.println("class: " + clasz.hashCode());        Object o = clasz.newInstance();//對象內存地址有哈希值        System.out.println(o);        System.out.println();//        System.out.println("------------兩個不同的命名空間--------------------");        Test16 loader2 = new Test16(loader1, "loader2");//將loader1作為loader2的父類加載器        loader2.setPath("E://cx//");        Class<?> clasz2 = loader2.loadClass("com.example.demo.com.jvm.Test1");        System.out.println("class: " + clasz2.hashCode());        Object o2 = clasz2.newInstance();//對象內存地址有哈希值        System.out.println(o2);        System.out.println();        Test16 loader3 = new Test16(loader2,"loader3");        loader3.setPath("E://cx//");        //改方法會調用我們重寫之后的findClass方法        Class<?> clasz3 = loader3.loadClass("com.example.demo.com.jvm.Test1");        System.out.println("class: " + clasz3.hashCode());        Object o3 = clasz3.newInstance();//對象內存地址有哈希值        System.out.println(o3);    }}

命名空間一致。Ygf28資訊網——每日最新資訊28at.com

命名空間一致findClass invoked:com.example.demo.com.jvm.Test1class loader name: loader1class: 1365202186com.example.demo.com.jvm.Test1@626b2d4a loader1 先去加類加載class: 1365202186com.example.demo.com.jvm.Test1@5e91993f loader2 交給父類父類交給appClassLoader加載發現已經加載直接拿來用class: 1365202186com.example.demo.com.jvm.Test1@1c4af82c loader3 同上

本文鏈接:http://www.tebozhan.com/showinfo-26-82180-0.htmlJVM類加載:如何手寫自定義類加載器,命名空間詳解

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 超級離譜的前端需求:搜索圖片里的文字

下一篇: 面試官:如何設計和實現一個帶過期時間的本地緩存?

標簽:
  • 熱門焦點
Top