日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java类加载和双亲委派模型浅说

發布時間:2023/12/29 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java类加载和双亲委派模型浅说 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文目錄

  • 前言
  • 一、類加載器
    • 1.1 類加載機制的基本特征
    • 1.2 類加載的分類
    • 1.3 類加載器
      • A、啟動類加載器(引導類加載器,Bootstrap ClassLoader)
      • B、擴展類加載器(Extension ClassLoader)
      • C、應用程序類加載器(系統類加載器,AppClassLoaer)
      • D、自定義類加載器
    • 1.4 類加載器的必要性
  • 二、雙親委派模型
    • 2.1 概述
    • 2.2 雙親委派模型的實現
    • 2.3 雙親委派模型優劣勢
      • 2.3.1 優勢
      • 2.3.2 劣勢
    • 2.4 破壞雙親委派模型
      • 2.4.1 第一次破壞雙親委派模型
      • 2.4.2 第二次破壞雙親委派模型
      • 2.4.3 第三次破壞雙親委派模型
  • 三、自定義類加載器
    • 3.1 為啥要自定義類加載器?
    • 3.2 常見的場景
    • 3.3 實現方式
      • 3.3.1 實現方式
      • 3.3.2 兩種實現方式的對比
      • 3.3.3 說明

前言

之前被問到雙親委派模型和如果沒有此模型會導致什么問題,我去,一下子懵了,但下來細想,這不就是問的此模型的優勢嘛,我竟然沒有答出來,應該是我緊張了,自以為把此模型掌握的很扎實,實則不然,所以再來整理一下相關的知識點。

一、類加載器

說到雙親委派模型,不得不提一下類加載器,再說類加載器之前不得不說類加載的分類和類加載機制。

1.1 類加載機制的基本特征

類加載機制通常有三個基本特征:
A、雙親委派模型:首先說明并不是所有類加載都遵循這個模型。有的時候,啟動類加載器所加載的類型,是可能要加載用戶代碼的,比如JDK內部的SPI機制,用戶可以在標準API框架上提供自己的實現,JDK也需要提供些默認的參考實現。比如,java中JNDI,JDBC,文件系統等很多方面,都是利用的這種機制,這種情況就不會用雙親委派模型去加載,而是利用所謂的上下文加載器。
B、可見性:子類加載器可以訪問父類加載器加載的類型,凡是反過來是不允許的。不然,因為缺少必要的隔離,我們就沒有辦法利用類加載器去實現容器的邏輯。
C、單一性:由于父加載器的類型對于子加載器是可見的,所以父加載器中加載過的類型,就不會在子加載器中重復加載。但是注意,類加載器“鄰居”間,同一類型仍然可以被加載多次,因為互相并不可見。

1.2 類加載的分類

類的加載分為:顯式加載 VS 隱式加載
class文件的顯式加載與隱式加載的方式是指JVM加載class文件到內存的方式。
? 顯式加載指的是在代碼中通過調用ClassLoader加載class對象,如直接使用Class.forName("java.lang.String")或this.getClass().getClassLoader().loadClass()加載class對象。
? 隱式加載是通過虛擬機自動加載到內存中,而不是直接在代碼中調用ClassLoader的方法加載class對象,如在加載某個類的class文件時,該類的class文件中引用了另一個類的對象,此時額外引用的類將通過JVM自動加載到內存中。
在日常開發以上兩種方式一般會混合使用。

代碼示例一

