了解和擴展Java ClassLoader

Java ClassLoader是項目開發中Java的關鍵但很少使用的組件之一。 就我個人而言,我從未在任何項目中擴展ClassLoader,但是擁有自己的可以自定義Java類加載的ClassLoader的想法讓我感到很興奮。

本文將概述Java類加載,然后繼續創建自定義ClassLoader并使用它。

什么是ClassLoader?

我們知道Java程序在Java虛擬機(JVM)上運行。 當我們編譯Java類時,它會以字節碼的形式將其轉換為平臺和機器無關的已編譯程序,并將其存儲為.class文件。 之后,當我們嘗試使用類時,Java ClassLoader將該類加載到內存中。

Java中內置了三種類型的內置類加載器:

  1. Bootstrap類加載器 –它加載JDK內部類,通常加載rt.jar和其他核心類,例如java.lang。*包類
  2. 擴展類加載器 –它從JDK擴展目錄(通常為$ JAVA_HOME / lib / ext目錄)中加載類。
  3. 系統類加載器 –它從當前類路徑加載類,這些類可以在使用-cp或-classpath命令行選項調用程序時進行設置。

讓我們通過執行以下java程序更好地理解這一點:

ClassLoaderTest.java

package com.journaldev.classloader;public class ClassLoaderTest {public static void main(String[] args) {System.out.println("class loader for HashMap: "+ java.util.HashMap.class.getClassLoader());System.out.println("class loader for DNSNameService: "+ sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());System.out.println("class loader for this class: "+ ClassLoaderTest.class.getClassLoader());System.out.println(com.mysql.jdbc.Blob.class.getClassLoader());}}

上面程序的輸出:

HashMap的類加載器:null
DNSNameService的類加載器:sun.misc.Launcher$ExtClassLoader@51f12c4e
此類的類加載器:sun.misc.Launcher$AppClassLoader@799134f4
sun.misc.Launcher$AppClassLoader@799134f4

如您所見,java.util.HashMap ClassLoader作為null來反映Bootstrap ClassLoader,而DNSNameService ClassLoader是ExtClassLoader。 由于類本身位于CLASSPATH中,因此System ClassLoader會加載它。

當我們嘗試加載HashMap時,我們的System ClassLoader將其委托給Extension ClassLoader,后者再將其委托給找到該類的Bootstrap ClassLoader并將其加載到JVM中。 DNSNameService類遵循相同的過程,但是Bootstrap ClassLoader無法定位它,因為它位于$ JAVA_HOME / lib / ext / dnsns.jar中,因此由擴展類加載器加載。

還有一點要注意的是,子類加載器加載的類可以查看其父類加載器加載的類。 因此,由System ClassLoader加載的類具有對Extensions和Bootstrap ClassLoader加載的類的可見性。

如果有同級類加載器,則它們將無法訪問彼此加載的類。

為什么要編寫ClassLoader?

Java默認的ClassLoader可以從本地文件系統中加載文件,這在大多數情況下已經足夠了。 但是,如果在加載類時希望在運行時或從FTP服務器或通過第三方Web服務獲取類,則必須擴展現有的類加載器。 例如,AppletViewers從遠程Web服務器加載類。

ClassLoader如何工作?

當JVM請求一個類時,它通過傳遞類的完全分類名稱來調用ClassLoader的loadClass函數。

loadClass函數調用findLoadedClass()方法來檢查該類是否已加載。 需要避免多次加載該類。

如果尚未加載該類,則它將把請求委派給父ClassLoader以加載該類。

如果父ClassLoader找不到該Class,則它將調用findClass()方法在文件系統中查找這些類。

創建我們自己的ClassLoader

我們將通過擴展ClassLoader類并覆蓋loadClass(String name)函數來創建自己的ClassLoader。 如果名稱以com.journaldev(即我們的示例類包)開頭,則將使用我們自己的類加載器加載它,否則我們將調用父ClassLoader loadClass()方法加載該類。

項目結構如下圖所示:

CCLoader.java:這是我們的自定義類加載器,具有以下方法。

1. 專用字節[] loadClassFileData(字符串名稱)

此方法將從文件系統讀取類文件到字節數組。

2. 私有類getClass(String name)

此方法將調用loadClassFileData()函數,并通過調用父defineClass()方法,它將生成Class并返回它。

3. public Class loadClass(字符串名稱)

此方法負責加載Class。 如果類名以com.journaldev(我們的示例類)開頭,則它將使用getClass()方法加載它,否則它將調用父loadClass函數來加載它。

4. public CCLoader(ClassLoader的父對象)

這是負責設置父ClassLoader的構造函數。

