最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JVM雙親委派模型知識詳細總結(jié)

 更新時間:2021年05月25日 10:30:27   作者:外星喵  
今天帶各位小伙伴學習Java虛擬機的相關(guān)知識,文中對JVM雙親委派模型作了非常詳細的介紹,對正在學習java的小伙伴們有很好的幫助,需要的朋友可以參考下

一、簡介

除了頂層的啟動類加載器(Bootstrap ClassLoader)外,其余的類加載器都應當有自己的上層加載器,如果一個類加載器收到了類加載請求,它并不會自己先去加載,而是把這個請求委托給上層的加載器,如果上層類加載器還存在其上層類加載器,則進一步向上委托,依次遞歸,直到請求最終到達頂層的啟動類加載器,從頂層類加載器開始,如果類加載器根據(jù)類的全限定名查詢到已經(jīng)加載過這個類,就成功返回加載過的此類信息,倘若加載器未加載過此類,則原路返回給下層加載器繼續(xù)重復此過程,直到最先加載此類的加載器所有上層加載器都未加載過此類后,此類加載器才會嘗試自己去加載,這便是雙親委派模式。

在這里插入圖片描述

舉個栗子:

假如你是某個企業(yè)員工,你寫了一份方案希望得到執(zhí)行,首先你得拿給你的經(jīng)理去審批吧,經(jīng)理說這個事情他做不了主,于是經(jīng)理就拿給總經(jīng)理看,總經(jīng)理也做不了主,就給董事長看,然后董事長看了看,也看不明白于是讓總經(jīng)理自己拿主意吧,總經(jīng)理仔細一看,這個方案之前公司已經(jīng)做過了,于是讓經(jīng)理去告訴那個員工不用做了,直接用那個做過的方案吧。

二、雙親委派的意義

采用雙親委派模式的是好處是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系,通過這種層級關(guān)系可以避免類的重復加載,當上層類加載器已經(jīng)加載了該類時,就沒有必要下層的ClassLoader再加載一次。

其次是考慮到安全因素,jdk中定義的類不會被隨意替換,假設(shè)我們在classpath路徑下自定義一個名為java.lang.Integer的類,通過雙親委托模式傳遞到啟動類加載器,而啟動類加載器通過索引發(fā)現(xiàn)同全限定名的類已被加載,并不會重新加載網(wǎng)絡傳遞的過來的java.lang.Integer,而直接返回已加載過的Integer.class,那么你所定義的類就不會被加載,這樣便可以防止核心API庫被隨意篡改。

如:

package java.lang;

public class Integer {

    public void print(){
        System.out.println("this is Integer.");
    }

    public static void main(String[] args) {
        new Integer().print();
    }
}

執(zhí)行main方法后輸出如下:

在這里插入圖片描述

三、JVM提供的類加載器

  • 啟動類加載器(BootstrapClassLoader):由jvm負責管理,主要負責加載核心的類庫(**rt.jar,也就是java.lang.***等),并構(gòu)造和啟動ExtClassLoader和APPClassLoader。
  • 擴展類加載器(ExtClassLoader):主要負責加載jre/lib/ext**目錄下的一些擴展的jar。
  • 應用類加載器(AppClassLoader):主要負責加載classpath下jar包和應用程序的主函數(shù)類。

如果當前類加載器加載的類引用了其它類,那么也會通過遞歸的方式先對其所有引用進行加載。

四、執(zhí)行類加載的五種方式

認識了這三種類加載器,接下來我們看看類加載的五種方式。

1.通過命令行使用java命令啟動應用時由JVM初始化加載含有main()方法的主類。

2.使用new關(guān)鍵字初始化一個對象時

3.通過類對應的Class對象的newInstance()方法

4.通過Class.forName(name)方法動態(tài)加載

5.通過ClassLoader.loadClass(name)方法動態(tài)加載

五、自定義類加載器

java系統(tǒng)為我們提供的三種類加載器,還給出了他們的層次關(guān)系圖,最下面就是自定義類加載器,那么我們?nèi)绾巫约憾x類加載器呢?這主要有兩種方式

(1)遵守雙親委派模型:繼承ClassLoader,重寫findClass方法。

通常我們推薦采用此方式自定義類加載器,最大程度上的遵守雙親委派模型。

findClass(name)查找具有指定全限定名的類。此方法用于遵循加載類的委托模型的類裝入器實現(xiàn)重寫,并將在檢查請求類的父類裝入器之后由loadClass方法調(diào)用。如果該類沒有被加載過且其class文件讀取不到則拋出ClassNotFoundException異常。

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

其實現(xiàn)例子:

package com.aliencat.javabase.classloader;

