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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java-Java反射

發(fā)布時間:2025/3/21 java 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java-Java反射 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

  • Java反射概述
  • 示例
    • Code
    • 分析
  • 類裝載器ClassLoader
    • 工作機(jī)制
    • ClassLoader分類
    • 全盤負(fù)責(zé)委托機(jī)制
    • 重要方法
      • loadClassString name
      • defineClassString name byte b int off int len
      • findSystemClassString name
      • findLoadedClassString name
      • getParent
    • 總結(jié)
  • Java反射機(jī)制
    • 三個主要的反射類
      • Constructor
      • Method
      • Field

Java反射概述

Java語言允許通過程序化的方式間接對Class進(jìn)行操作。

Class文件由類裝載器裝載后,在JVM中形成一份描述Class結(jié)構(gòu)的元信息對象,通過該元對象可以獲知Class的結(jié)構(gòu)信息,如構(gòu)造函數(shù)、屬性和方法等。

Java允許用戶借由這個與Class相關(guān)的元信息對象間接調(diào)用Class對象的功能, 這就為使用程序化方式操作Class對象開辟了途徑。

使用反射不同于常規(guī)的Java編程,其中它與 元數(shù)據(jù)–描述其它數(shù)據(jù)的數(shù)據(jù)協(xié)作。Java語言反射接入的特殊類型的原數(shù)據(jù)是JVM中類和對象的描述。

Java反射機(jī)制可以讓我們在編譯期(Compile Time)之外的運(yùn)行期(Runtime)獲得任何一個類的字節(jié)碼。包括接口、變量、方法等信息。還可以讓我們在運(yùn)行期實(shí)例化對象,通過調(diào)用get/set方法獲取變量的值。


示例

Code

我們將用下面這個例子來了解Java反射機(jī)制。

package com.xgj.master.ioc.reflect;public class Car {private String brand ;private String color;private int speed;public String getBrand() {return brand;}public void setBrand(String brand) {this.brand = brand;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}/*** * @Title:Car* @Description:默認(rèn)構(gòu)造函數(shù)*/public Car(){}/*** * @Title:Car* @Description:帶參構(gòu)造函數(shù)* @param brand* @param color* @param speed*/public Car(String brand ,String color ,int speed){this.brand = brand;this.color = color;this.speed = speed;}public void introduceCar(){System.out.println("Car : " + this.brand + " , " + this.color + " , " + this.speed);}}

通常情況下,我們實(shí)例化類,調(diào)用類中的方法如下:

Car car = new Car("BMW","Black",180);car.introduceCar();

輸出: Car : BMW , Black , 180

這是使用傳統(tǒng)的方式來直接調(diào)用目標(biāo)類的方法。

如果使用Java的反射機(jī)制 該如何控制目標(biāo)類呢?

來看代碼