package com.fanhf.javastudy.classloader;/** * @author fanhf * @Description 顯式和隱式加載 * @date 2021-02-07 14:22 */ public class UserTest{User user = new User();//隱式加載public static void main(String[] args){try{//顯式加載方式1Class clazz = Class.forName("com.fanhf.javastudy.classloader.User");//顯式加載方式2Class<?> aClass = ClassLoader.getSystemClassLoader().loadClass("com.fanhf.javastudy.classloader.User");}catch(ClassNotFoundException e){e.printStackTrace();}} } // User類 class User{static {System.out.println("我是User類的初始化");} }

1.3 類加載器

JVM支持兩種類型的類加載器,分別為引導類加載器(Bootstrap ClassLoader)和自定義類加載器(User-Defined ClassLoader)。
從概念上來講,自定義類加載器一般指的是程序中由開發人員自定義的一類類加載器,但是java虛擬機規范卻沒有這么定義,而是將所有派生于抽象類ClassLoader的類加載器都劃分為自定義類加載器。無論類加載器的類型如何劃分,在程序中我們最常見的類加載器結構如下情況:

#mermaid-svg-FUB8qFrwydmVq8ci {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FUB8qFrwydmVq8ci .error-icon{fill:#552222;}#mermaid-svg-FUB8qFrwydmVq8ci .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FUB8qFrwydmVq8ci .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-FUB8qFrwydmVq8ci .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FUB8qFrwydmVq8ci .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FUB8qFrwydmVq8ci .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FUB8qFrwydmVq8ci .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FUB8qFrwydmVq8ci .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FUB8qFrwydmVq8ci .marker.cross{stroke:#333333;}#mermaid-svg-FUB8qFrwydmVq8ci svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FUB8qFrwydmVq8ci g.classGroup text{fill:#9370DB;fill:#131300;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-FUB8qFrwydmVq8ci g.classGroup text .title{font-weight:bolder;}#mermaid-svg-FUB8qFrwydmVq8ci .nodeLabel,#mermaid-svg-FUB8qFrwydmVq8ci .edgeLabel{color:#131300;}#mermaid-svg-FUB8qFrwydmVq8ci .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-FUB8qFrwydmVq8ci .label text{fill:#131300;}#mermaid-svg-FUB8qFrwydmVq8ci .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-FUB8qFrwydmVq8ci .classTitle{font-weight:bolder;}#mermaid-svg-FUB8qFrwydmVq8ci .node rect,#mermaid-svg-FUB8qFrwydmVq8ci .node circle,#mermaid-svg-FUB8qFrwydmVq8ci .node ellipse,#mermaid-svg-FUB8qFrwydmVq8ci .node polygon,#mermaid-svg-FUB8qFrwydmVq8ci .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FUB8qFrwydmVq8ci .divider{stroke:#9370DB;stroke:1;}#mermaid-svg-FUB8qFrwydmVq8ci g.clickable{cursor:pointer;}#mermaid-svg-FUB8qFrwydmVq8ci g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-FUB8qFrwydmVq8ci g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-FUB8qFrwydmVq8ci .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-FUB8qFrwydmVq8ci .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-FUB8qFrwydmVq8ci .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-FUB8qFrwydmVq8ci .dashed-line{stroke-dasharray:3;}#mermaid-svg-FUB8qFrwydmVq8ci #compositionStart,#mermaid-svg-FUB8qFrwydmVq8ci .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-FUB8qFrwydmVq8ci #compositionEnd,#mermaid-svg-FUB8qFrwydmVq8ci .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-FUB8qFrwydmVq8ci #dependencyStart,#mermaid-svg-FUB8qFrwydmVq8ci .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-FUB8qFrwydmVq8ci #dependencyStart,#mermaid-svg-FUB8qFrwydmVq8ci .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-FUB8qFrwydmVq8ci #extensionStart,#mermaid-svg-FUB8qFrwydmVq8ci .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-FUB8qFrwydmVq8ci #extensionEnd,#mermaid-svg-FUB8qFrwydmVq8ci .extension{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-FUB8qFrwydmVq8ci #aggregationStart,#mermaid-svg-FUB8qFrwydmVq8ci .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-FUB8qFrwydmVq8ci #aggregationEnd,#mermaid-svg-FUB8qFrwydmVq8ci .aggregation{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-FUB8qFrwydmVq8ci .edgeTerminals{font-size:11px;}#mermaid-svg-FUB8qFrwydmVq8ci :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}啟動類加載器BootStrap ClassLoader擴展類加載器Extension ClassLoader應用程序類加載器Application ClassLoader用戶自定義類加載器1User ClassLoader用戶自定義類加載器2User ClassLoader

除了頂層的啟動類加載器外,其余的類加載器都應當有自己的“父類”加載器。
不同類加載器看似是繼承(Inheritance)關系,實際上是包含關系。在下層加載器中,包含著上層加載器的引用

A、啟動類加載器(引導類加載器,Bootstrap ClassLoader)

? 這個類加載使用C/C++語言實現的,嵌套在JVM內部 ? 它用來加載java的核心庫(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.path路徑下的內容)。用于提供JVM自身需要的類。 ? 并不繼承自java.lang.ClassLoader,沒有父加載器。 ? 出于安全考慮,Bootstrap啟動類加載器只加載包名為:java、javax、sun等開頭的類 ? 加載擴展類和應用程序類加載器,并指定為他們的父類加載器。

使用-XX:+TraceClassLoading參數得到。
在上面的代碼示例一中,加入-XX:+TraceClassLoading的啟動參數,可以看到

此處省略許多類…

此處省略許多類…

代碼示例一中,加載了504個類,包括以下包的類
java.lang、java.util、java.io、java.nio、java.net、java.security、
sun.misc、sun.reflect、sun.nio、sun.usagetracker、sun.launcher

B、擴展類加載器(Extension ClassLoader)

? java語言編寫,由sun.misc.Launcher$ExtClassLoader實現。 ? 繼承于ClassLoader類 ? 父類加載器為啟動類加載器

從java.ext.dirs系統屬性所指定的目錄中加載類庫,或從JDK的安裝目錄的jre/lib/ext子目錄下加載類庫。如果用戶創建的JAR放在此目錄下,也會自動由擴展類加載器加載。

類加載器繼承關系如下圖:

ClassLoader的孩子們:

Launcher類中的實現

C、應用程序類加載器(系統類加載器,AppClassLoaer)

? java語言編寫,由sun.misc.Launcher$AppClassLoader實現 ? 繼承于ClassLoader類 ? 父類加載器為擴展類加載器 ? 它負責加載環境變量classpath或系統屬性 java.class.path指定路徑下的類庫 ? 應用程序中的類加載器默認是系統類加載器 ? 他是用戶自定義類加載器的默認父加載器

通過ClassLoader的getSystemClassLoader() 方法可以獲取到該類加載器

D、自定義類加載器

自定義類加載器可以實現應用隔離,比如Tomcat,Spring等中間件和組件框架都在內部實現了自定義的加載器。自定義類加載器通過需要繼承自ClassLoader,此類中的loadClass方法中的邏輯就是雙親委派模型的實現,繼承ClassLoader后,在jdk1.2之后,不建議去覆蓋loadClass方法,而是在findClass方法中實現,findClass就是在loadClass中調用的,當loadClass方法中父類加載器加載失敗,就會調用自己寫的findClass方法來完成類的加載,這樣就可以保證自定義的類加載器也符合雙親委派模型。

通過看源碼得知,ClassLoader是一個抽象類,很多方法是空的沒有實現,比如findClass()、findResource()等。而URLClassLoader這個實現為這些方法提供了具體的實現。并新增了URLClassPath類協助取得Class字節碼流等功能。在編寫自定義類加載器時,如果沒有太過于復雜的需求,可以直接繼承URLClassLoader類,這樣就可以避免自己去編寫findClass()方法及其獲取字節碼流的方式,使自定義類加載器編寫更加簡潔。

1.4 類加載器的必要性

一般情況下,java開發人員并不需要在程序中顯式的使用類加載器,但是了解類加載器的加載機制卻很重要。從以下幾點說明:
? 避免在開發中遇到java.lang.ClassNotFoundException異常或java.lang.NoClassDefFoundError異常時手足無措。只有了解類加載器的加載機制才能夠在出現異常的時候快速的根據錯誤異常日志定位問題。
? 需要支持類的動態加載或需要對編譯后的字節碼文件進行加解密操作時,就需要和類加載器打交道了。
開發人員可以在程序中編寫自定義類加載器來重新定義類的加載規則,以便實現一些自定義的處理邏輯。

贈送面試題:Class.forName()與ClassLoader.loadClass()

? Class.forName():是一個Class類中在靜態方法,屬于顯式加載,最常用的是Class.forName(String className);
根據傳入的類的全限定名返回一個Class對象。該方法在將Class文件加載到內存的同時,會執行類的初始化。如:Class.forName(“com.fanhf.javastudy.classloader.ClassLoaderTest”);

? ClassLoader.loadClass():這是一個實例方法,屬于隱式加載,需要一個ClassLoader對象來調用此方法。該方法將Class文件加載到內存時,并不會執行類的初始化,直到這個類第一次使用時才進行初始化。該方法因為需要得到一個ClassLoader對象,所以可以根據需要指定使用哪個類加載器,如:ClassLoader cl=……;
cl.loadClass(“com.fanhf.javastudy.classloader.ClassLoaderTest”)

二、雙親委派模型

簡單說完了類加載器和類加載機制,步入正題說一下雙親委派模型

2.1 概述

類加載器用來把類加載到java虛擬機中。從JDK1.2開始,類的加載過程采取雙親委派機制,這種機制能更好的保證java平臺的安全。如果一個類加載器在接到加載類的請求時,它首先不會自己嘗試去加載這個類,而是把這個請求任務委托給父類加載器去完成,依次遞歸,如果父類加載器可以完成類加載器任務,就成功返回。只有父類加載器無法完成此加載任務時,才自己去加載。
如下圖:

然而,java虛擬機規范中并沒有明確要求類加載器的加載機制一定要使用雙親委派模型,只是建議使用這種方式。在tomcat中,缺省的類加載器接到一個類加載任務,會自行加載,加載失敗才會委托給它的超類進行加載,這也是Servelet規范推薦的一種做法。

2.2 雙親委派模型的實現

雙親委派機制在java.lang.ClassLoader.loadClass(String,boolean)接口中體現。
重點分析一下loadClass方法

/* * * <p> If the class was found using the above steps, and the * <tt>resolve</tt> flag is true, this method will then invoke the {@link * #resolveClass(Class)} method on the resulting <tt>Class</tt> object. * * 翻譯為:如果使用上述步驟找到了該類,并且resolve標志為true,則此方法將在生成的Class上調用 resolveClass(Class)方法對象。 */ protected Class<?>loadClass(String name,boolean resolve) throws ClassNotFoundException //resolve為true,加載class的同時進行解析操作。 {synchronized(getClassLoadingLock(name)){ //同步操作,保證只能加載一次//First,check if the class has already been loaded//調用{@link findLoadedClass(String)},以檢查是否已加載該類。//首先,在緩存中判斷是否已經加載同名的類Class<?> c = findLoadedClass(name);if(c == null){long t0 = System.nanoTime();try{//獲取當前類的父類加載器if(parent != null){//如果存在父類加載器,就調用父類的加載器進行類的加載c = parent.loadClass(name,false);}else{//parent是null,說明父類加載器是引導類加載器c = findBootstrapClassOrNull(name);}}catch(ClassNotFoundException e){//ClassNotFoundException thrown if class not found//from the non-null parent classloader}if(c == null){ //當前類的加載器的父類加載器未加載此類 or 當前類的加載器未加載此類//If still not found,then invoke findClassinorder//to find the class.long t1 = System.nanoTime();c = findClass(name);//this is the defining classloader;record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1-t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if(resolve){ //是否進行解析操作resolveClass(c);}return c;} }

實現邏輯:
1)先在當前類加載器緩存中查找有無目標類,如果不為空,則調用parent.loadClass(name,false)接口進行加載。
2)判斷當前加載器的父類加載器是否為空,如果不為空,則調用parent.loadClass(name,false)接口進行加載。
3)反之,如果當前加載器的父類加載器為空,則調用findBootstrapClassOrNull(name)接口,讓引導類加載器進行加載。
4)如果通過以上3條路徑都沒有加載成功,則調用findClass(name)進行加載。該接口最終調用java.lang.ClassLoader 接口的defineClass系列的native接口加載目標java類。

