一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java的反射機制

Java的反射機制

2023-05-08 01:04未知服務器之家 Java教程

Java 的反射機制允許在程序運行期間,借助反射 API 獲取類的內部信息,并能直接操作對象的內部屬性及方法。 介紹反射機制 Java 的反射機制允許在程序運行期間,借助反射 API 獲取類的內部信息,并能直接操作對象的內部屬性及方

Java 的反射機制允許在程序運行期間,借助反射 API 獲取類的內部信息,并能直接操作對象的內部屬性及方法。

介紹反射機制

Java 的反射機制允許在程序運行期間,借助反射 API 獲取類的內部信息,并能直接操作對象的內部屬性及方法。


Java 反射機制提供的功能:

  • 在運行時,使用反射分析類的能力,獲取有關類的一切信息(類所在的包、類實現的接口、標注的注解、類的數據域、類的構造器、類的方法等)
  • 在運行時,使用反射分析對象,設置實例域的值,查看實例域的值。
  • 反射機制允許你調用任意方法(類的構造器方法、類的成員方法 等)

反射是一種功能強大且復雜的機制。使用反射機制的主要人員是工具構造者,而不是應用程序員。

Class 類

在程序運行期間,Java 運行時系統始終為所有的對象維護一個被稱為運行時的類型標識。這個信息跟蹤著每個對象所屬的類。虛擬機利用運行時類型信息選擇相應的方法執行。

然而,可以通過專門的 Java 類訪問這些信息。保存這些信息的類被稱為 Class。Object 類中的 getClass() 方法將會返回一個 Class 類型的實例。

如同用一個 Employee 對象表示一個特定的雇員屬性一樣,一個 Class 對象將表示一個特定類的屬性。

虛擬機為每個類型管理一個 Class 對象。因此,可以利用 == 運算符實現兩個 Class 對象比較的操作。

