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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java-Java反射

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

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

Java反射概述

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

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

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

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

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


示例

Code

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

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:默認構造函數*/public Car(){}/*** * @Title:Car* @Description:帶參構造函數* @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);}}

通常情況下,我們實例化類,調用類中的方法如下:

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

輸出: Car : BMW , Black , 180

這是使用傳統的方式來直接調用目標類的方法。

如果使用Java的反射機制 該如何控制目標類呢?

來看代碼

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)獲取類的默認構造函數,并通過它實例化CarConstructor constructor = claz.getDeclaredConstructor(null);Car car = (Car) constructor.newInstance();// (3)通過反射方法設置屬性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();}}

運行結果: Car : BMW , black , 100


分析

我們完全可以通過編程方式調用Class的各項功能,與通過構造函數和方法直接調用類的功能的效果是一致的,只不過是間接調用罷了。

幾個重要的反射類

  • ClassLoader
  • Class
  • Constructor
  • Method .
    通過這些反射類我們就可以間接的調用目標Class的各項功能。

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

在(2)處,我們通過Car的反射類對象獲取Car的默認構造函數對象,通過構造函數對象的newInstance()方法實例化Car對象,等同于 new Car()

在(3)處,我們又通過Car的反射類對象的getMethod(String methodName, Class paramsClass)獲取屬性的Setter方法對象,其中第一個參數是目標Class的方法名,第二個參數是方法入參的對象類型。

在獲取到方法反射對象后,就可以通過invoke(Object ob, Object param)方法調用目標類的方法了。 該方法的第一個禪師是操作的目標類對象實例,第二個參數目標方法的入參。


類裝載器ClassLoader

工作機制

類裝載器就是尋找類的字節碼文件并構造類在JVM內部表示對象的組件。

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

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


    ClassLoader分類

    JVM運行期間會產生3個ClassLoader

    • 根裝載器
    • ExtClassLoader(擴展類裝載器)
    • AppClassLoader(應用類裝載器)

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

    • 根裝載器負責裝載JRE的核心類庫,比如JRE目標下的JAR

    • ExtClassLoader負責裝載JRE擴展目錄ext中的JAR包

    • AppClassLoader負責裝載應用Classpath路徑下的類包

    三者關系: 根裝載器是ExtClassLoader的父裝載器,ExtClassLoader是AppClassLoader的父裝載器。 默認情況下,使用AppClassLoader來裝載應用程序的類。

    驗證下:

    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 .

    全盤負責委托機制

    JVM裝載類時使用“全盤負責委托機制”。

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

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

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

    如何查看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參數指定類裝載器需要裝載的類的名字,必須使用全限定類名。

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


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

    將類文件的字節數組轉換成JVM內部的java.lang.Class對象。 參數name為字節數組對應的全限定類名。


    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);}

    從本地文件系統裝載Class文件,不存在則拋出ClassNotFoundException。 該方法為AJVM默認使用的裝載機制。

    findLoadedClass(String name)

    調用該方法查看ClassLoader是否已經載入某個類,如果載入,返回java.lang.Class對象,否則返回null.

    如果強行裝載已經存在的類,將拋出鏈接錯誤。


    getParent()

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

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


    總結

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

    類文件被裝載并解析后,在JVM內將擁有一個對應的java.lang.Class類描述對象,該類的實例都擁有指向這個類描述對象的引用,而類描述對象又擁有指向關聯ClassLoader的引用。

    如下圖《類實例、類描述對象及裝載器的關系》所示

    每個類在JVM中都有一個對應的java.lang.Class對象。它提供了類結構信息的描述。

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


    Java反射機制

    Class反射對象描述類定義結構,可以從Class對象中獲取構造函數、成員變量、方法類等類元素的反射對象,并以編程的方式通過這些反射對象對目標類對象進行操作。

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

    三個主要的反射類

    Constructor

    類的構造函數反射類。

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

    在Java5.0中,還可以通過getConstructor(Class...parameterTypes)獲取擁有特定入參的構造函數反射對象。

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

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


    Method

    類方法的反射類。

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

    在Java5.0中,可以通過getDeclaredMethod(String name,Class...parameterTypes)獲取特定簽名的方法。其中name為方法名,Class...為方法入參類型列表。

    Method最主要的方法是invoke(Object obj , Object[] args) , 其中obj表示操作的目標對象;args為方法入參。

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

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

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

    Field

    類的成員變量的反射類。

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

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

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

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

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

    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)設置了相應的安全機制,那么調用該方法會拋出SecurityException

    總結

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

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