舉個🌰:
有一個java.lang.Object類需要加載,這個類屬于java中核心的不能再核心的類了,因此一定只能由啟動類加載器(Bootstrap ClassLoader)進行加載。jvm按照上面的4步進行加載,首先從應用類加載器(AppClassLoader)的緩存中查找該類,但此時并無此類,所以由應用類加載器的父類加載器即擴展類加載器進行加載,于是擴展類加載器繼續從第一步開始重復。由于擴展類加載器的緩存中也一定找不到該類,于是進入第二步,擴展類的父加載器時null,因此調用findClass方法進行加載,最終通過啟動類加載器進行加載。

2.3 雙親委派模型優劣勢

2.3.1 優勢

A、避免類的重復加載,確保一個類的全局唯一性
java類隨著它的類加載器一起舉杯了一種帶有優先級的層次關系,通過這種層級關閉可以避免類被重復加載,當父類已經加載了該類時,就沒有必要下一級加載器在加載一次了。
B、保護程序的安全,防止核心API被隨意篡改。

2.3.2 劣勢

檢查類是否加載的委托過程是單向的。
這個加載方式雖然從結構上說比較清晰,使各個ClassLoader的職責非常明確,但是同時帶來一個問題,即頂層的ClassLoader無法訪問底層的ClassLoader所加載的類。
通常情況下,啟動類加載器的類為系統核心類,包括一些重復的系統接口,而在應用類加載器中,為應用類,按照這種模式,應用類訪問啟動類自然是沒有問題的,但是啟動類訪問應用類就會出現問題。比如在啟動類提供了一個接口,該接口需要在應用類中得以實現,該接口還綁定了一個工廠方法,用于創建該接口的實力,而接口和工廠方法都在啟動類。

