給定一個Class執行個體,我們可以獲得Constructor(構造器)、Method(方法)和Field(域),而這些對象提供了“通過程式來通路類的成員變量、域類型、方法簽名等資訊”的能力。通過調用Constructor、Method、Field執行個體上的方法,可以:構造底層類的執行個體、調用底層類的方法、通路底層類中的域。例如,Method.invoke 使你可以調用任何類的任何對象上的任何方法(遵從正常的安全限制);反射機制甚至能允許一個類使用另一個類,即使目前者被編譯時後者還根本不存在(執行個體化)。

- 喪失了編譯時類型檢查的好處。(如果程式企圖使用反射通路不存在的或者不可通路的方法,運作時便會失敗抛出異常,除非我們采取特别的預防措施)
- 執行反射通路所需要的代碼非常笨拙和冗長。(反射代碼塊的可閱讀性是非常低的)
- 性能損失。(反射方法調用比普通方法調用慢了許多,具體慢了多少,是受到多個因素的影響的,包括虛拟機被配置設定的記憶體/堆棧使用情況/目前CPU使用情況等等,書作者給了一個大概的說法:速度差異可能小到2倍,也可能大到50倍)

反射應用描述
下面舉個例子:建立一個Set<String> 執行個體,它的類是由第一個指令行參數指定的,然後将剩餘參數插入到集合裡面。
public class ReflectClass { public static void main(String[] args) { Class<?> c1 = null; try { c1 = Class.forName(args[0]); } catch (ClassNotFoundException e) { System.err.println("Class not found."); System.exit(1); } Set<String> s = null; try { s = (Set<String>) c1.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } s.addAll(Arrays.asList(args).subList(1, args.length)); System.out.println(s); } }
輸出結果:
[1,2,3,45]
這個程式像一個“玩偶”,但它示範的這種方法時很強大的:
- 通用的集合測試器,通過侵入式操作一個或多個集合執行個體,并檢查是否遵守Set 接口的約定
- 缺點一:反射的使用可能導緻3個運作時錯誤,如果不使用反射方式的執行個體化,那麼這3個錯誤都會成為編譯時錯誤
- 缺點二:根據類名生成它的執行個體,需要20行冗長的代碼,而調用一個構造器可以非常間接地使用一行代碼。一旦對象被執行個體化,它與其他的Set 執行個體就難以區分。

總結
反射機制是一種功能強大的機制,對于特定的複雜系統程式設計任務,它是非常必要的,但也有一些缺點。如果你編寫的程式必須要與編譯時未知的類一起工作,如有可能,就應該僅僅使用反射機制來執行個體化對象,而通路對象時則使用編譯時已知的某個接口或超類。
—END—
掃描二維碼
擷取技術幹貨