精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

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

開發(fā) 后端
類加載器是負(fù)責(zé)加載類的對象。類加載器是一個抽象類。給定類的二進(jìn)制名,類加載器應(yīng)該嘗試定位或生成構(gòu)成類定義的數(shù)據(jù)(回去查找對應(yīng)的class文件如果沒有解析成class文件)。一個典型的策略是將名稱轉(zhuǎn)換為文件名,然后從文件中讀取該名稱的“類文件”系統(tǒng)。

二進(jìn)制名字

如java.net.URLClassLoader$3$1 表示URLClassLoader的第三個匿名內(nèi)部類中的第一個匿名內(nèi)部類。

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.
  • 類加載器是負(fù)責(zé)加載類的對象。類加載器是一個抽象類。給定類的二進(jìn)制名,類加載器應(yīng)該嘗試定位或生成構(gòu)成類定義的數(shù)據(jù)(回去查找對應(yīng)的class文件如果沒有解析成class文件)。一個典型的策略是將名稱轉(zhuǎn)換為文件名,然后從文件中讀取該名稱的“類文件”系統(tǒng)。(我們編寫的java程序類都是這樣運行)。
  • 每個類對象都包含對定義它的類加載器的引用(getClassLoader()獲取當(dāng)前類的類加載器)。
  • 數(shù)組類的類對象不是由類加載器創(chuàng)建的,而是由類加載器創(chuàng)建的根據(jù)Java運行時的要求自動執(zhí)行(數(shù)組類對象是由jvm虛擬機(jī)動態(tài)創(chuàng)建的)。數(shù)組類的類加載器,如 getclassloader()返回的類與它的元素類型的類裝入器相同(數(shù)組元素類型是什么類加載器就是什么類型);如果 元素類型是基本(原生8種)類型,因此數(shù)組類沒有類裝入器。
  • 應(yīng)用程序?qū)崿F(xiàn)的ClassLoader類加載器為了擴(kuò)展Java虛擬機(jī)動態(tài)加載的方式類。

示例:

public class Test15 {

    public static void main(String[] args) {

        String[] strings = new String[2];
        System.out.println(strings.getClass().getClassLoader()); //string[]數(shù)組元素類型是String 在rt.jar包中是由根類加載器加載的

        System.out.println("----------------");

        Test15[] test15s = new Test15[2];
        System.out.println(test15s.getClass().getClassLoader());//同理 該元素是由AppClassLoader系統(tǒng)類加載器加載的 但是數(shù)組本身不是由類加載器加載

        System.out.println("----------------");

        int[] ints = new int[2];//如果 元素類型是基本(原生8種)類型,因此數(shù)組類沒有類裝入器。
        System.out.println(ints.getClass().getClassLoader());
        
    }
}

打印:

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

自定義加載器子類必須定義兩個方法{@link#findClass findClass}和loadClassData加載類來自網(wǎng)絡(luò)。一旦它下載了構(gòu)成類的字節(jié),應(yīng)該使用方法{@link #defineClass defineClass} to創(chuàng)建一個類實例。一個示例實現(xiàn)是:

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對象返回給調(diào)用者
          }
 
          private byte[] loadClassData(String name) {
              // load the class data from the connection
             .
          }
      }

編寫自定義類加載器

自定一 此時因為在ClassPath下 所以會調(diào)用父類AppClassLoader的系統(tǒng)類加載器 所以自定義的的findClass不會被執(zhí)行。

public class Test16 extends ClassLoader {

    private String classLoaderName;

    private final String fileExtension = ".class";

    public Test16(String classLoaderName) {
        // this(checkCreateClassLoader(), getSystemClassLoader());
        // ClassLoader中當(dāng)創(chuàng)建新的類加載器返回的的是系統(tǒng)類加載器, 所以當(dāng)創(chuàng)建新的類加載器 默認(rèn)父加載器為系統(tǒng)類加載器
        super();//可加可不加
        this.classLoaderName = classLoaderName;
    }

    public Test16(ClassLoader parent, String classLoaderName) {
        // this(checkCreateClassLoader(), parent);
        //ClassLoader中當(dāng)創(chuàng)建新的類加載器自定義父加載器 如 :
        //a繼承b b繼承ClassLoader  此時a可以拿這個構(gòu)造方法將b作為自己的雙親 不一定都交給系統(tǒng)類加載器
        super(parent);
        this.classLoaderName = classLoaderName;
    }