public class MyClassLoader extends ClassLoader{

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        switch (name){
            case "java.lang.Integer":return Double.class;
            case "com.aliencat.javabase.classloader.LoaderDemo" :  return loadClassFromDisk(name);
        }
        throw new ClassNotFoundException(name);
    }

    //從calsspath下加載類的字節(jié)碼文件
    public byte[] loadClassFromDisk(String name) {
        String classPathRoot = Thread.currentThread().getContextClassLoader().getResource("").getPath();
        classPathRoot = classPathRoot.substring(1);
        String filePath = classPathRoot + name.replace(".","/") + ".class";
        try(InputStream in = new FileInputStream(filePath) ;
            ByteOutputStream stream = new ByteOutputStream()) {
            byte[] buff = new byte[1024];
            for(int num = 0; (num=in.read(buff)) != -1;){
                stream.write(buff,0,num);
            }
            return stream.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public static void main(String[] args) throws ClassNotFoundException {
        Class clzz = new MyClassLoader().loadClass("com.aliencat.javabase.classloader.LoaderDemo");
        System.out.println(clzz);
        clzz = new MyClassLoader().loadClass("java.lang.Integer");
        System.out.println(clzz);
        clzz = new MyClassLoader().loadClass("java.lang.String");
        System.out.println(clzz);
        clzz = new MyClassLoader().loadClass("java.lang.xxxxx");
        System.out.println(clzz);
    }
}

輸出如下:

在這里插入圖片描述

(2)破壞雙親委派模型:繼承ClassLoader,重寫loadClass方法。

我們先來看下loadClass方法的源碼是怎樣的:

  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) {
                        //存在上層類加載器,則讓上層取執(zhí)行l(wèi)oadClass方法
                        c = parent.loadClass(name, false);
                    } else {
                        //讓BootstrapClass類加載器去查找該類
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // 上層類加載器拋出了ClassNotFoundException 則不進行處理

                }

                if (c == null) {

                    long t1 = System.nanoTime();
                    // 如果上層類加載器都沒有找到
                    // 那么這個類加載器自己去找
                    // 如果找到了,則將resolve置為true
                    c = findClass(name);

                    // 這是定義類加載器;記錄統(tǒng)計數(shù)據(jù)
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                //解析此類的信息
                resolveClass(c);
            }
            return c;
        }
    }

示例如下:

public class MyClassLoader extends ClassLoader {

    public static void main(String[] args) throws ClassNotFoundException {
        Class clzz = new MyClassLoader().loadClass("com.aliencat.javabase.classloader.ClassTest");
        System.out.println(clzz);
        clzz = new MyClassLoader().loadClass("java.lang.Double");
        System.out.println(clzz);
        clzz = new MyClassLoader().loadClass("java.lang.String");
        System.out.println(clzz);
    }

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        switch (name) {
            case "java.lang.Double":
                return Integer.class;
            case "java.lang.Object":  //如果去掉此項,則破壞雙親委任的情況下會報找不到Object的NoClassDefFoundError異常
                return Object.class;
            case "com.aliencat.javabase.classloader.ClassTest":
                byte[] bytes = loadClassFromDisk(name);
                if(bytes != null){
                    return defineClass(name,bytes,0,bytes.length);
                }else {
                    return null;
                }
        }
        throw new ClassNotFoundException(name);
    }

    //從calsspath下加載類的字節(jié)碼文件
    public byte[] loadClassFromDisk(String name) {
        String classPathRoot = Thread.currentThread().getContextClassLoader().getResource("").getPath();
        classPathRoot = classPathRoot.substring(1);
        String filePath = classPathRoot + name.replace(".","/") + ".class";
        try(InputStream in = new FileInputStream(filePath) ;
            ByteOutputStream stream = new ByteOutputStream()) {
            byte[] buff = new byte[1024];
            for(int num = 0; (num=in.read(buff)) != -1;){
                stream.write(buff,0,num);
            }
            return stream.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
        synchronized (LoaderDemo.class) {
            // 首先,在當前加載器加載過的類中檢查這個類有沒有被加載過
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                //沒加載過的話就去磁盤對應路徑下去找
                c = findClass(name);
            }
            return c;
        }
    }

}

class ClassTest{

}

輸出如下:

在這里插入圖片描述

所以破壞雙親委托的方法簡單來說就是通過繼承ClassLoader重寫loadClass方法,去掉其中委托給上級加載類的相關(guān)邏輯然后實現(xiàn)自定義的加載類的findClass邏輯。(另外你可以試試把ClassTest替換String的類名是什么效果哦,我就不演示了)

六、總結(jié)

Q:前面說了一堆雙親委托的好處,那么為什么要破壞雙親委托呢?

A:因為在某些情況下父類加載器需要委托子類加載器去加載class文件。受到加載范圍的限制,父類加載器無法加載到需要的文件,以JDBC接口為例,由于JDBC接口定義在jdk當中的,而其實現(xiàn)由各個數(shù)據(jù)庫的服務商來提供,比如mysql的就寫了MySQL-Connector,那么問題就來了,JDBC接口由啟動類加載器加載,而JDBC的實現(xiàn)是由服務商提供的,并不在啟動類加載器的加載目錄下,在不破壞雙親委派的情況下,啟動類加載器下層的類加載器要加載JDBC則必然會返回啟動類加載器中已經(jīng)加載過的接口,那么服務商提供的JDBC就不會被加載,所以需要自定義類加載器破壞雙親委派拿到服務商實現(xiàn)的JDBC接口,這里僅僅是舉了破壞雙親委派的其中一個情況,類似的還有tomcat。