2.4 破壞雙親委派模型

雙親委派模型并不是一個具有強制性約束的模型,而是java設計者推薦給開發者們的類加載器實現方式。
在java的世界中大部分的類加載器都遵循這個模型,直到java模塊化出現為止,雙親委派模型主要出現過3次較大規模“被破壞”的情況。

2.4.1 第一次破壞雙親委派模型

第一次被破壞是發生在雙親委派模型出現之前的JDK1.2版本以前的古老的時代。
為了兼容1.2之前已有的類加載器的概念和抽象類java.lang.ClassLoader中可能已經被用戶自定義來加載器的代碼,在1.2之后在此類中增加了一個protected的findClass()方法,引導用戶編寫的類加載邏輯時盡可能去重新這個方法,而不是在loadClass中編寫代碼。
上面我們分析過loadClass方法,按照此方法的邏輯,如果父類加載失敗,會自動調用findClass方法來完成加載,這樣不僅保證用戶可以按照自己的需求去加載,還可以保證新實現的類加載器是符合雙親委派模型的。

2.4.2 第二次破壞雙親委派模型

第二次被破壞是由于這個模型自身的缺陷所導致的,在2.3.2節提到此模型的劣勢是頂層類加載器無法訪問底層所加載的類,換言之,頂層類加載器加載的都是比較基礎的類,當基礎的類想要訪問應用類加載的類的方法時,這條路似乎被堵死了,實際上這種問題被一個叫做線程上下文類加載器給解決了。
線程上下文類加載器(Thread Context ClassLoader)可以通過java.lang.Thread類的setContextClassLoader()方法進行設置,如果創建線程時還未設置,它將會從父線程中繼承一個,如果在應用程序的全局內部沒有設置過的話,這個類加載器就默認使用應用類加載器進行加載。
舉個🌰:JNDI(Java Name and Directory Interface)java命名和目錄接口,作用是為JAVA應用程序提供命名和目錄訪問服務的API,現在是java標準的服務,其代碼由啟動類加載器進行加載,這個接口需要調用其他廠商實現并部署在classPath下的服務提供者接口(Service Provider Interface,SPI)的代碼,那么就可以使用線程上下文類加載器去加載所需的SPI服務代碼,這就是典型的父類加載去請求子類加載器完成類加載的行為,這種行為實際上打通了雙親委派模型的層次結構來逆向使用類加載器,違背了雙親委派模型的一般性原則,java中涉及SPI的加載基本上都采用這種方式來完成。

