5.7 java之反射(獲取class對象,分析類對象的成員變量、構造器、普通方法、動態分析類對象的值,編寫泛型數組,調用任意方法)

0.什麼是反射?

  • 首先我們先了解一下反射庫,反射庫(reflection library)提供了一個非常豐富且精心設計的工具集, 以便編寫能夠動態操縱 Java 代碼的程序
  • 使用反射,在設計或運行中添加新類時, 能夠快速地應用開發工具動態地查詢新添 加類的能力。

能夠分析類能力的程序稱爲反射(reflective)

反射機制可以用來:

  • 在運行時分析類的能力。
  • 在運行時查看對象, 例如, 編寫一個 toString方法供所有類使用。
  • 實現通用的數組操作代碼。
  • 利用 Method 對象, 這個對象很像C++中的函數指針

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

1.獲取class類對象

  • 在程序運行期間,Java運行時系統始終爲所有的對象維護一個被稱爲運行時的類型標識。 這個信息跟蹤着每個對象所屬的類。 虛擬機利用運行時類型信息選擇相應的方法執行。
  • 然而,可以通過專門的 Java 類訪問這些信息保存這些信息的類被稱爲 Class

獲得Class類對象的三種方法:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

  • 在啓動時, 包含 main 方法的類被加載。它會加載所有需要的類。這些被加栽的類 又要加載它們需要的類, 以此類推。對於一個大型的應用程序來說, 這將會消耗很多時 間, 用戶會因此感到不耐煩。
  • 可以使用下面這個技巧給用戶一種啓動速度比較快的幻覺。 不過,要確保包含 main 方法的類沒有顯式地引用其他的類。
  • 首先,顯示一個啓動畫面; 然後,通過調用 Class.forName 手工地加載其他的類。

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

在這裏插入圖片描述
還有一個很有用的方法 newlnstance( ), 可以用來動態地創建一個類的實例例如:

在這裏插入圖片描述

創建了一個與 e具有相同類類型的實例。newlnstance方法調用默認的構造器(沒有參數的構 造器)初始化新創建的對象。如果這個類沒有默認的構造器, 就會拋出一個異常。

將 forName 與 newlnstance 配合起來使用, 可以根據存儲在字符串中的類名創建一個對象
在這裏插入圖片描述

2.初識捕獲異常

  • 我們將在第 7 章中全面地講述異常處理機制,但現在時常遇到一些方法需要拋出異常。
  • 當程序運行過程中發生錯誤時, 就會“ 拋出異常’拋出異常比終止程序要靈活得多, 這是因爲可以提供一個“ 捕獲” 異常的處理器(handler)對異常情況進行處理, 如果沒有提供處理器,程序就會終止,並在控制檯上打印出一條信息, 其中給出了異常的 類型。可能在前面已經看到過一些異常報告, 例如, 偶然使用了 null 引用或者數組越界等。
  • 異常有兩種類型: 未檢查異常和已檢查異常
    對於已檢查異常, 編譯器將會檢查是否提供了處理器。
    然而,有很多常見的異常, 例如,訪問 null 引用, 都屬於未檢查異常。編譯 器不會査看是否爲這些錯誤提供了處理器。
    畢竟,應該精心地編寫代碼來避免這些錯誤的發 生, 而不要將精力花在編寫異常處理器上。 並不是所有的錯誤都是可以避免的。
    如果竭盡全力還是發生了異常, 編譯器就要求提供 一個處理器。

現在, 只介紹一下如何實現最簡單的處理器。
將可能拋出已檢査異常的一個或多個方法調用代碼放在 try塊中,然後在 catch 子句中提 供處理器代碼。

在這裏插入圖片描述

  • 如果類名不存在, 則將跳過 try塊中的剩餘代碼,程序直接進人 catch 子句(這裏,利用 Throwable 類的 printStackTrace 方法打印出棧的軌跡。Throwable 是 Exception 類的超類) 。
  • 如果try塊中沒有拋出任何異常, 那麼會跳過 catch 子句的處理器代碼。
  • 對於已檢查異常,只需要提供一個異常處理器。可以很容易地發現會拋出已檢査異常的 方法。如果調用了一個拋出已檢查異常的方法,而又沒有提供處理器,編譯器就會給出錯誤 報告