CCLoader源代碼:

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;/*** Our Custom Class Loader to load the classes. Any class in the com.journaldev* package will be loaded using this ClassLoader. For other classes, it will* delegate the request to its Parent ClassLoader.**/
public class CCLoader extends ClassLoader {/*** This constructor is used to set the parent ClassLoader*/public CCLoader(ClassLoader parent) {super(parent);}/*** Loads the class from the file system. The class file should be located in* the file system. The name should be relative to get the file location** @param name*            Fully Classified name of class, for example com.journaldev.Foo*/private Class getClass(String name) throws ClassNotFoundException {String file = name.replace('.', File.separatorChar) + ".class";byte[] b = null;try {// This loads the byte code data from the fileb = loadClassFileData(file);// defineClass is inherited from the ClassLoader class// that converts byte array into a Class. defineClass is Final// so we cannot override itClass c = defineClass(name, b, 0, b.length);resolveClass(c);return c;} catch (IOException e) {e.printStackTrace();return null;}}/*** Every request for a class passes through this method. If the class is in* com.journaldev package, we will use this classloader or else delegate the* request to parent classloader.*** @param name*            Full class name*/@Overridepublic Class loadClass(String name) throws ClassNotFoundException {System.out.println("Loading Class '" + name + "'");if (name.startsWith("com.journaldev")) {System.out.println("Loading Class using CCLoader");return getClass(name);}return super.loadClass(name);}/*** Reads the file (.class) into a byte array. The file should be* accessible as a resource and make sure that its not in Classpath to avoid* any confusion.** @param name*            File name* @return Byte array read from the file* @throws IOException*             if any exception comes in reading the file*/private byte[] loadClassFileData(String name) throws IOException {InputStream stream = getClass().getClassLoader().getResourceAsStream(name);int size = stream.available();byte buff[] = new byte[size];DataInputStream in = new DataInputStream(stream);in.readFully(buff);in.close();return buff;}
}

CCRun.java:

這是帶有主要功能的測試類,我們在其中創建ClassLoader的對象并使用其loadClass方法加載示例類。 加載該類后,我們將使用Java Reflection API來調用其方法。

import java.lang.reflect.Method;public class CCRun {public static void main(String args[]) throws Exception {String progClass = args[0];String progArgs[] = new String[args.length - 1];System.arraycopy(args, 1, progArgs, 0, progArgs.length);CCLoader ccl = new CCLoader(CCRun.class.getClassLoader());Class clas = ccl.loadClass(progClass);Class mainArgType[] = { (new String[0]).getClass() };Method main = clas.getMethod("main", mainArgType);Object argsArray[] = { progArgs };main.invoke(null, argsArray);// Below method is used to check that the Foo is getting loaded// by our custom class loader i.e CCLoaderMethod printCL = clas.getMethod("printCL", null);printCL.invoke(null, new Object[0]);}}

Foo.java和Bar.java:

這些是由我們的自定義類加載器加載的測試類。 它們還具有一個printCL()方法,該方法將被調用以打印已加載該類的ClassLoader。 Foo類將由我們的自定義類加載器加載,后者又使用Bar類,因此Bar類也將由我們的自定義類加載器加載。

Foo.java源代碼:

package com.journaldev.cl;public class Foo {static public void main(String args[]) throws Exception {System.out.println("Foo Constructor " + args[0] + " " + args[1]);Bar bar = new Bar(args[0], args[1]);bar.printCL();}public static void printCL() {System.out.println("Foo ClassLoader: "+Foo.class.getClassLoader());}
}

Bar.java源代碼:

package com.journaldev.cl;public class Bar {public Bar(String a, String b) {System.out.println("Bar Constructor  " + a + " " + b);}public void printCL() {System.out.println("Bar ClassLoader: "+Bar.class.getClassLoader());}
}

執行步驟:

首先,我們將通過命令行編譯所有類。 之后,我們將通過傳遞三個參數來運行CCRun類。 第一個參數是Foo類的完全分類名稱,它將由我們的類加載器加載。 其他兩個參數將傳遞給Foo類的主函數和Bar構造函數。 輸出的執行步驟如下所示。

Pankaj $ javac -cp。 com / journaldev / cl / Foo.java
Pankaj $ javac -cp。 com / journaldev / cl / Bar.java
Pankaj $ javac CCLoader.java
Pankaj $ javac CCRun.java
CCRun.java:18:警告:對最后一個參數使用不精確參數類型的varargs方法的非varargs調用;
轉換為java.lang.Class以進行varargs調用
強制轉換為java.lang.Class []進行非可變參數調用并禁止顯示此警告
方法printCL = clas.getMethod(“ printCL”,null);
^
1警告
Pankaj $ java CCRun com.journaldev.cl.Foo 1212 1313
正在加載類“ com.journaldev.cl.Foo”
使用CCLoader加載類
加載類“ java.lang.Object”
加載類“ java.lang.String”
加載類“ java.lang.Exception”
加載類“ java.lang.System”
加載類“ java.lang.StringBuilder”
加載類'java.io.PrintStream'
Foo構造函數1212 1313
加載類“ com.journaldev.cl.Bar”
使用CCLoader加載類
酒吧建設者1212 1313
加載類“ java.lang.Class”
欄類加載器:CCLoader @ 71f6f0bf
Foo ClassLoader:CCLoader @ 71f6f0bf
ctk-pcs1313512-2:src pk93229 $

如果仔細查看輸出,則首先嘗試加載com.journaldev.cl.Foo類,但是由于擴展了java.lang.Object類,因此嘗試首先加載它,并請求將其委托給CCLoader loadClass方法它到父類。 因此,父類加載器正在加載Object,String和其他Java類。 我們的ClassLoader僅從文件系統加載Foo和Bar類,而當我們調用它們的printCL()函數時,該文件系統將變得很清晰。

請注意,我們可以更改loadClassFileData()功能以從FTP服務器讀取字節數組,或者通過調用任何第三方服務來即時獲取類字節數組。

參考: Java面試問題:從我們的JCG合作伙伴Pankaj 了解和擴展Java ClassLoader 。

相關文章:

  • JDK中的設計模式
  • Java內存模型–快速概述和注意事項
  • Java Fork / Join進行并行編程
  • 依賴注入–手動方式

翻譯自: https://www.javacodegeeks.com/2011/03/understanding-extending-java.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374616.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374616.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374616.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CAD教程-AL對其命令

AL可以實現不規則的對其功能 1.第一步按下AL,按下Enter 2.選擇第一個源點 3.選擇第一個目標點 4.選擇第二個源點 5.選擇第二個目標點 6.按下Enter,完成移位 轉載于:https://www.cnblogs.com/weloveshare/p/4739873.html

使用Spring將POJO公開為JMX MBean

這是一個非常不錯的教程,介紹了如何通過我們最新的JCG合作伙伴 “ The Holy Java ”博客(很酷的名字)實現“ 用Spring輕松將POJO作為JMX MBean公開 ”。 (注意:對原始帖子進行了少量編輯以提高可讀性) Jav…

mysql 5.1由于Host為localhost的用戶為空,密碼為空,導致本地用戶無法登陸。

不說了。直接上mysql的用戶數據,第四列里面,host為localhost,用戶為空,密碼為空。 導致在本地登陸的時候除了root的賬戶外,其他賬號不需要密碼即可登陸,并且影響host為 %的用戶登陸。 這里只需要刪除對應的…

scala 88 for替換map,flatmap,filtermap,for,scala,flatmap

王家林親授《DT大數據夢工廠》大數據實戰視頻“Scala深入淺出實戰經典”視頻、音頻和PPT下載!第88講:Scala中使用For表達式實現map、flatMap、filter百度云盤:http://pan.baidu.com/s/1mgtgcIG360云盤:http://yunpan.cn/cdXsbctXf…

簡單闡述下OC中UIImage三種創建方式~~~

一. 直接使用imageNamed進行創建 1 UIImage * image [UIImage imageNamed:"1.jpg"]; 簡單說一下這種方式的優缺點: 優點:代碼量少,一行代碼就可以搞定。當程序中多次加載這張圖片時,系統會指向同一塊內存,…

(一二四)tableView的多組數據展示和手動排序

最近在寫一個輕量級的網絡游戲,遇到了技能優先順序手動排序的需求,我就想到了iOS自帶的tableView編輯功能,對其進行了初步探索,最后做出的效果如下圖所示: 點擊左邊可以刪除,拖住右邊可以手動排序&#xff…

ARC_xp_20160530

1、 申請內存的地方在哪里?忘了...(應該是用的 malloc) 2、 鍵盤上按下一個鍵,處理的函數為:(所在的模塊 應該是“CEGUIIrrlichtRenderer.dll”) 003B465F CC INT3 003B4660 /$ 53 PUSH EBX 003B4661 |. 56 …

Hibernate映射集合性能問題

首先,這篇文章的靈感來自于Burt Beckwith在2011年1月27日于SpringOne 2GX上發表的有關高級GORM –性能,定制和監控的演講 。 簡而言之, Burt Beckwith討論了使用映射集合和GORM中的Hibernate 2級緩存的潛在性能問題,以及避免此類性…

算法:1!+(1!+3!)+(1!+3!+5!) + ( 1! + 3! + 5! + 7! + 9!)+....+(1!+3!+5!+ ... + m!)...

-(void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event{ //算法入口 [self func2:9]; } //計算階乘 factor&#xff08;m&#xff09;&#xff1d; m&#xff01; -(int)factor:(int)m{ int factorNum0; if(m0|m1) return 1; e…

知道這 20 個正則表達式,能讓你少寫 1,000 行代碼

CocoaChina05-13正則表達式&#xff0c;一個十分古老而又強大的文本處理工具&#xff0c;僅僅用一段非常簡短的表達式語句&#xff0c;便能夠快速實現一個非常復雜的業務邏輯。熟練地掌握正則表達式的話&#xff0c;能夠使你的開發效率得到極大的提升。下面是技匠整理的&#x…

用Jackson進行Java JSON處理

JSON &#xff08;Javascript對象符號&#xff09;正成為一種非常流行的數據交換格式。 在使用諸如YUI&#xff0c;ExtJS&#xff0c;DOJO等Javascript框架開發Web應用程序時&#xff0c;我們可以使用XML或JSON在客戶端和服務器之間交換數據。 通常&#xff0c;我們從服務器獲得…

Android Togglebutton 默認背景被放大

1 . 最近在項目中自定義toggleButton 發現背景圖放入后 &#xff0c;比美工給我的原圖要大很大 2. 為什么&#xff1f; 3.比較ToggleButton 和TextView源碼 發現, toggleButton 設置了minWidth 和minHeigh &#xff0c;導致我的背景小圖被拉伸。 4.解決這種問題&#xff1a; 重…

MapReduce:簡單介紹

MapReduce是Google流行的一種并行編程技術。 它用于處理大量數據。 僅通過將工作并行分配給多臺機器&#xff0c;就可以在合理的時間內完成這種處理。 每臺機器都處理一小部分數據。 MapReduce是一種編程模型&#xff0c;使開發人員可以專注于編寫處理數據的代碼&#xff0c;而…

python翻譯詞典實例

#!/usr/bin/python # -*- coding:utf-8 -*- #通過有道翻譯來進行內容翻譯 import urllib2 import urllib import json #---------翻譯方法定義 start---------# def transfer( transferStr , lanSource auto ): data {type:lanSource,i:transferStr,doctype:json,xmlVersion:1…

元素分類--塊級元素(特點:獨占一行, 寬高邊距可改)

什么是塊級元素&#xff1f;在html中<div>、 <p>、<h1>、<form>、<ul> 和 <li>就是塊級元素。設置display:block就是將元素顯示為塊級元素。如下代碼就是將內聯元素a轉換為塊狀元素&#xff0c;從而使a元素具有塊狀元素特點。 a{display:b…

Java應用程序中的消息傳遞主體

消息傳遞是每個Java應用程序的關鍵方面&#xff0c;尤其是對于涉及企業應用程序集成&#xff08;EAI&#xff09;或關注點分離的應用程序&#xff0c;例如多層WEB應用程序。 消息傳遞可以分為兩個主要類別&#xff0c;即同步和異步。 另一方面&#xff0c;使用同步消息傳遞時&…

站立會議05(第二次沖刺)

一、站立會議信息&#xff08;配站立會議照片&#xff09; 第五天我們繼續開發&#xff0c;把注冊驗證信息完善一下&#xff0c;將開始網站公共主頁的開發。 二、任務進度 第五天我們注冊驗證完成。 三、任務看板&#xff08;圖&#xff09; 四、燃盡圖&#xff08;圖&#xff…

[SoapUI] DataSource, DataSourceLoop, DataSink

Script assertion in login: 轉載于:https://www.cnblogs.com/MasterMonkInTemple/p/4748189.html

1154. 一年中的第幾天

給你一個字符串 date &#xff0c;按 YYYY-MM-DD 格式表示一個 現行公元紀年法 日期。請你計算并返回該日期是當年的第幾天。 通常情況下&#xff0c;我們認為 1 月 1 日是每年的第 1 天&#xff0c;1 月 2 日是每年的第 2 天&#xff0c;依此類推。每個月的天數與現行公元紀年…

將CAPTCHA添加到您的GWT應用程序

什么是驗證碼&#xff1f; 在一個充滿惡意機器人的世界中&#xff0c;您該怎么做才能保護您寶貴的Web應用程序&#xff1f; 您真正應該做的基本事情之一就是向其中添加CAPTCHA功能。 如果您不熟悉&#xff08;聽起來有些奇怪&#xff09;&#xff0c;則CAPTCHA是確保用戶實際上…