Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

CH17:反射與類別載入器

152 vues

Publié le

取得.class檔案資訊
動態生成物件與操作方法
認識模組與反射的權限設定
瞭解JDK9類別載入器階層
使用ClassLoader實例

Publié dans : Technologie
  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

CH17:反射與類別載入器

  1. 1. 1
  2. 2. 反射與類別載入器 學習目標 • 取得.class檔案資訊 • 動態生成物件與操作方法 • 認識模組與反射的權限設定 • 瞭解JDK9類別載入器階層 • 使用ClassLoader實例 2
  3. 3. 運用反射 • JDK9支援模組化 • 開發者在採取模組設計時,如何在不破壞模 組封裝下又能運用反射機制的彈性,就成了 認識反射時必須知道的一大課題 3
  4. 4. Class與.class檔案 • Java真正需要某個類別時才會載入對應 的.class檔案 • java.lang.Class的實例代表Java應用程 式運行時載入的.class檔案 • Class類別沒有公開(public)建構式, 實例是由JVM自動產生 4
  5. 5. Class與.class檔案 • 可以透過Object的getClass()方法,或 者是透過.class常量(Class literal)取得每個 物件對應的Class物件 • 如果是基本型態,也可以使用對應的包裹類 別加上.TYPE取得Class物件 – 如Integer.TYPE可取得代表int的Class物件 – 如果要取得代表Integer.class檔案的Class,則必 須使用Integer.class 5
  6. 6. Class與.class檔案 • 可以操作Class物件的公開方法取得類別基 本資訊 6
  7. 7. Class與.class檔案 • 載入.class檔案的時機 – 使用指定類別生成物件時 – 使用 Class.forName() – 使用java.lang.ClassLoader實例的 loadClass() • 使用類別宣告參考名稱並不會載入.class檔案 7
  8. 8. Class與.class檔案 8
  9. 9. Class與.class檔案 • 編譯時期若使用到相關類別,編譯器會檢查 對應的.class檔案中記載之資訊,以確定是否 可完成編譯 • 執行時期使用某類別時,會先檢查是否有對 應的 Class物件,如果沒有,會載入對應 的.class檔案並生成對應的Class實例 9
  10. 10. Class與.class檔案 • 預設JVM只會用一個Class實例來代表一 個.class檔案(確切說法是,經由同一類別載 入器載入的.class檔案,只會有一個對應的 Class實例) • 每個類別的實例都會知道自己由哪個Class實 例生成。預設使用getClass()或.class取得 的Class實例會是同一個物件 10
  11. 11. 使用Class.forName() • 可以使用Class.forName()方法實現動態 載入類別,可用字串指定類別名稱來獲得類 別相關資訊 11
  12. 12. 使用Class.forName() • Class.forName()另一版本可以讓指定類 別名稱、載入類別時是否執行靜態區塊與類 別載入器: • 如果使用第一個版本的Class.forName() 方法,等同於: 12
  13. 13. 使用Class.forName() 13
  14. 14. 從Class建立物件 • 取得代表建構式的Constructor物件,利 用其newInstance()方法建立類別實例 14
  15. 15. 15
  16. 16. • 指定List初始容量(capacity) • 動態生成長度為10的ArrayList陣列: 16
  17. 17. • 使用Array.set()方法指定索引設值,或 是使用Array.get()方法指定索引取值 17
  18. 18. • 9.1.7中實作過的ArrayList,若現在為其設 計一個toArray()方法: 18
  19. 19. • 拋出java.lang.ClassCastException 19
  20. 20. 從Class獲得資訊 • 取得Class物件後,就可以取得與.class檔案 中記載的的資訊,像是套件、建構式、方法 成員、資料成員等訊息 – java.lang.Package – java.lang.reflect.Constructor – java.lang.reflect.Method – java.lang.reflect.Field – … 20
  21. 21. 21
  22. 22. 22
  23. 23. 23
  24. 24. 24
  25. 25. 操作物件方法與成員 • java.lang.reflect.Method實例是方法 的代表物件,可以使用invoke()方法來動 態呼叫指定的方法 25
  26. 26. 操作物件方法與成員 • 底下會設計一個BeanUtil類別,可以指定 Map物件與類別名稱呼叫getBean()方法, 這個方法會抽取Map內容並封裝為指定類別 的實例 – 例如Map中收集了學生資料,則以下傳回的就是 Student實例 26
  27. 27. 27
  28. 28. 操作物件方法與成員 • 想呼叫受保護的(protected)或私有 (private)方法 28
  29. 29. 操作物件方法與成員 • 可以使用反射機制存取類別資料成員(Field) 29
  30. 30. 操作物件方法與成員 • setAccessible(true)時,如果模組權限 設定上不允許,會拋出 java.lang.reflect.InaccessibleOb jectException • 就目前來說,上面的程式片段中 setAccessible(true)可以用於同一模組 的類別,然而不能用於java.base中的類別 • 因為java.base模組並沒有開放權限 30
  31. 31. 動態代理 • 需要在執行某些方法時進行日誌記錄,你可 能會如下撰寫: 31
  32. 32. 靜態代理 • 在靜態代理實現中,代理物件與被代理物件 必須實現同一介面 32
  33. 33. 靜態代理 • 代理物件同樣也要實現Hello介面 33
  34. 34. 靜態代理 • 可以如下使用代理物件: 34
  35. 35. 動態代理 • 使用動態代理機制,可使用一個處理者 (Handler)代理多個介面的實作物件 35
  36. 36. 36
  37. 37. 動態代理 • 使用LoggingHandler的bind()方法來綁 定被代理物件 37
  38. 38. 當反射遇上模組 • 對於JDK8以及先前版本的JDK,就算開發者 不願意,只要程式庫的使用者運用反射,就 可以訪問類別定義的成員 • 這包括了公開、受保護與私有成員,事實上 ,在Java的生態圈中 • 有不少程式庫或框架,都依賴在這種深層反 射(Deep reflection)機制上運作 38
  39. 39. 當反射遇上模組 • JDK9的模組是為了強封裝,若反射機制依舊 可以這般自由,那麼模組就失去了強封裝的 意義 • 然而,也必須考慮現存程式庫或框架依賴於 深層反射的現況,否則這些程式庫或框架將 不會考慮遷移至JDK9 39
  40. 40. 當反射遇上模組 • 模組的設計者可以決定是否允許反射 • 範例檔labsCH17ReflectModuleR 40
  41. 41. 41
  42. 42. 模組圖 • 一個模組在模組路徑之中,只是表示JVM可 以找到模組描述檔(module-info.class) • 不代表模組圖(Module graph)中有這個模 組,模組圖中不存在的模組,自然就找不到 模組中的類別 42
  43. 43. • 要將模組加入模組圖的方式之一,是在執行 java指令啟動JVM時,使用--add- modules引數加入其他模組作為根模組 • 如果執行java指令指定了--add-modules cc.openhome.reflect 43
  44. 44. • 另一個將指定模組加入模組圖方式是,在模 組描述檔中加入requires設定 • 現有模組會依賴在requires的模組之上 • 現有模組可以讀取(read)該模組,或稱現 在模組對該模組有讀取能力(Readability) 44
  45. 45. 45
  46. 46. 46
  47. 47. exports套件 • 若模組能允許其他模組對指定的公開成員進 行操作,必須在模組描述檔使用exports定義 哪些套件中的類別是可以允許此動作 • 更確切的說法是,指定此模組中哪些套件中 的公開成員可以存取(Accessibility) 47
  48. 48. 48
  49. 49. 49
  50. 50. 50
  51. 51. opens套件或open模組 • 若模組能允許其他模組在反射時,對指定的 非公開成員進行操作,必須在模組描述檔使 用opens定義哪些套件中的類別是可以允許 此動作 51
  52. 52. • 除了使用opens指定要開放的套件之外,也 可以使用 open module,表示開放這個模 組中全部的套件 • 如果使用了open module,那麼module中 的定義就不能再有opens的獨立設定 • 被設定為open module的模組被稱為開放 模組(Open Module) • 沒有open的模組稱為一般模組(Normal module),它們都屬於顯式模組。 52
  53. 53. • java.base模組中的套件使用了exports • 沒有設定opens的套件,也沒有直接open module java.base • 若採取模組化設計,就不允許其他模組在反 射時,對指定的非公開成員進行操作 53
  54. 54. 使用ServiceLoader • 如果Player是種API服務,更實際的場景會 是,Player會是服務的介面之一,它也許是 定義在cc.openhome.api套件之中 • ConsolePlayer是服務的具體實現,可能 定義在cc.openhome.impl套件之中 • MediaMaster是你正在設計的應用程式, 定義在cc.openhome套件之中 • 拆成三個模組 54
  55. 55. 使用ServiceLoader 55
  56. 56. 56
  57. 57. 57
  58. 58. • java.sql模組的module-info.java定義 58
  59. 59. JDK9類別載入器階層 • 在JDK9中,有三種層次的類別載入器負責載 入類別 – System類別載入器(也稱為Application載入器) – Platform類別載入器 – Bootstrap類別載入器 59
  60. 60. JDK9類別載入器階層 • System的父載入器為Platform,而Platform 的父載入器為Bootstrap 60
  61. 61. • System類別載入器可以載入應用程式模組路 徑、類別路徑上的類別 • Platform載入器可以載入整個Java SE 平臺 API、實作類別 • Bootstrap類別載入器是JVM內建的載入器 61
  62. 62. 62
  63. 63. 建立ClassLoader實例 • Bootstrap Loader、Platform Loader與System Loader在程式啟動後,就無法再改變它們的 搜尋路徑 • 可以使用URLClassLoader來產生新的類別 載入器 63
  64. 64. 建立ClassLoader實例 • 由同一類別載入器載入的.class檔案,只會有 一個Class實例 • 如果同一.class檔案由兩個不同的類別載入器 載入,則會有兩份不同的Class實例 64
  65. 65. 65

×