在這裏插入圖片描述

3.利用反射分析類的能力,獲取成員變量、構造器、方法的結構和類型

我們可以利用反射機制檢查類的結構。
下面介紹一下reflect包中的類和方法來分析類的能力:

  • 在java.lang.reflect 包中有三個類 Field、Method 和 Constructor分別用於描述類的域、 方 法和構造器
    這三個類都有一個叫做 getName 的方法, 用來返回項目的名稱。
    還有一個叫 做getModifiers的方法, 它將返回一個整型數值,用不同的位開關描述 public 和 static 這樣 的修飾符使用狀況。

  • 可以利用java.lang.reflect 包中的 Modifiei類的靜態方法分析getModifiers返回的整型數值。
    例如:可以使用Modifiei類中的 isPublic、 isPrivate 或 isFinal 判斷方法或構造器是否是 public、 private 或 final。
    我們需要做的全部工作就是調用 Modifier 類的相應方法,並對返回的整型數值進行分析,另外,還可以利用 Modifier.toString方法將 修飾符打印出來

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

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

  • Class類中的 getFields、 getMethods 和 getConstructors方 法將 分 別 返 回 類 提 供 的 public 域、 方法和構造器數組, 其中包括超類的公有成員

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

下面是一個代碼案例,顯示瞭如何打印一個類的全部信息的方法。
這個程序提醒用戶輸入一個類名,然後輸出類中所有的方法和構造器簽名,以及全部域名。

在這裏插入圖片描述

輸出結果:

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

4.在運行時使用反射分析對象,獲取類對象的成員變量值和類型

在編寫程序時, 如果知道想要査看的域名 和類型,查看指定的域是一件很容易的事情。而利用反射機制可以查看在編譯時還不清楚的對象域

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

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

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

在這裏插入圖片描述
setAccessible方法是 AccessibleObject 類中的一個方法, 它是 Field、 Method 和 Constructor 類的公共超類。這個特性是爲調試、持久存儲和相似機制提供的。

get方法還有一個需要解決的問題。name 域是一個 String, 因此把它作爲 Object 返回 沒有什麼問題。但是, 假定我們想要查看salary 域。它屬於 double 類型,而 Java中數值類 型不是對象。要想解決這個問題, 可以使用 Field 類中的 getDouble方法,也可以調用 get 方法,此時, 反射機制將會自動地將這個域值打包到相應的對象包裝器中,這裏將打包成 Double

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

下圖顯示瞭如何編寫一個可供任意類使用的通用 toString方法。 其中使用 getDeclaredFileds 獲得所有的數據域, 然後使用 setAccessible 將所有的域設置爲可訪問的。 對 於每個域,獲得了名字和值。遞歸調用 toString方法,將每個值轉換成字符串。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
泛型 toString方法需要解釋幾個複雜的問題。循環引用將有可能導致無限遞歸。因此, ObjectAnalyzer 將記錄已經被訪問過的對象。另外, 爲了能夠査看數組內部, 需要採用一種 不同的方式

還可以使用通用的 toString 方法實現ft 己類中的 toString 方法, 如下所示:
在這裏插入圖片描述
這是一種公認的提供 toString 方法的手段, 在編寫程序時會發現, 它是非常有用的。

5.使用反射編寫泛型數組代碼,複製數組

  • 可以利用反射編寫一個通用的擴充任意類型數組長度的方法,類似copyOf()
  • 這裏需要注意的一點是:將一個Employee[]數組臨時的轉換爲Object[],然後再轉換回來是可以的,但是從一開始就是Object[]數組卻永遠不能轉換成Employee[]數組。(強制類型轉換),聯想父類子類的轉換。
    在這裏插入圖片描述在這裏插入圖片描述

6.利用反射庫調用任意方法

  • 反射機制可以允許調用任意的方法
  • 在Method類中有一個invoke方法,它可以允許調用包裝在Method對象中的方法
    在這裏插入圖片描述
    在這裏插入圖片描述
    下面是一個代碼實例:

在這裏插入圖片描述在這裏插入圖片描述
參考:《java核心技術卷一》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章