精品欧美一区二区三区在线观看 _久久久久国色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ù)棧公眾號

国产日产欧美精品一区二区三区| 亚洲成av人电影| 色综合亚洲欧洲| 西游记1978| 亚洲奶汁xxxx哺乳期| 99精品国产一区二区青青牛奶| 亚洲免费av电影| 国产精品久久a| 国产乱码在线| 国产精品美女一区二区三区 | 国产一区二区av在线| 色爱综合区网| 精品制服美女丁香| 69国产精品成人在线播放| 国产精品69久久久久孕妇欧美| caoporn成人免费视频在线| 91久久人澡人人添人人爽欧美| 国产盗摄视频在线观看| 黄色在线视频观看网站| 国产91精品一区二区麻豆亚洲| 国产精品91一区| 日韩少妇裸体做爰视频| 久久中文视频| 亚洲欧洲av一区二区| 深夜视频在线观看| 日韩综合av| 91久久精品日日躁夜夜躁欧美| 日本欧美视频在线观看| а√天堂在线官网| 日本一区免费视频| 欧美激情论坛| 午夜视频在线播放| 国产999精品久久久久久 | 国产情侣一区二区三区| 色综合中文综合网| 日本少妇高潮喷水视频| 成人爽a毛片免费啪啪动漫| 国产精品素人一区二区| 日本高清一区| 麻豆影视在线| 久久综合给合久久狠狠狠97色69| 韩日午夜在线资源一区二区| 亚洲国产精品久久久久爰性色 | 视频一区二区三区不卡| 久久精子c满五个校花| 蜜桃999成人看片在线观看| 欧美 中文字幕| 成人久久视频在线观看| 草莓视频一区| 日韩一级在线播放| 不卡av在线网| 亚洲免费视频成人| 国产精品久久久久久久久久直播 | 亚洲女人天堂av| 亚洲自拍偷拍一区二区| 日韩av三区| 亚洲免费小视频| 亚洲av成人无码久久精品| 国产成人精品一区二区免费看京| 亚洲欧洲国产精品| 日本爱爱爱视频| 色中色综合网| 久久艳片www.17c.com | 日本二三区不卡| 毛葺葺老太做受视频| 91另类视频| 欧美剧在线免费观看网站 | 北条麻妃一区二区三区在线| 日韩一级高清毛片| 精品一区二区三区四区五区六区| 高清欧美性猛交xxxx黑人猛| 亚洲激情小视频| 日本少妇高潮喷水xxxxxxx| 国产不卡一二三区| 色av中文字幕一区| 欧美精品色哟哟| 国产精品入口| 国产裸体写真av一区二区| 国产日韩免费视频| 白白色 亚洲乱淫| 欧美一区2区三区4区公司二百| av网页在线| 亚洲激情男女视频| 精品国产免费av| 成人深夜福利| 日韩你懂的在线播放| aa片在线观看视频在线播放| 操欧美老女人| 欧美激情久久久久久| 午夜国产福利在线观看| 国产成人在线免费视频| 欧美精品日韩| 日韩av电影院| 精品久久久免费视频| heyzo一本久久综合| 亚洲国产午夜伦理片大全在线观看网站| 日本a在线播放| 婷婷六月综合亚洲| 亚欧美在线观看| 国产一级成人av| 色偷偷88888欧美精品久久久| 久久精品国产亚洲AV无码麻豆| 日韩精品三区四区| 成人av影视在线| 在线免费观看黄色| 欧美日韩久久久久| 日本高清免费在线视频| 亚洲第一福利社区| 欧美美女操人视频| 中文字幕久久熟女蜜桃| 亚洲精品综合久久| 91视频在线看| 国产精品视频一二三四区| 欧美色网在线| 精品国产91亚洲一区二区三区婷婷| 国产精品扒开腿做爽爽| 欧美日韩成人| 成人日韩在线电影| 国产高清视频在线| 午夜亚洲国产au精品一区二区| 奇米影视四色在线| 日韩欧美四区| 欧美国产精品va在线观看| 中文字幕视频在线播放| 久久综合九色综合97婷婷女人| 蜜臀精品一区二区| 婷婷精品久久久久久久久久不卡| 亚洲欧美资源在线| 国产乱国产乱老熟| 9人人澡人人爽人人精品| 四虎4hu永久免费入口| 国产精品蜜月aⅴ在线| 亚洲欧美日韩爽爽影院| 日本一级一片免费视频| 日韩在线观看视频一区二区三区| 欧美国产日本韩| 97成人在线观看视频| 久久久免费毛片| 久久久久久久国产精品视频| 国产视频一区二区三区四区五区| 国产精品人成在线观看免费| 人人爽人人av| 激情五月综合网| 国产精品久久不能| www.这里只有精品| 日韩欧美一中文字暮专区| 日韩欧美视频在线| 五月天丁香激情| 国产精品1区2区3区在线观看| 伊人久久青草| av在线精品| 九九久久国产精品| www.黄色小说.com| 一区二区欧美国产| 国产人妻精品午夜福利免费| 欧美精品日本| 国产一区高清视频| 97se综合| 色999日韩欧美国产| 国产精品高潮呻吟久久久| 国产精品成人一区二区三区夜夜夜 | 中国特级黄色片| 1024日韩| 秋霞毛片久久久久久久久| 日韩不卡在线| 久久精品久久久久电影| 亚洲奶汁xxxx哺乳期| 午夜欧美在线一二页| aa片在线观看视频在线播放| 久久国产一二区| 四虎一区二区| 九九九九九九精品任你躁| 欧美激情综合亚洲一二区| 午夜av免费在线观看| 91豆麻精品91久久久久久| 日韩在线不卡av| 国产91丝袜在线18| 欧美日韩亚洲一二三| 91久久电影| 激情久久av| 78精品国产综合久久香蕉| 久久这里有精品视频| 日日夜夜精品免费| 日韩成人久久| 日韩中文字幕免费视频| 丰满少妇一级片| 色综合激情五月| 久草综合在线视频| 久久综合成人精品亚洲另类欧美 | 欧美日韩中文在线视频| 97人人精品| 九九久久99| 色综合视频一区二区三区44| 久久噜噜噜精品国产亚洲综合| 免费人成黄页在线观看忧物| 在线播放日韩导航| 久久国产精品免费看| 综合色天天鬼久久鬼色| aaaaa级少妇高潮大片免费看| 国产一区二区女| 国产免费成人在线| 欧美精品日韩| 亚洲欧洲在线一区| 欧美日韩一区二区三区四区不卡| 国产欧美亚洲精品| 国产直播在线| 久久伊人精品一区二区三区| 日本午夜在线视频| 日韩欧美亚洲另类制服综合在线| 高潮无码精品色欲av午夜福利| 亚洲午夜av在线| 911国产在线| 国产丝袜欧美中文另类| 黄色av网址在线观看| 国产一区二区在线观看免费 | 日韩成人影音| 久久久久久久国产| av片在线观看网站| 日韩在线视频国产| 成人jjav| 亚洲老板91色精品久久| 欧美一级淫片aaaaaa| 6080国产精品一区二区| 91porny九色| 精品久久久国产| 久久精品视频日本| 亚洲精品成人天堂一二三| 午夜激情福利电影| 国产精品网曝门| 日韩视频在线观看免费视频| 91蜜桃婷婷狠狠久久综合9色| 日本成人在线免费| 国产成人亚洲精品青草天美| 99中文字幕在线| av免费在线免费| 亚洲国产精品久久人人爱蜜臀| 又色又爽的视频| 中文字幕免费不卡| 国产一区二区三区四区五区六区 | 欧美精品久久99久久在免费线| 波多野结衣一区二区三区四区| 狠狠躁夜夜躁久久躁别揉| 国产午夜福利精品| 亚洲成av人综合在线观看| 久久精品国产亚洲av高清色欲| 亚洲午夜国产一区99re久久| 国产在线拍揄自揄拍| 亚洲高清在线视频| 国产一级淫片免费| 五月天中文字幕一区二区| 日本一级淫片免费放| 亚洲 欧美综合在线网络| 日韩男人的天堂| 欧美日韩免费看| 无码人妻精品一区二| 欧美亚洲国产一区二区三区va| 亚洲大尺度在线观看| 欧美怡红院视频| 一起草av在线| 日韩一区二区三区观看| 亚洲第一页综合| 亚洲精品久久久久国产| 欧洲伦理片一区 二区 三区| 亚洲社区在线观看| 欧美一区二区三区| 久久国产精品免费视频 | 亚洲地区一二三色| 亚洲免费在线观看av| 日本高清不卡一区| 国产老妇伦国产熟女老妇视频| 日韩一区二区三区观看| 污污的视频网站在线观看| 亚洲欧美日韩天堂一区二区| 欧美r级在线| 国内久久久精品| 三上悠亚一区二区| 成人免费观看a| 高潮按摩久久久久久av免费| 日韩影院一区| 欧美日韩一区自拍| 日本黄色三级大片| 精品系列免费在线观看| 波多野结衣加勒比| 欧美国产精品一区二区三区| 91福利国产在线观看菠萝蜜| 欧美色综合影院| 国产强被迫伦姧在线观看无码| 日韩午夜在线影院| 色哟哟在线观看| 丝袜情趣国产精品| 成人在线免费观看视频网站| 欧美岛国激情| 热99这里只有精品| 麻豆专区一区二区三区四区五区| 18禁一区二区三区| 国产喷白浆一区二区三区| 国产黄色片在线免费观看| 岛国av在线不卡| 国产特级aaaaaa大片| 亚洲乱码一区av黑人高潮| 老司机在线永久免费观看| 97国产成人精品视频| 欧美亚洲黄色| 久久精品人成| 在线一区电影| 冲田杏梨av在线| av在线综合网| 国产一区二区视频在线观看免费| 福利二区91精品bt7086| 精品人妻一区二区三区浪潮在线| 亚洲日本欧美日韩高观看| 肉肉视频在线观看| 国产欧美 在线欧美| 日日天天久久| 久久这里只有精品18| 精品无人区卡一卡二卡三乱码免费卡| 韩国无码一区二区三区精品| 亚洲男同性恋视频| 91免费视频播放| 亚洲最新av在线| 成人免费无遮挡| 精品国产乱码一区二区三区四区| 午夜久久影院| 精品综合久久久久| 国产日韩欧美精品综合| 五月天综合激情网| 精品国产乱码久久久久久影片| 二区三区在线观看| 成人精品aaaa网站| 97人人精品| 一道本在线免费视频| 欧美高清在线一区二区| 无码人妻丰满熟妇区bbbbxxxx | 真实乱偷全部视频| 成人免费小视频| 中文字幕人妻丝袜乱一区三区| 亚洲欧美日韩一区二区三区在线| 国产精品yjizz视频网| 国产精品国产亚洲精品看不卡15 | 久久91超碰青草是什么| 精品视频在线播放一区二区三区 | 欧美精品99久久久**| 视频一区二区三区不卡| 国产日韩欧美电影在线观看| 欧美疯狂party性派对| 国产一伦一伦一伦| 国产精品久久久久久久久免费相片| 久久久久亚洲视频| 少妇精69xxtheporn| 国产精品xxx| 中文字幕日韩一区二区三区不卡| 男女啪啪999亚洲精品| xvideos国产精品| 看片一区二区| 中文字幕制服丝袜在线| 精品写真视频在线观看| 中文字幕在线有码| 欧美变态tickling挠脚心| gogo久久| 欧美日韩一区在线视频| 日本在线不卡视频一二三区| 国产wwwwxxxx| 欧美一级理论性理论a| 女子免费在线观看视频www| 国精产品一区二区| 久久久777| 小向美奈子av| 精品国产一区二区精华| 老司机成人影院| 亚洲午夜精品久久久中文影院av| 极品少妇一区二区| 黄页网站免费观看| 精品无人国产偷自产在线| 成人黄色免费网站| 影音先锋成人资源网站| av中文字幕在线不卡| 波多野结衣理论片| 欧美成人精品xxx| 欧美色图婷婷| 日本77777| 精品欧美国产一区二区三区| 国产九色在线| 444亚洲人体| 久久久久久自在自线| 一级片一级片一级片| 日韩黄色av网站| 国产精品成人国产| 久久久久久久久久久99| 国产精品国产三级国产专播品爱网| 99精品视频免费看| 全亚洲最色的网站在线观看| 91精品天堂福利在线观看| 老熟妇精品一区二区三区| 欧美性淫爽ww久久久久无| 日本三级在线观看网站| 日韩欧美视频一区二区| 丁香另类激情小说| 伊人精品一区二区三区| 97精品国产97久久久久久春色| 水蜜桃精品av一区二区|