    /**
     * 查找指定二進(jìn)制名字的class 這個方法應(yīng)該被子類加載器實現(xiàn)重寫,再檢查完對應(yīng)父加載器之后該方法會被loaderClass()方法調(diào)用 ,
     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫
     * 如:
     * java.lang.String
     *
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        //對應(yīng)二進(jìn)制名字對應(yīng)的字節(jié)數(shù)組
        byte[] data = this.loadClassData(className);
        
        System.out.println("findClass invoked" + className);
        System.out.println("class loader name" + classLoaderName);
        
        //defineClass(類名,字節(jié)數(shù)據(jù),起,末) 創(chuàng)建類實例
        return this.defineClass(className, data, 0, data.length);
    }

    //獲取文件字節(jié)數(shù)據(jù)
    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 {
        //改方法會調(diào)用我們重寫之后的findClass方法
        Class<?> clasz = classLoader.loadClass("com.example.demo.com.jvm.Test1");
        Object o = clasz.newInstance();
        System.out.println(o);
    }
}

打印結(jié)果:

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

基于上例重構(gòu) 新增自定義路徑將class字節(jié)碼路徑放在其他位置 此時父類加載器appClassLoader無法加載 此時就會調(diào)用自己的findClass() 需要將classpath 下的需要加載的.class刪除。

public class Test16 extends ClassLoader {

    private String classLoaderName;

    //路徑
    private String path;

    private final String fileExtension = ".class";

    public Test16(String classLoaderName) {
        // this(checkCreateClassLoader(), getSystemClassLoader());
        // ClassLoader中當(dāng)創(chuàng)建新的類加載器返回的的是系統(tǒng)類加載器, 所以當(dāng)創(chuàng)建新的類加載器 默認(rèn)父加載器為系統(tǒng)類加載器
        super();//可加可不加
        this.classLoaderName = classLoaderName;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public Test16(ClassLoader parent, String classLoaderName) {
        // this(checkCreateClassLoader(), parent);
        //ClassLoader中當(dāng)創(chuàng)建新的類加載器自定義父加載器 如 :
        //a繼承b b繼承ClassLoader  此時a可以拿這個構(gòu)造方法將b作為自己的雙親 不一定都交給系統(tǒng)類加載器
        super(parent);
        this.classLoaderName = classLoaderName;
    }


    /**
     * 查找指定二進(jìn)制名字的class 這個方法應(yīng)該被子類加載器實現(xiàn)重新,再檢查完對應(yīng)父加載器之后該方法會被loaderClass()方法調(diào)用 ,
     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫
     * 如:
     * java.lang.String
     *
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        //對應(yīng)二進(jìn)制名字對應(yīng)的字節(jié)數(shù)組
        byte[] data = this.loadClassData(className);
        System.out.println("findClass invoked" + className);
        System.out.println("class loader name= " + classLoaderName);

        //defineClass(類名,字節(jié)數(shù)據(jù),起,末) 創(chuàng)建類實例
        return this.defineClass(className, data, 0, data.length);
    }

    //獲取文件字節(jié)數(shù)據(jù)
    private byte[] loadClassData(String className) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;

        className = className.replace(".", "\\");
        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\\");

        //改方法會調(diào)用我們重寫之后的findClass方法
        Class<?> clasz = test16.loadClass("com.example.demo.com.jvm.Test1");
        System.out.println("class: " + clasz.hashCode());

        Object o = clasz.newInstance();//對象內(nèi)存地址有哈希值
        System.out.println(o);
    }
}
此時findClass中的打印語句執(zhí)行了 
findClass invokedcom.example.demo.com.jvm.Test1
class loader name= test16

class: 1365202186
com.example.demo.com.jvm.Test1@626b2d4a

當(dāng)我們在編寫完自定義類加載器時重寫了loadClassData和findClass方法。在main方法中實例化Test16對象調(diào)用返回系統(tǒng)類加載器的構(gòu)造函數(shù),因為在classpath路徑以上(雙親委托下并沒有找到對應(yīng)的.class文件 所以自定義加載器去加載 此時調(diào)用classLoader的loadClass方法獲取對應(yīng)的Class實例 此時自定義類加載器并沒有直接調(diào)用findClass方法 而是在loadClass方法中ClassLoader幫我們直接調(diào)用了我們自己重寫好的findclass方法。

調(diào)用findClass

方法只是拋出了一個異常所有我們在自定義類加載器時必須重寫對應(yīng)方法。

重寫方法

當(dāng)我們調(diào)用對應(yīng)的方法完畢。

調(diào)用完畢

重寫loadClassData方法將獲取對應(yīng)二進(jìn)制類名文件字節(jié)數(shù)組。

重寫loadClassData

在通過方法獲取對應(yīng)二進(jìn)制名稱的Class對象。

通過defineClass

而在ClassLoader中的defineClass方法調(diào)用了重載的defineClass方法多加了個ProtectionDomainProtectionDomain 類封裝域的特征,域中包裝一個類集合,在代表給定的主體集合執(zhí)行這些類的實例時會授予它們一個權(quán)限集合。主要是支持支持動態(tài)安全策略。

在這個方法里面才是真正獲取對應(yīng)二進(jìn)制名字的Class對象。

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

自此程序運行結(jié)束 返回Class對象。

ClassLoader 中l(wèi)oadClass 詳解

  • classLoader.lordClass和forName的區(qū)別(主動加載和被動加載的區(qū)別)。
  • class.forName()除了將類的.class文件加載到j(luò)vm中之外,還會對類進(jìn)行解釋,執(zhí)行類中的static塊。
  • 而classLoader.lordClass只干一件事情,就是將.class文件加載到j(luò)vm中,不會執(zhí)行static中的內(nèi)容,只有在newInstance才會去執(zhí)行static塊。(不初始)。
  • Class.forName(name, initialize, loader)帶參函數(shù)也可控制是否加載static塊。并且只有調(diào)用了newInstance()方法才用調(diào)用構(gòu)造函數(shù),來創(chuàng)建類的對象(初始)。

ClassLoader 中l(wèi)oadClass 此時獲取的Class還沒有鏈接 只是剛加載到JVM中。

加載指定的二進(jìn)制名的類此方法的實現(xiàn)會默認(rèn)按照以下的順序?qū)ふ翌?/h3>
  • 調(diào)用{@link #findLoadedClass(String)}檢查類是否已經(jīng)加載(一個類只能被加載一次)。
  • 調(diào)用父類的{@link #loadClass(String) loadClass}方法,如果父類是null 就會使用虛擬機(jī)內(nèi)置的根類加載器。
  • 調(diào)用{@link #findClass(String)}方法查找。

如果類被發(fā)現(xiàn)使用上述步驟,和解析標(biāo)志為真,此方法將調(diào)用{@link#resolveClass(Class)}方法的結(jié)果類對象。
子類ClassLoader被鼓勵重寫{@link#findClass(String)},而不是這個方法。

在整個類裝入過程中除非被覆蓋,否則此方法對的結(jié)果進(jìn)行同步{@link #getClassLoadingLock getClassLoadingLock}方法。

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 檢查類是否已經(jīng)加載(一個類只能被加載一次)
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                       //如果父類不是null 就會使用虛擬機(jī)內(nèi)置的根類加載器去加載二進(jìn)制名(name對應(yīng)的數(shù)據(jù)),
                       //子類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();
                }
            }
           // 如果類被發(fā)現(xiàn)使用上述步驟,和解析標(biāo)志為真,此方法將調(diào)用{@link#resolveClass(Class)}方法的結(jié)果類對象。
            if (resolve) {
                resolveClass(c);
            }
            //返回Class
            return c;
        }
    }

基于上例Test16繼續(xù)重構(gòu)。

public class Test16 extends ClassLoader {

    private String classLoaderName;

    //路徑
    private String path;

    private final String fileExtension = ".class";

    public Test16(String classLoaderName) {
        // this(checkCreateClassLoader(), getSystemClassLoader());
        // ClassLoader中當(dāng)創(chuàng)建新的類加載器返回的的是系統(tǒng)類加載器, 所以當(dāng)創(chuàng)建新的類加載器 默認(rèn)父加載器為系統(tǒng)類加載器
        super();//可加可不加
        this.classLoaderName = classLoaderName;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public Test16(ClassLoader parent, String classLoaderName) {
        // this(checkCreateClassLoader(), parent);
        //ClassLoader中當(dāng)創(chuàng)建新的類加載器自定義父加載器 如 :
        //a繼承b b繼承ClassLoader  此時a可以拿這個構(gòu)造方法將b作為自己的雙親 不一定都交給系統(tǒng)類加載器
        super(parent);
        this.classLoaderName = classLoaderName;
    }
    /**
     * 查找指定二進(jìn)制名字的class 這個方法應(yīng)該被子類加載器實現(xiàn)重新,再檢查完對應(yīng)父加載器之后該方法會被loaderClass()方法調(diào)用 ,
     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫
     * 如:
     * java.lang.String
     *
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        //對應(yīng)二進(jìn)制名字對應(yīng)的字節(jié)數(shù)組
        byte[] data = this.loadClassData(className);
        System.out.println("findClass invoked:" + className);
        System.out.println("class loader name: " + classLoaderName);

        //defineClass(類名,字節(jié)數(shù)據(jù),起,末) 創(chuàng)建類實例
        return this.defineClass(className, data, 0, data.length);
    }

    //獲取文件字節(jié)數(shù)據(jù)
    private byte[] loadClassData(String className) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;

        className = className.replace(".", "\\");
        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\\");

        //改方法會調(diào)用我們重寫之后的findClass方法
        Class<?> clasz = test16.loadClass("com.example.demo.com.jvm.Test1");
        System.out.println("class: " + clasz.hashCode());

        Object o = clasz.newInstance();//對象內(nèi)存地址有哈希值
        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();//對象內(nèi)存地址有哈希值
        System.out.println(o2);
    }
}
/*
當(dāng)classPath下有對應(yīng)的加載的.class時 第二次交給父類加載器發(fā)現(xiàn)已經(jīng)加載所以字節(jié)拿過來用 所以此時獲取的Class類時一致的
class: 515132998
com.example.demo.com.jvm.Test1@6504e3b2
class: 515132998
com.example.demo.com.jvm.Test1@515f550a

當(dāng)classPath下沒有對應(yīng)的加載的.class 制定了對應(yīng)的路徑 此時類獲取幾次就會加載幾次 涉及到了命名空間的問題
findClass invoked:com.example.demo.com.jvm.Test1
class loader name: test16
class: 1365202186
com.example.demo.com.jvm.Test1@626b2d4a
--------------兩個不同的命名空間------------------
findClass invoked:com.example.demo.com.jvm.Test1
class loader name: test17
class: 932583850
com.example.demo.com.jvm.Test1@cac736f

 */

總結(jié):同一個命名空間不會出現(xiàn)兩個完全相同的類,不同的命名空間會出現(xiàn)兩個完全相同的類,父加載器加載的類不可以看到子類加載器加載的類,但是子類加載器加載的類可以看到父類加載器加載的類。

解釋:

命名空間

上例繼續(xù)改造://將loader1作為loader2的父類加載器。

public class Test16 extends ClassLoader {