package com.xgj.master.ioc.reflect;import java.lang.reflect.Constructor; import java.lang.reflect.Method;public class ReflectTest {public static Car initCarByDefaultConstrut() throws Exception {// (1)通過類裝載器獲取Car類對象ClassLoader loader = Thread.currentThread().getContextClassLoader();Class claz = loader.loadClass("com.xgj.master.ioc.reflect.Car");// (2)獲取類的默認(rèn)構(gòu)造函數(shù),并通過它實(shí)例化CarConstructor constructor = claz.getDeclaredConstructor(null);Car car = (Car) constructor.newInstance();// (3)通過反射方法設(shè)置屬性Method method = claz.getMethod("setBrand", String.class);method.invoke(car, "BMW");Method method2 = claz.getMethod("setColor", String.class);method2.invoke(car, "black");Method method3 = claz.getMethod("setSpeed", int.class);method3.invoke(car, 100);return car;}public static void main(String[] args) throws Exception {initCarByDefaultConstrut().introduceCar();}}

運(yùn)行結(jié)果: Car : BMW , black , 100


分析

我們完全可以通過編程方式調(diào)用Class的各項(xiàng)功能,與通過構(gòu)造函數(shù)和方法直接調(diào)用類的功能的效果是一致的,只不過是間接調(diào)用罷了。

幾個重要的反射類

  • ClassLoader
  • Class
  • Constructor
  • Method .
    通過這些反射類我們就可以間接的調(diào)用目標(biāo)Class的各項(xiàng)功能。

在(1)處,我們獲取當(dāng)前線程的ClassLoader, 然后通過指全限定類名com.xgj.master.ioc.reflect.Car 來裝載Car類對應(yīng)的反射實(shí)例。

在(2)處,我們通過Car的反射類對象獲取Car的默認(rèn)構(gòu)造函數(shù)對象,通過構(gòu)造函數(shù)對象的newInstance()方法實(shí)例化Car對象,等同于 new Car()

在(3)處,我們又通過Car的反射類對象的getMethod(String methodName, Class paramsClass)獲取屬性的Setter方法對象,其中第一個參數(shù)是目標(biāo)Class的方法名,第二個參數(shù)是方法入?yún)⒌膶ο箢愋汀?

在獲取到方法反射對象后,就可以通過invoke(Object ob, Object param)方法調(diào)用目標(biāo)類的方法了。 該方法的第一個禪師是操作的目標(biāo)類對象實(shí)例,第二個參數(shù)目標(biāo)方法的入?yún)ⅰ?/font>


類裝載器ClassLoader

工作機(jī)制

類裝載器就是尋找類的字節(jié)碼文件并構(gòu)造類在JVM內(nèi)部表示對象的組件。