2.4.3 第三次破壞雙親委派模型

第三次是由于用戶對程序動態性的追求而導致的,如代碼熱替換,模塊熱部署等。這里提一下IBM主導的OSGI實現模塊化熱部署,實現的關鍵是它自定義的類加載機制的實現,每一個程序模塊(OSGI中稱為Bundle)都有一個自己的類加載器,當需要更換一個Bundle時,就把Bundle連同類加載器一起換掉以實現代碼的熱替換,在OSGI環境下,類加載器不再是雙親委派模型推薦的樹狀結構,而是更為復雜的網狀結構。
(OSGI這玩意我接觸的也不多,至于類加載的實現方式我就不多說了,感興趣的小可愛可以自行學習。)

三、自定義類加載器

在1.3節中說到有四種類加載器,最后一種是自定義類加載器,那就再來說說這玩意!

3.1 為啥要自定義類加載器?

A、 隔離加載類
在某些框架內進行中間件與應用的模塊隔離,把類加載到不同的環境。比如,阿里內某容器框架通過自定義類加載器確保應用中依賴的jar包不會影響到中間件運行時使用的jar包。再比如:tomcat這類web應用服務器,內部自定義了好幾種類加載器,用于隔離同一個web應用服務器上的不同應用程序。
B、 修改類加載的方式
類的加載模型并非強制,除bootstrap外,其他的加載并非不一定要引入,或者根據實際情況在某個時間點進行按需進行動態加載。
C、擴展加載源
比如從數據庫、網絡、甚至是電視機機頂盒進行加載
D、 防止源碼泄漏
java代碼容易被編譯和篡改,可以進行編譯加密。那么類加載也需要自定義,還原加密的字節碼。