到此這篇關(guān)于JVM雙親委派模型知識詳細總結(jié)的文章就介紹到這了,更多相關(guān)JVM雙親委派模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java多線程通過CompletableFuture組裝異步計算單元

    java多線程通過CompletableFuture組裝異步計算單元

    這篇文章主要為大家介紹了java多線程通過CompletableFuture組裝異步計算單元,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • Java 超詳細講解ThreadLocal類的使用

    Java 超詳細講解ThreadLocal類的使用

    寫SpringBoot項目的時候,經(jīng)常用到的一個保存用戶信息的類就是Threadlocal,我們今天就來詳細介紹一下這個類,感興趣的朋友來看看吧
    2022-04-04
  • Java數(shù)據(jù)結(jié)構(gòu)順序表用法詳解

    Java數(shù)據(jù)結(jié)構(gòu)順序表用法詳解

    順序表是計算機內(nèi)存中以數(shù)組的形式保存的線性表,線性表的順序存儲是指用一組地址連續(xù)的存儲單元依次存儲線性表中的各個元素、使得線性表中在邏輯結(jié)構(gòu)上相鄰的數(shù)據(jù)元素存儲在相鄰的物理存儲單元中,即通過數(shù)據(jù)元素物理存儲的相鄰關(guān)系來反映數(shù)據(jù)元素之間邏輯上的相鄰關(guān)系
    2021-10-10
  • Java 實現(xiàn)RSA非對稱加密算法

    Java 實現(xiàn)RSA非對稱加密算法

    RSA解決了對稱加密的一個不足,比如AES算法加密和解密時使用的是同一個秘鑰,因此這個秘鑰不能公開,因此對于需要公開秘鑰的場合,我們需要在加密和解密過程中使用不同的秘鑰,加密使用的公鑰可以公開,解密使用的私鑰要保密,這就是非對稱加密的好處?!?/div> 2021-06-06
  • Spring Boot + Vue 前后端分離開發(fā)之前端網(wǎng)絡請求封裝與配置

    Spring Boot + Vue 前后端分離開發(fā)之前端網(wǎng)絡請求封裝與配置

    這篇文章主要介紹了Spring Boot + Vue 前后端分離開發(fā)之前端網(wǎng)絡請求封裝與配置方法,本文給大家介紹的非常詳細,具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2019-05-05
  • SpringMVC接收前臺傳遞過來的值的實例

    SpringMVC接收前臺傳遞過來的值的實例

    下面小編就為大家分享一篇SpringMVC接收前臺傳遞過來的值的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-03-03
  • 如何擴展Spring Cache實現(xiàn)支持多級緩存

    如何擴展Spring Cache實現(xiàn)支持多級緩存

    這篇文章主要介紹了如何擴展Spring Cache實現(xiàn)支持多級緩存,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-11-11
  • 學習Java之如何正確地跳出循環(huán)結(jié)構(gòu)

    學習Java之如何正確地跳出循環(huán)結(jié)構(gòu)

    我們在利用循環(huán)執(zhí)行重復操作的過程中,存在著一個需求:如何中止,或者說提前結(jié)束一個循環(huán),所以就給大家講解一下,如何在java代碼中返回一個結(jié)果,如何結(jié)束和跳出一個循環(huán),需要的朋友可以參考下
    2023-05-05
  • springboot排除某些自動配置的操作方法

    springboot排除某些自動配置的操作方法

    Spring Boot 提供的自動配置非常強大,某些情況下,自動配置的功能可能不符合我們的需求,需要我們自定義配置,這個時候就需要排除/禁用Spring Boot 某些類的自動化配置了,本文給大家介紹springboot排除某些自動配置的方法,感興趣的朋友一起看看吧
    2023-08-08
  • Java結(jié)構(gòu)型設(shè)計模式之組合模式詳解

    Java結(jié)構(gòu)型設(shè)計模式之組合模式詳解

    組合模式,又叫部分整體模式,它創(chuàng)建了對象組的數(shù)據(jù)結(jié)構(gòu)組合模式使得用戶對單個對象和組合對象的訪問具有一致性。本文將通過示例為大家詳細介紹一下組合模式,需要的可以參考一下
    2022-09-09

最新評論

水城县| 咸丰县| 安岳县| 易门县| 安化县| 宁德市| 靖边县| 特克斯县| 南部县| 如皋市| 通山县| 香河县| 利川市| 徐闻县| 嘉峪关市| 广宁县| 郁南县| 沿河| 灵山县| 沧州市| 安泽县| 清水河县| 兴安县| 亳州市| 铁岭市| 涿鹿县| 新晃| 凉城县| 泾川县| 革吉县| 康马县| 老河口市| 香格里拉县| 江都市| 绥芬河市| 临猗县| 隆回县| 白沙| 富阳市| 昌平区| 龙岩市|