類裝載器把一個類裝入JVM中,步驟如下:

  • 裝載:查找和導(dǎo)入Class
  • 鏈接:執(zhí)行校驗(yàn)、準(zhǔn)備和解析步驟(解析步驟可選)
  • 初始化:對類的靜態(tài)變量、靜態(tài)代碼塊執(zhí)行初始化工作
    其中第二步操作包括:
    (1). 檢驗(yàn):檢查載入Class文件數(shù)據(jù)的正確性
    (2). 準(zhǔn)備:給類的靜態(tài)變量分配存儲空間
    (3). 解析:將符號引用轉(zhuǎn)換為直接引用
  • 類裝載工作由ClassLoader及其子類負(fù)責(zé),負(fù)責(zé)在運(yùn)行時查找和裝入Class直接碼文件。


    ClassLoader分類

    JVM運(yùn)行期間會產(chǎn)生3個ClassLoader

    • 根裝載器
    • ExtClassLoader(擴(kuò)展類裝載器)
    • AppClassLoader(應(yīng)用類裝載器)

      其中 ExtClassLoader和AppClassLoader 是 ClassLoader的子類
      根裝載器不是ClassLoader的子類,它是C++編寫。

    • 根裝載器負(fù)責(zé)裝載JRE的核心類庫,比如JRE目標(biāo)下的JAR

    • ExtClassLoader負(fù)責(zé)裝載JRE擴(kuò)展目錄ext中的JAR包

    • AppClassLoader負(fù)責(zé)裝載應(yīng)用Classpath路徑下的類包

    三者關(guān)系: 根裝載器是ExtClassLoader的父裝載器,ExtClassLoader是AppClassLoader的父裝載器。 默認(rèn)情況下,使用AppClassLoader來裝載應(yīng)用程序的類。

    驗(yàn)證下:

    package com.xgj.master.ioc.classloader;public class ClassLoaderTest {public static void main(String[] args) {// TODO Auto-generated method stubClassLoader classLoader = Thread.currentThread().getContextClassLoader();System.out.println("current loader:" + classLoader);System.out.println("parent laoder:" + classLoader.getParent());System.out.println("grandparent laoder:" + classLoader.getParent().getParent());}}

    輸出:

    current loader:sun.misc.Launcher$AppClassLoader@8391b0c parent laoder:sun.misc.Launcher$ExtClassLoader@5d1eb50b grandparent laoder:null

    根裝載器在Java中無法獲取到它的句柄,因此返回null .

    全盤負(fù)責(zé)委托機(jī)制

    JVM裝載類時使用“全盤負(fù)責(zé)委托機(jī)制”。

    全盤負(fù)責(zé):是指當(dāng)一個ClassLoader裝載一個類時,除非顯示地使用另外一個ClassLoader,該類所依賴以及引用的類也由這個ClassLoader載入。

    委托機(jī)制:是指先委托父類裝載器尋找目標(biāo)類,只有在找不到的情況下才從自己的類路徑中查找并裝載目標(biāo)類。

    這一點(diǎn)是從安全角度考慮,舉個例子,比如有人惡意編寫了一個基礎(chǔ)類如java.lang.String 并裝載到JVM中,如果沒有委托機(jī)制,jvm就會讀取了這個惡意基礎(chǔ)類,全盤負(fù)責(zé)委托機(jī)制保證了java.lang.String永遠(yuǎn)由根裝載器來裝載,避免了安全隱患的發(fā)生。

    如何查看JVM從哪個JAR包中加載指定類呢?

    請看 Java-查看JVM從哪個JAR包中加載指定類


    重要方法

    loadClass(String name)

    public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);} protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {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 statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}

    name參數(shù)指定類裝載器需要裝載的類的名字,必須使用全限定類名。

    該方法有一個重載方法 loadClass(String name ,boolean resolve) .resolve參數(shù)告訴類裝載器是否需要解析該類, 如果JVM只需要知道該類是否存在或者找出該類的超類,那么就不需要進(jìn)行解析。


    defineClass(String name, byte[] b, int off, int len)

    將類文件的字節(jié)數(shù)組轉(zhuǎn)換成JVM內(nèi)部的java.lang.Class對象。 參數(shù)name為字節(jié)數(shù)組對應(yīng)的全限定類名。


    findSystemClass(String name)

    protected final Class<?> findSystemClass(String name)throws ClassNotFoundException{ClassLoader system = getSystemClassLoader();if (system == null) {if (!checkName(name))throw new ClassNotFoundException(name);Class cls = findBootstrapClass(name);if (cls == null) {throw new ClassNotFoundException(name);}return cls;}return system.loadClass(name);}

    從本地文件系統(tǒng)裝載Class文件,不存在則拋出ClassNotFoundException。 該方法為AJVM默認(rèn)使用的裝載機(jī)制。

    findLoadedClass(String name)

    調(diào)用該方法查看ClassLoader是否已經(jīng)載入某個類,如果載入,返回java.lang.Class對象,否則返回null.

    如果強(qiáng)行裝載已經(jīng)存在的類,將拋出鏈接錯誤。


    getParent()

    @CallerSensitivepublic final ClassLoader getParent() {if (parent == null)return null;SecurityManager sm = System.getSecurityManager();if (sm != null) {checkClassLoaderPermission(parent, Reflection.getCallerClass());}return parent;}

    獲取類裝載器的父裝載器。 除了根裝載器外,所有的類裝載器都有且有且只有一個父裝載器。 ExtClassLoader的父裝載器是根裝載器。 因?yàn)楦b載器非Java語言編寫,因此無法獲得,返回null.


    總結(jié)

    除了JVM默認(rèn)的3個ClassLoader外,用戶也可以編寫自己的第三方類裝載器,以實(shí)現(xiàn)一些特殊的需求。

    類文件被裝載并解析后,在JVM內(nèi)將擁有一個對應(yīng)的java.lang.Class類描述對象,該類的實(shí)例都擁有指向這個類描述對象的引用,而類描述對象又擁有指向關(guān)聯(lián)ClassLoader的引用。

    如下圖《類實(shí)例、類描述對象及裝載器的關(guān)系》所示

    每個類在JVM中都有一個對應(yīng)的java.lang.Class對象。它提供了類結(jié)構(gòu)信息的描述。

    Class沒有public的構(gòu)造方法,Class對象是在裝載類時由JVM通過調(diào)用類裝載器中的defineClass()方法自動構(gòu)造的。


    Java反射機(jī)制

    Class反射對象描述類定義結(jié)構(gòu),可以從Class對象中獲取構(gòu)造函數(shù)、成員變量、方法類等類元素的反射對象,并以編程的方式通過這些反射對象對目標(biāo)類對象進(jìn)行操作。

    這些反射對象定義在java.lang.reflect包中。

    三個主要的反射類

    Constructor

    類的構(gòu)造函數(shù)反射類。

    通過Class#getConstructors()方法可以獲取類的所有構(gòu)造函數(shù)反射對象數(shù)組。

    在Java5.0中,還可以通過getConstructor(Class...parameterTypes)獲取擁有特定入?yún)⒌臉?gòu)造函數(shù)反射對象。

    Constructor的一個主要方法是newInstance(Object[] initargs),通過該方法可以創(chuàng)建一個對象類的實(shí)例。相當(dāng)于new關(guān)鍵字。

    在Java5.0中,該方法演化為更為靈活的形式:newInstance(Object...initargs)


    Method

    類方法的反射類。

    通過Class#getDeclaredMethods()方法可以獲取類的所有方法反射類對象數(shù)組Method[].

    在Java5.0中,可以通過getDeclaredMethod(String name,Class...parameterTypes)獲取特定簽名的方法。其中name為方法名,Class...為方法入?yún)㈩愋土斜怼?/font>

    Method最主要的方法是invoke(Object obj , Object[] args) , 其中obj表示操作的目標(biāo)對象;args為方法入?yún)ⅰ?/font>

    在Java5.0中,該方法調(diào)整為invoke(Object obj, Object...args) .

    此外,其他比較常用的方法:

    • Class getReturnType():獲取方法的返回值烈性
    • Class[] getParamaterTypes():獲取方法的入?yún)㈩愋蛿?shù)組
    • Class[] getExceptionTypes() 獲取該方法的異常類型數(shù)組
    • Annotation[][] getParameterAnnotations() 獲取方法的注解洗洗,是Java5.0中新增的方法。

    Field

    類的成員變量的反射類。

    通過Class#getDeclaredFields()方法可以獲取類的成員變量反射對象數(shù)組,
    通過Class#getDeclaredField(String name)則可以獲取某個特定名稱的成員變量反射對象。

    Field類的主要方法是set(Object obj , Object value) 其中obj表示操作的目標(biāo)對象,通過value為目標(biāo)對象的成員變量設(shè)置值。

    如果成員變量為基礎(chǔ)類型,則可以使用Field類中提供的帶類型名的值設(shè)置方法,比如setBoolean(Object obj , Object value)、setInt(Object obj , Object value)等。

    此外Java還未包提供了Package反射類,在Java5.0中還未注解提供了AnnotatedElement反射類。

    對于private或者procted成員變量和方法,只要JVM的安全機(jī)制允許,也可以通過反射調(diào)用。比如:

    package com.xgj.master.ioc.reflect;public class PrivateCar {private String brand;protected void introduce() {System.out.println("brand:" + brand);}} package com.xgj.master.ioc.reflect;import java.lang.reflect.Field; import java.lang.reflect.Method;public class PrivateCarTest {public static void main(String[] args) throws Exception {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Class claz = classLoader.loadClass("com.xgj.master.ioc.reflect.PrivateCar");PrivateCar pcar = (PrivateCar) claz.newInstance();Field field = claz.getDeclaredField("brand");// 取消Java語言訪問檢查以便訪問private變量field.setAccessible(true);field.set(pcar, "BMW");Method method = claz.getDeclaredMethod("introduce", (Class[]) null);// 取消Java語言訪問檢查以便訪問protected方法method.setAccessible(true);method.invoke(pcar, (Object[]) null);}}

    在訪問private 或者 protected成員變量和方法時,必須通過setAccessible(boolean access)方法取消Java語言檢查,否則將拋出IllegalAccessException. 如果JVM的安全管理器(SecurityManager)設(shè)置了相應(yīng)的安全機(jī)制,那么調(diào)用該方法會拋出SecurityException

    總結(jié)

    以上是生活随笔為你收集整理的Java-Java反射的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。