// 獲得 Class 對象的多種方式:
public static void main(String[] args) {
    // 方式 1
    // 如果 T 是任意的 Java 類型 (或 void 關鍵字), T.class 將代表匹配的 Class 對象。
    Class<Person> clazz1 = Person.class;

    // 方式 2
    Person person = new Person();
    Class clazz2 = person.getClass();

    // 方式 3
    try {
        Class clazz3 = Class.forName("類的路徑");
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    }

    // 方式4
    // 獲取到 ClassLoader(這里獲取到的是:AppClassLoader)
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    try {
        Class clazz4 = classLoader.loadClass("類的路徑");
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}

還有一個很有用的方法:Class 類的 newlnstance(),可以用這個方法來動態地創建一個類的實例。newlnstance() 方法調用默認的構造器(沒有參數的構造器)初始化新創建的對象。如果這個類沒有默認的構造器,就會拋出一個 InstantiationException 異常。

將 Class 類的 forName() 方法與 Class 類的 newlnstance() 方法配合起來使用,可以根據存儲在字符串中的類名創建一個對象。

public static void main(String[] args) throws Exception {
    String className = "java.util.Random";
    Object object = Class.forName(className).newInstance();
}

如果需要以這種方式向希望按名稱創建的類的構造器提供參數,就不要使用上面那條語句,而必須使用 Constructor 類中的 newlnstance() 方法。

分析類的能力

在運行時,使用反射分析類的能力。

下面簡要地介紹一下反射機制最重要的內容:檢查類的結構。在 java.lang.reflect 包中有三個類 Field、Method 和 Constructor 分別用于描述類的數據域、類的方法和類的構造器。


這三個類都有一個叫做 getName() 的方法,用來返回項目的名稱。

Field 類有一個 getType() 方法,用來返回描述數據域所屬類型的 Class 對象。

Method 類和 Constructor 類有能夠報告參數類型的方法,Method 類還有一個可以報告返回類型的方法。

這三個類還有一個叫做 getModifiers() 的方法,它將返回一個整型數值,用不同的位開關描述 public 和 static 這樣的修飾符使用狀況。另外, 還可以利用 java.lang.reflect 包中的 Modifier 類的靜態方法分析 getModifiers() 返回的整型數值。例如,可以使用 Modifier 類中的 isPublic()、isPrivate() 或 isFinal() 判斷方法或構造器是否是 public、private 或 final 的。我們需要做的全部工作就是調用 Modifier 類的相應方法,并對返回的整型數值進行分析,另外,還可以利用 Modifier.toString() 方法將修飾符打印出來。


Class 類的 getFields()、getMethods() 和 getConstructors() 方法將分別返回類中聲明的 public 域、public 方法和 public 構造器數組,其中包括父類的公有成員。

Class 類的 getDeclareFields()、getDeclareMethods() 和 getDeclaredConstructors() 方法將分別返回類中聲明的全部的數據域、全部的方法和全部的構造器,其中包括私有和受保護成員,但不包括父類的成員。

分析對象

在運行時,使用反射分析對象。

從前面一節中,已經知道如何查看任意對象的數據域的名稱和類型:

  • 獲得對應的 Class 對象。
  • 調用 Class 對象的 getDeclaredFields() 方法。

本節將進一步查看數據域的實際內容。當然,在編寫程序時,如果知道想要査看的數據域的名稱和類型,查看指定的數據域是一件很容易的事情。而利用反射機制可以查看在編譯時還不清楚的數據域。

查看數據域值的關鍵方法是 Field 類中的 get() 方法。如果 f 是一個 Field 類型的對象(例如,通過 getDeclaredFields() 得到的對象),obj 是某個包含 f 域的類的對象,f.get(obj) 將返回一個對象,其值為 obj 對象的 f 域的當前值。

當然,可以獲得就可以設置。調用 f.set(obj, value) 可以將 obj 對象的 f 域設置成新值。


public static void main(String[] args) {
    Employee harry = new Employee("Harry Hacker", 35000, 10, 1, 1989);
    Class cl = harry.getClass();
    
    // the class object representing Employee
    Field f = cl.getDeclaredField("name");
    // the name field of the Employee class
    Object v = f.get(harry);
    // the value of the name field of the harry object, i .e., the String object "Harry Hacker"
}

實際上,上面這段代碼存在一個問題。由于 name 是一個私有域,所以 get() 方法將會拋出一個 illegalAccessException。只有利用 get() 方法才能得到可訪問域的值。除非擁有訪問權限,否則 Java 安全機制只允許査看任意對象有哪些域,而不允許讀取它們的值。

反射機制的默認行為受限于 Java 的訪問控制。然而,如果一個 Java 程序沒有受到安全管理器的控制,就可以覆蓋訪問控制。為了達到這個目的,需要調用 Field、Method 或 Constructor 對象的 setAccessible() 方法。例如:

f.setAtcessible(true); // now OK to call f.get(harry);

setAccessible() 方法是 AccessibleObject 類中的一個方法,AccessibleObject 類是 Field、Method 和 Constructor 類的公共父類。這個特性是為調試、持久存儲和相似機制提供的。

調用任意方法

在 C 和 C++ 中,可以從函數指針執行任意函數。從表面上看,Java 沒有提供方法指針,即將一個方法的存儲地址傳給另外一個方法,以便第二個方法能夠隨后調用它。事實上,Java 的設計者曾說過:方法指針是很危險的,并且常常會帶來隱患。他們認為 Java 提供的接口(interface)是一種更好的解決方案。然而,反射機制允許你調用任意方法。

為了能夠看到方法指針的工作過程,先回憶一下利用 Field 類的 get() 方法查看數據域值的過程。與之類似,在 Method 類中有一個 invoke() 方法,它允許調用包裝在當前 Method 對象中的方法。


可以使用 method 對象實現 C 語言中函數指針(或 C# 中的委派)的所有操作。同 C 一樣,這種程序設計風格并不太簡便,出錯的可能性也比較大。如果在調用方法的時候提供了一個錯誤的參數,那么 invoke() 方法將會拋出一個異常。

另外, invoke() 方法的參數和返回值必須是 Object 類型的。這就意味著必須進行多次的類型轉換。這樣做將會使編譯器錯過檢查代碼的機會。因此,等到測試階段才會發現這些錯誤,找到并改正它們將會更加困難。

在進行類型轉換的過程中,編譯器無法檢查代碼中類型轉換的正確性,也就是無法保證轉換后的類型與原始類型是兼容的。這樣就會增加程序出錯的可能性,并且如果出現錯誤的話,調試和修正也會更加困難。

不僅如此,使用反射獲得方法指針的代碼執行要比直接調用方法明顯慢一些。

有鑒于此,建議僅在必要的時候才使用 Method 對象,而最好使用接口以及 Java8 中的 lambda 表達式。

特別要重申:建議 Java 開發者不要使用 Method 對象的回調功能。使用接口進行回調會使得代碼的執行速度更快,更易于維護。

參考資料

《Java核心技術卷一:基礎知識》(第10版)第 5 章:繼承 5.7 反射

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产嘿咻 | 亚洲是图你懂的 | 亚州日韩精品AV片无码中文 | 高中生放荡日记高h娜娜 | 日韩精品福利视频一区二区三区 | 欧美色图亚洲 | 国产成人精品一区二区阿娇陈冠希 | 国产日韩高清一区二区三区 | 草莓视频旧版本 | 国产高清视频一区二区 | www.麻豆| 亚洲欧美优优色在线影院 | 日本精品一区二区三区 | 欧美一级h | aaaa黄| 91精品大神国产在线播放 | 日本wwxx| 青青热久久综合网伊人 | 国产大片51精品免费观看 | 四虎影库网址 | 亚洲AV无码偷拍在线观看 | 亚洲天堂免费看 | 国产第一综合另类色区奇米 | 丁香五香天堂 | 国产亚洲人成网站在线观看不卡 | 色哟哟哟| jizz 日本亚洲 | 天堂一区二区在线观看 | 爸爸的宝贝小说全文在线阅读 | 亚洲天堂三区 | 激情小说色图 | 天天草人人草 | 图片一区 | 日本大乳护士的引诱图片 | xvideoscom极品肌肉警察 | 日韩一级片在线观看 | 四虎影库紧急大通知 | 久久精品一卡二卡三卡四卡视频版 | 丝袜护士强制脚足取精 | 国产精品香蕉 | 日本一区二区不卡久久入口 |