    private String classLoaderName;

    //路徑
    private String path;

    private final String fileExtension = ".class";

    public Test16(String classLoaderName) {
        // this(checkCreateClassLoader(), getSystemClassLoader());
        // ClassLoader中當(dāng)創(chuàng)建新的類加載器返回的的是系統(tǒng)類加載器, 所以當(dāng)創(chuàng)建新的類加載器 默認(rèn)父加載器為系統(tǒng)類加載器
        super();//可加可不加
        this.classLoaderName = classLoaderName;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public Test16(ClassLoader parent, String classLoaderName) {
        // this(checkCreateClassLoader(), parent);
        //ClassLoader中當(dāng)創(chuàng)建新的類加載器自定義父加載器 如 :
        //a繼承b b繼承ClassLoader  此時a可以拿這個構(gòu)造方法將b作為自己的雙親 不一定都交給系統(tǒng)類加載器
        super(parent);
        this.classLoaderName = classLoaderName;
    }
    /**
     * 查找指定二進(jìn)制名字的class 這個方法應(yīng)該被子類加載器實現(xiàn)重新,再檢查完對應(yīng)父加載器之后該方法會被loaderClass()方法調(diào)用 ,
     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫
     * 如:
     * java.lang.String
     *
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        //對應(yīng)二進(jìn)制名字對應(yīng)的字節(jié)數(shù)組
        byte[] data = this.loadClassData(className);
        System.out.println("findClass invoked:" + className);
        System.out.println("class loader name: " + classLoaderName);

        //defineClass(類名,字節(jié)數(shù)據(jù),起,末) 創(chuàng)建類實例
        return this.defineClass(className, data, 0, data.length);
    }

    //獲取文件字節(jié)數(shù)據(jù)
    private byte[] loadClassData(String className) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;

        className = className.replace(".", "\\");
        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\\");

        //改方法會調(diào)用我們重寫之后的findClass方法
        Class<?> clasz = loader1.loadClass("com.example.demo.com.jvm.Test1");
        System.out.println("class: " + clasz.hashCode());

        Object o = clasz.newInstance();//對象內(nèi)存地址有哈希值
        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();//對象內(nèi)存地址有哈希值
        System.out.println(o2);
    }
}
-------------------------------------------
當(dāng)classPath下沒有對應(yīng)的加載的.class時
Test16 loader2 = new Test16(loader1,"loader2");//將loader1作為loader2的父類加載器

findClass invoked:com.example.demo.com.jvm.Test1
class loader name: loader1
class: 1365202186
com.example.demo.com.jvm.Test1@626b2d4a  //此時父加載器loader1已經(jīng)加載完畢 loader2直接拿來使用

class: 1365202186
com.example.demo.com.jvm.Test1@5e91993f

通過上例繼續(xù)改造: 新增一個類加載器 父類設(shè)置為loader2。

public class Test16 extends ClassLoader {

    private String classLoaderName;

    //路徑
    private String path;

    private final String fileExtension = ".class";

    public Test16(String classLoaderName) {
        // this(checkCreateClassLoader(), getSystemClassLoader());
        // ClassLoader中當(dāng)創(chuàng)建新的類加載器返回的的是系統(tǒng)類加載器, 所以當(dāng)創(chuàng)建新的類加載器 默認(rèn)父加載器為系統(tǒng)類加載器
        super();//可加可不加
        this.classLoaderName = classLoaderName;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public Test16(ClassLoader parent, String classLoaderName) {
        // this(checkCreateClassLoader(), parent);
        //ClassLoader中當(dāng)創(chuàng)建新的類加載器自定義父加載器 如 :
        //a繼承b b繼承ClassLoader  此時a可以拿這個構(gòu)造方法將b作為自己的雙親 不一定都交給系統(tǒng)類加載器
        super(parent);
        this.classLoaderName = classLoaderName;
    }


    /**
     * 查找指定二進(jìn)制名字的class 這個方法應(yīng)該被子類加載器實現(xiàn)重新,再檢查完對應(yīng)父加載器之后該方法會被loaderClass()方法調(diào)用 ,
     * 在父類中  throw new ClassNotFoundException(name); 只是拋出來一個異常必須重寫
     * 如:
     * java.lang.String
     *
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        //對應(yīng)二進(jìn)制名字對應(yīng)的字節(jié)數(shù)組
        byte[] data = this.loadClassData(className);
        System.out.println("findClass invoked:" + className);
        System.out.println("class loader name: " + classLoaderName);

        //defineClass(類名,字節(jié)數(shù)據(jù),起,末) 創(chuàng)建類實例
        return this.defineClass(className, data, 0, data.length);
    }

    //獲取文件字節(jié)數(shù)據(jù)
    private byte[] loadClassData(String className) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;

        className = className.replace(".", "\\");
        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\\");

        //改方法會調(diào)用我們重寫之后的findClass方法
        Class<?> clasz = loader1.loadClass("com.example.demo.com.jvm.Test1");
        System.out.println("class: " + clasz.hashCode());

        Object o = clasz.newInstance();//對象內(nèi)存地址有哈希值
        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();//對象內(nèi)存地址有哈希值
        System.out.println(o2);

        System.out.println();

        Test16 loader3 = new Test16(loader2,"loader3");
        loader3.setPath("E:\\cx\\");

        //改方法會調(diào)用我們重寫之后的findClass方法
        Class<?> clasz3 = loader3.loadClass("com.example.demo.com.jvm.Test1");
        System.out.println("class: " + clasz3.hashCode());

        Object o3 = clasz3.newInstance();//對象內(nèi)存地址有哈希值
        System.out.println(o3);
    }
}

命名空間一致。

命名空間一致
findClass invoked:com.example.demo.com.jvm.Test1
class loader name: loader1
class: 1365202186
com.example.demo.com.jvm.Test1@626b2d4a loader1 先去加類加載

class: 1365202186
com.example.demo.com.jvm.Test1@5e91993f loader2 交給父類父類交給appClassLoader加載發(fā)現(xiàn)已經(jīng)加載直接拿來用

class: 1365202186
com.example.demo.com.jvm.Test1@1c4af82c loader3 同上

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2024-12-04 09:01:55

引導(dǎo)類加載器C++

2022-08-08 08:17:43

類隔離加載器自定義類

2021-07-05 06:51:43

Java機(jī)制類加載器

2020-10-26 11:20:04

jvm類加載Java

2023-10-19 09:14:34

Java開發(fā)

2024-03-12 07:44:53

JVM雙親委托機(jī)制類加載器

2023-08-02 08:38:27

JVM加載機(jī)制

2023-10-31 16:00:51

類加載機(jī)制Java

2024-03-08 08:26:25

類的加載Class文件Java

2024-12-02 09:01:23

Java虛擬機(jī)內(nèi)存

2021-01-29 06:03:29

JDK15JVM類加載器

2012-02-09 10:31:17

Java

2023-10-18 18:23:58

2011-12-16 14:23:51

Java

2017-09-20 08:07:32

java加載機(jī)制

2017-03-08 10:30:43

JVMJava加載機(jī)制

2021-04-29 11:18:14

JVM加載機(jī)制

2012-03-13 14:41:41

JavaJVM

2024-08-09 11:50:00

2025-06-26 03:33:00

點贊
收藏

51CTO技術(shù)棧公眾號

亚洲精品高清在线观看| 国产一区二区在线观看视频| 国产亚洲精品va在线观看| 亚洲欧美久久久久| 欧美卡一卡二| 久久久精品人体av艺术| 91免费欧美精品| 国产成人在线免费视频 | 国产一区国产精品| 在线观看国产黄| 亚洲小说欧美另类婷婷| 中文字幕精品久久久久| 免费看91视频| 日本h片久久| 天天综合色天天综合| 在线看成人av电影| 水莓100在线视频| 国产精品影视网| 国产成人精品在线| 国产精品suv一区二区69| 日韩高清欧美| 精品视频在线播放免| 国产毛片久久久久久| 欧美大片免费| 日韩乱码在线视频| 91网站在线观看免费| 国产在线视频资源| 不卡一二三区首页| 亚洲xxxxx电影| 久久久久久久亚洲| 亚洲免费激情| 欧美日本国产在线| 无码人妻精品中文字幕 | 欧美一区二区三区色| 91香蕉视频污版| 欧美激情网站| 亚洲成人激情自拍| 六月婷婷激情综合| 91亚洲天堂| 国产精品久久久久桃色tv| 蜜桃久久精品乱码一区二区 | 日韩欧美三区| 色激情天天射综合网| 久久亚洲中文字幕无码| 欧美xxxxhdvideosex| 最近中文字幕一区二区三区| 一本一道久久a久久精品综合| 激情福利在线| 久久综合中文字幕| 久久偷窥视频| 日韩精品视频无播放器在线看 | 毛片大全在线观看| 亚洲女人小视频在线观看| 一区二区三区四区在线视频| 99re在线视频| 国产精品免费网站在线观看| 亚洲一区二区三区涩| 亚洲欧美视频一区二区| 中文字幕国产一区二区| 亚洲精品在线免费| 在线国产情侣| 国产精品视频一二| 正在播放一区| 日本动漫同人动漫在线观看| 一区二区三区四区亚洲| 日韩一级性生活片| 日本黄色免费在线| 日本丶国产丶欧美色综合| 99草草国产熟女视频在线| 88xx成人网| 91麻豆精品国产综合久久久久久| 国产成人强伦免费视频网站| av日韩在线播放| 亚洲国产日韩一区| 男女做爰猛烈刺激| 婷婷亚洲五月| 欧美激情乱人伦一区| 日韩免费观看一区二区| 水野朝阳av一区二区三区| 国产精品私拍pans大尺度在线| 国产一区二区自拍视频| 成人免费毛片高清视频| 免费精品视频一区二区三区| 日韩美女网站| 亚洲伊人伊色伊影伊综合网| 超碰网在线观看| 久久不卡日韩美女| 欧美大片免费久久精品三p| 美女又爽又黄免费| jiujiure精品视频播放| 欧美精品免费看| 久久亚洲天堂网| 精品亚洲国内自在自线福利| 国产嫩草一区二区三区在线观看| 黄色在线观看网| 亚洲欧美另类久久久精品2019| 热99这里只有精品| 欧美成人家庭影院| 亚洲经典中文字幕| 成人信息集中地| 国产日本精品| 92看片淫黄大片欧美看国产片| 天天综合网在线| 国产精品久久二区二区| 亚洲人成无码网站久久99热国产 | 5566中文字幕一区二区| 天天色棕合合合合合合合| 亚洲欧美综合在线精品| 97成人在线免费视频| 日韩专区视频| 亚洲精品资源美女情侣酒店| 国产在线一卡二卡| 手机精品视频在线观看| 国产99在线播放| 午夜视频成人| 福利一区视频在线观看| 麻豆传媒在线看| 日韩av片子| 91av中文字幕| 俄罗斯嫩小性bbwbbw| 国产精品久久久久久户外露出| av之家在线观看| 日日夜夜精品视频| 日韩天堂在线视频| 亚洲欧美日韩一区二区三区四区| 豆国产96在线|亚洲| 一级黄色录像免费看| 91p九色成人| 亚洲欧美精品中文字幕在线| 日本一级一片免费视频| 国产成人在线看| 在线免费一区| 欧美a视频在线| 亚洲视频自拍偷拍| 青青青国产在线| 成人不卡免费av| 亚洲一区二区三区av无码| 亚洲日日夜夜| 俺去亚洲欧洲欧美日韩| 在线免费观看一级片| 欧美国产一区二区| 黄色一级二级三级| 国内成人精品| 国产成人短视频| 青青草观看免费视频在线| 偷偷要91色婷婷| 国产激情第一页| 国产精品一级| 蜜桃av噜噜一区二区三区| 亚洲优女在线| 亚洲欧美另类在线观看| 免费看污视频的网站| 久久久国产一区二区三区四区小说| 黄色动漫网站入口| 亚洲老女人视频免费| 日韩av观看网址| 成人好色电影| 欧美色中文字幕| 欧美一区二区三区观看| 韩国视频一区二区| 992tv快乐视频| av成人资源| 午夜精品一区二区三区在线视频 | 国产aⅴ综合色| 福利在线一区二区| 久久精品色综合| 91av视频导航| 高清毛片在线看| 欧美日韩国产精选| 国产十六处破外女视频| 成人激情午夜影院| 国产精品亚洲αv天堂无码| 色综合综合色| 91精品国产综合久久香蕉922| а√天堂资源地址在线下载| 精品1区2区在线观看| 久久国产视频播放| 亚洲国产精品99久久久久久久久| 日本黄色的视频| 欧美午夜一区| 噜噜噜噜噜久久久久久91| 成人在线免费av| 欧美美最猛性xxxxxx| 天堂成人在线观看| 91黄视频在线| 日韩精品一区二区亚洲av性色 | 国产精品久久久久久久免费大片| 97人人在线视频| 自拍偷拍亚洲在线| 狠狠人妻久久久久久综合麻豆 | 国产绿帽一区二区三区| 亚洲丰满少妇videoshd| 精品成人无码一区二区三区| 国产成人免费av在线| 久久美女福利视频| 91精品国产91久久久久久密臀| 国产精品二区三区四区| 日韩毛片一区| 国产+人+亚洲| 永久免费av在线| 亚洲国产精品电影| 国产原创中文av| 色综合久久中文字幕综合网| 久热这里有精品| 26uuu精品一区二区在线观看| 一区二区三区四区毛片| 亚洲欧美卡通另类91av| 在线观看成人免费| 狠狠综合久久av一区二区蜜桃| 91香蕉视频在线下载| 国产精品无码久久久久| 91精品国产91久久久久久吃药| 黄av在线播放| 伊人久久综合97精品| 日本视频在线观看一区二区三区| 欧美一区二区三区性视频| 无码一区二区三区| 午夜精品一区在线观看| 婷婷色中文字幕| 国产精品传媒入口麻豆| 国产人妻一区二区| 成人国产精品视频| 欧洲成人午夜精品无码区久久| 久久成人精品无人区| 日韩有码免费视频| 欧美日本中文| 一区二区av| 色999国产精品| 日产精品高清视频免费| 亚洲国产精品嫩草影院久久av| 国产精品二区在线| 97se亚洲| 成人免费在线一区二区三区| 国产精品视频一区二区三区综合 | 2020国产精品极品色在线观看| 国产精品高精视频免费| 成人欧美大片| 欧美在线视频免费播放| 乱馆动漫1~6集在线观看| 九九久久综合网站| 久久av色综合| 欧美放荡办公室videos4k| 怡红院红怡院欧美aⅴ怡春院| 美女av一区二区三区| 成人直播在线| 欧美精品少妇videofree| 97影院秋霞午夜在线观看| 欧美巨大黑人极品精男| 青青草原国产在线| 欧美高清不卡在线| 毛片大全在线观看| 97久久超碰福利国产精品…| av女在线播放| 日本在线观看天堂男亚洲| 精品国产第一福利网站| 国产精品精品视频| 青草综合视频| 51国产成人精品午夜福中文下载| 欧美另类中文字幕| 国产精品久久久一区二区三区| 动漫av一区| 欧美不卡在线一区二区三区| 精品久久网站| 四虎影院一区二区| 国产字幕视频一区二区| 91专区在线观看| 久久在线精品| 日韩av卡一卡二| 成人一区二区三区视频| 黄色国产在线观看| 国产精品视频在线看| 青青操国产视频| 亚洲国产一区在线观看| 久久夜色精品国产噜噜亚洲av| 欧美在线免费观看亚洲| 国产精品热久久| 亚洲成人激情在线| 你懂的在线看| 久久精品视频免费播放| 男人天堂亚洲| 国产成人精品电影| 精品中文在线| 精品免费国产| 99热国内精品永久免费观看| 久草视频国产在线| 日韩电影在线免费看| 麻豆精品国产传媒| 久久久久久一二三区| 69xx绿帽三人行| 一本到不卡免费一区二区| 国产乱叫456在线| 亚洲精品国精品久久99热一 | 欧美精品videos性欧美| 伊人久久国产| 亚洲xxxxx电影| 国产亚洲电影| 青青草视频在线视频| 日韩成人dvd| 在线观看亚洲免费视频| 亚洲国产精品成人综合色在线婷婷| 亚洲国产成人精品综合99| 在线视频欧美精品| 欧美视频在线观看一区二区三区| 中文字幕在线观看亚洲| 川上优av中文字幕一区二区| 国产欧美中文字幕| 最新亚洲精品| 免费不卡av在线| 国产美女视频91| 日本欧美一区二区三区不卡视频| 亚洲国产精品嫩草影院| 国产精品羞羞答答在线| 亚洲男人第一av网站| xxxx在线视频| 91久久精品www人人做人人爽 | 91亚洲永久精品| 欧美丰满熟妇bbbbbb| 欧洲国内综合视频| 丝袜视频国产在线播放| 欧美国产第二页| 国产精品亚洲综合在线观看| 四虎影院一区二区三区| 亚洲欧美高清| 中文字幕av观看| 亚洲国产成人av好男人在线观看| 国产精品呻吟久久| 最近2019年好看中文字幕视频| 五月天国产在线| 精品免费视频123区| 影音国产精品| 永久免费未满蜜桃| 亚洲电影第三页| 国精品人妻无码一区二区三区喝尿 | 国产乱理伦片a级在线观看| 91精品国产91久久久久久| 国产在线播放精品| www.夜夜爱| 国产成人精品免费在线| 成年人av电影| 日韩午夜中文字幕| 亚洲羞羞网站| 亚洲综合在线播放| 综合激情婷婷| 永久看看免费大片| 一区二区三区中文字幕电影| 99热这里是精品| 久久6精品影院| 一区二区日韩| 黄页网站大全在线观看| av中文字幕在线不卡| 中文字幕亚洲精品在线| 精品视频一区在线视频| 日本美女一区| 视频一区二区在线观看| 麻豆国产欧美一区二区三区| 亚洲女人毛茸茸高潮| 欧美一卡在线观看| 激情av在线| 久久国产精品一区二区三区四区| 午夜一级在线看亚洲| 夫妇交换中文字幕| 91精品国产全国免费观看 | 欧美伊久线香蕉线新在线| 一呦二呦三呦国产精品| 久久久国产欧美| 亚洲欧洲日韩在线| 国产夫妻自拍av| 午夜精品一区二区三区在线播放| 婷婷成人在线| 久久精品影视大全| 亚洲精品日韩综合观看成人91| 亚洲第一黄色片| 奇米成人av国产一区二区三区| 欧美日韩激情| 亚洲一区二区三区三州| 亚洲午夜电影网| 黄色在线观看网| 91最新国产视频| av成人国产| 91ts人妖另类精品系列| 精品欧美一区二区三区精品久久 | 欧美日韩在线大尺度| 免费看黄色aaaaaa 片| 欧美精品一二三四| av影院在线免费观看| 日韩中文字幕一区二区| 国产成人免费视频精品含羞草妖精 | 中文字幕在线观看91| 一本久道中文字幕精品亚洲嫩| 韩国中文字幕在线| 欧洲成人一区二区| 国产成人在线看| 中文字幕人妻互换av久久| 欧美激情一区二区三区高清视频| 女厕嘘嘘一区二区在线播放 | 久久久久国产精品麻豆ai换脸 | 国产成人自拍网| 色av性av丰满av| 欧美黑人巨大精品一区二区| 精品一二三区|