제이온

[Java] 反射的概念及使用方法

  • 撰写语言: 韓国語
  • 基准国家: 所有国家country-flag
  • 信息技术

撰写: 2024-04-25

撰写: 2024-04-25 22:27

反射是什麼?

反射是一種 API,它允許透過載入至堆疊區域的 Class 類型物件,建立所需類別的實例,並允許存取實例的欄位和方法,而無論存取修飾符為何。



這裡所說的載入類別,指的是 JVM 的類別載入器完成對類別檔案的載入後,建立包含該類別資訊的Class 類型物件並將其儲存到記憶體的堆疊區域。請注意,這與使用 new 關鍵字建立的物件不同。如果對此 Class 類型物件的概念不夠了解,建議查閱 java.lang.class 物件的 JDK 文件。


使用方法

在使用反射之前,需要先取得載入至堆疊區域的 Class 類型物件。總共有三種方法。


  • 使用 Class.class 取得
  • 使用 instance.getClass() 取得
  • 使用 Class.forName("類別名稱") 取得


可以確認三種方法取得的 Class 類型實例都是相同的。無論使用哪種方式,其雜湊值都相同,因此可以根據情況選擇合適的方式使用。


現在,透過取得的 Class 類型,我們可以建立該類別的實例,以及存取實例的欄位和方法,而無論存取修飾符為何。首先,讓我們建立該類別的實例。


透過 getConstructor() 可以取得建構子,並透過 newInstance() 可以動態建立 Member 實例。

最後,讓我們存取實例的欄位和方法,而無論存取修飾符為何。

透過 getDeclaredFileds() 可以取得類別的所有實例變數,透過 get() 可以取得欄位的值,透過 set() 可以修改欄位的值。需要注意的是,當存取具有 private 存取修飾符的欄位時,需要將 setAccessible() 的參數設定為 true。


方法也可以透過 getDeclaredMethod() 取得。這時需要將方法的名稱和參數的類型作為參數傳遞。同樣地,當存取具有 private 存取修飾符的方法時,需要將 setAccessible() 的參數設定為 true。最後,透過 invoke() 方法可以呼叫透過反射 API 取得的方法。


優缺點

  • 優點
    • 在執行時期可以建立類別的實例,並存取欄位和方法,而無論存取修飾符為何,具有高度的彈性。
  • 缺點
    • 破壞封裝性。
    • 在執行時期建立實例,因此無法在編譯時期檢查類型。
    • 在執行時期建立實例,因此難以掌握具體的運作流程。
    • 相較於直接存取欄位和方法,使用反射存取的效能較差。(並非所有情況下效能都會較差。)


使用原因

透過反射 API,可以在執行時期存取類別資訊,並依需求操作類別。甚至可以操作以 private 存取修飾符宣告的欄位和方法。由於這會破壞物件導向設計中重要的封裝性,因此看起來像是應該避免使用的技術。


在規模較小的主控台階段,開發人員可以充分掌握程式中使用的物件及其依賴關係。但在框架等大型開發階段,很難掌握眾多物件及其依賴關係。此時,使用反射可以動態建立類別並建立依賴關係。


例如,在 Spring 的 Bean Factory 中,只要加上 @Controller、@Service、@Repository 等註解,Bean Factory 就會自動建立並管理具有這些註解的類別。開發人員從未告知 Bean Factory 有關這些類別的資訊,但這之所以可行,就是因為反射。在執行時期搜尋具有這些註解的類別,如果找到,則使用反射建立該類別的實例,並注入必要的欄位,然後將其儲存到 Bean Factory 中。


當然,如上所述,反射會破壞封裝性,因此最好僅在必要時使用。


資料來源

评论0