偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

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程序類都是這樣運(yùn)行)。
  • 每個類對象都包含對定義它的類加載器的引用(getClassLoader()獲取當(dāng)前類的類加載器)。
  • 數(shù)組類的類對象不是由類加載器創(chuàng)建的,而是由類加載器創(chuàng)建的根據(jù)Java運(yùn)行時的要求自動執(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;
    }

自此程序運(yùn)行結(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-10-31 16:00:51

類加載機(jī)制Java

2023-08-02 08:38:27

JVM加載機(jī)制

2024-03-08 08:26:25

類的加載Class文件Java

2021-01-29 06:03:29

JDK15JVM類加載器

2024-12-02 09:01:23

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

2012-02-09 10:31:17

Java

2023-10-18 18:23:58

2011-12-16 14:23:51

Java

2017-03-08 10:30:43

JVMJava加載機(jī)制

2021-04-29 11:18:14

JVM加載機(jī)制

2017-09-20 08:07:32

java加載機(jī)制

2024-08-09 11:50:00

2012-03-13 14:41:41

JavaJVM

2023-04-03 06:36:59

macOS內(nèi)存加載器
點贊
收藏

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