3.2 常見的場景

1)實現類似進程內隔離,類加載器實際上用作不同的命名空間,以提供類似容器,模塊化的效果。例如,兩個模塊依賴于某個類庫的不同版本,如果分別被不同的容器加載,就可以互不干擾。這個方面的集大成者是java EE和OSGI,JPMS等框架。
2)應用需要從不同的數據源獲取類定義信息,例如網絡數據源,而不是本地文件系統。或者是需要自己操縱字節碼動態修改或者生成類型。

3.3 實現方式

用戶通過定制自己的類加載器,這樣可以重新定義類的加載規則,以便實現一些自定義的處理邏輯。

3.3.1 實現方式

? java提供了抽象類,所有用戶自定義的類加載器都應該繼承ClassLoder類。 ? 在自定義ClassLoader的子類的時候,我們常見的會有2種做法。○ 方式一:重寫loadClass()方法○ 方式二:重寫findClass()方法--推薦

3.3.2 兩種實現方式的對比

這兩種方式本質差不多,畢竟 loadClass()也會調用findClass(),但是從邏輯上講我們最好不要直接修改loadClass()內部邏輯。建議的做法是只在findClass()里重寫自定義類的加載方法,根據參數指定類的名字,返回對應的Class對象的引用。
? loadClass()這個方法是實現雙親模型邏輯的地方,擅自修改這個方法會導致模型被破壞,容易造成問題,因此我們最好是在雙親委派模型框架內進行小范圍的改動,不破壞原有的穩定結構。同時,也避免了自己重寫loadClass()方法的過程中必須寫雙親委托的重復代碼,從代碼的復用性來看,不直接修改這個方法始終是比較好的選擇。
? 當編寫好自定義類加載器后,便可以在程序中調用loadClass()方法來實現類加載操作。

3.3.3 說明

? 其父類加載器是系統類加載器 ? JVM中的所有類加載都會使用java.lang.ClassLoader.loadClass(String)接口(自定義類加載器并重寫java.lang.ClassLoader.loadClass(String)接口的除外),連JDK的核心類庫也不能例外。

下面來看一下例子:

package com.fanhf.study.classLoader;import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException;public class MyClassLoader extends ClassLoader{private String byteCodePath;public MyClassLoader(String byteCodePath){this.byteCodePath = byteCodePath;}public MyClassLoader(ClassLoader parent, String byteCodePath) {super(parent);this.byteCodePath = byteCodePath;}@Overrideprotected Class<?> findClass(String className) {BufferedInputStream bis = null;ByteArrayOutputStream baos = null;try {//獲取字節碼文件的完整路徑String fileName = byteCodePath + className + ".class";//獲取一個輸入流bis = new BufferedInputStream(new FileInputStream(fileName));//獲取一個輸出流baos = new ByteArrayOutputStream();//具體讀入數據并寫出的過程int len;byte[] data = new byte[1024];while ((len = bis.read(data)) != -1) {baos.write(data, 0, len);}//獲取內存中完成的字節數組的數據byte[] byteCodes = baos.toByteArray();//調用defineClass講字節數組轉換為class實例Class clazz = defineClass(null, byteCodes, 0, byteCodes.length);return clazz;} catch (IOException e) {e.printStackTrace();} finally {try {if (bis != null) {bis.close();}} catch (IOException e) {e.printStackTrace();}try {if (baos != null) {baos.close();}} catch (IOException e) {e.printStackTrace();}}return null;} }

測試類

package com.fanhf.study.classLoader;public class MyClassLoaderTest{public static void main(String[] args) {MyClassLoader loader = new MyClassLoader("/Users/fanhuifang/IdeaProject/MyProjects/java-study/target/classes/com/fanhf/study/classLoader/");Class clazz = null;try {clazz = loader.loadClass("MyClassLoader");System.out.println("加載此類的類加載器為:" + clazz.getClassLoader().getClass().getName());System.out.println("加載當前Test類的類加載器的父類加載器為:" + clazz.getClassLoader().getParent().getClass().getName());} catch (ClassNotFoundException e) {e.printStackTrace();}} }

到此,本文就差不多告一段落了。

特此說明,本文中的內容出處來自于《深入了解java虛擬機第二版》以及尚硅谷的康師傅的jvm課程,感興趣的小伙伴也可以再去看看,定會受益匪淺。

-----------------你知道的越多,不知道的越多------------------

總結

以上是生活随笔為你收集整理的java类加载和双亲委派模型浅说的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 天天热天天干 | 国产尤物视频在线观看 | 精品国偷自产在线 | 国产女主播喷水视频在线观看 | 一级片视频免费 | 久久午夜无码鲁丝片午夜精品 | 一级全黄裸体免费视频 | 日本在线精品视频 | 欧美不卡一区二区三区 | 欧美精品一级片 | 亚洲午夜久久久久久久国产 | 四川丰满妇女毛片四川话 | 色婷婷av一区二区三区之红樱桃 | 国产黄色三级网站 | 奇米影视999 | 白嫩少妇激情无码 | 精品一区二区三区在线观看视频 | 女性裸体不遮胸图片 | 一区国产精品 | 99国产精品久久久久久久成人 | 9.1成人看片免费版 日韩经典在线 | 国产三级精品在线观看 | 91在线观| 新版红楼梦在线高清免费观看 | 中文字幕第23页 | 爱涩av| 亚洲欧美日本一区二区三区 | 特级免费毛片 | eeuss鲁丝片一区二区三区 | 亚洲成人av一区二区三区 | 欧美精品福利 | 成人人人人人欧美片做爰 | 2023国产精品| 亚洲自拍偷拍综合 | 亚洲精品1区2区 | 中文久久乱码一区二区 | 中文字幕一区二区三区乱码 | 欲求不满在线小早川怜子 | 黄黄的视频在线观看 | 台湾佬美性中文娱乐网 | 在线观看的黄色网址 | 亚洲a图| 亚洲一区免费视频 | 午夜在线你懂的 | 国产亚洲精品成人a | 成人国产一区二区 | 国产成人啪精品 | www.久久综合 | 日韩精品在线观看网站 | 久草老司机| 中文字幕第一页在线视频 | 欧美欧美欧美欧美 | 午夜久久久久 | 自拍偷拍亚洲一区 | 亚洲精品国产成人无码 | 88久久精品无码一区二区毛片 | 日韩精品h | 99精品久久99久久久久 | 俺来也俺也啪www色 欧洲一区二区视频 | 男人的天堂在线 | 在线观看免费视频一区 | 大桥未久中文字幕 | 在线视频网站 | 狠操av| 日韩视频在线观看免费视频 | 手机福利视频 | 在线观看午夜视频 | 91成人在线观看国产 | 涩涩网站在线观看 | 4438x在线观看 | 污污在线看 | 天堂中文视频 | 成人久久av | 国产一区二区三区在线视频观看 | xxxxhdvideos | 九九热精品视频在线观看 | 日韩经典第一页 | 国产麻豆剧果冻传媒白晶晶 | 视频二区在线 | 这里只有久久精品 | 99成人精品| 日日碰狠狠躁久久躁蜜桃 | 奇米影视9999 | 欧美黄色免费网站 | 欧美精品网站 | 日本三级吃奶头添泬无码苍井空 | 在线播放你懂得 | 欧美精品一区二区免费看 | 亚洲国产精品视频一区二区 | 亚洲经典一区二区三区四区 | 亚洲成人v | 免费黄色在线播放 | 日韩av成人在线观看 | 白丝美女被草 | 日韩欧美福利 | 国产99久久久国产精品成人免费 | 美女诱惑av | 91玉足脚交白嫩脚丫 | 伊人久久99 |