舉個例子,把代碼程序看作去一個目標地點,普通代碼呼叫呢就是事先知道經緯度,然后你坐直升機直接就到了;而通過反射呢就像不知道具體的地點,只知道先去一個地點,然后前往下一個地點,一步步到達目標,這兩種方法殊途同歸,反射因為要“尋路”,所以會慢一些,但在找到目標地點后和直接呼叫是一樣的,
有時候我們需要在程式中創建新的物件或是呼叫一個方法,而對應的細節我們事先并不知道,也就是說要在運行中動態地獲得類的資訊和呼叫方法,下面介紹如何利用反射來實作,
Class類:保存和類有關的資訊的類
-
需要了解的概念
-
RTTI(RunTime Type Information,運行時型別資訊)能夠在程式運行時發現和使用型別資訊
-
Class物件就保存著運行時型別資訊RTTI,表示一個特定類的屬性
-
Class類實際上表示的是一個泛型類Class<?>
-
當編譯一個新類時JVM會呼叫類加載器把這個類加載到記憶體中
-
類加載器首先會檢查這個類的 Class 物件是否已經加載,如果尚未加載,默認的類加載器就會根據類名查找 .class 檔案
-
一旦某個類的 Class 物件被載入記憶體,它就可以用來創建這個類的所有物件
-
-
JVM為每個型別管理一個Class物件
-
-
利用Class類得到類的資訊和實體化一個類
-
Class.getName()方法
-
回傳類的名字,如果類在一個包中還會加上包名
-
使用getSimpleName()得到不帶包名的類名
-
-
Class.forName(String className)方法
- 獲得className類名對應的Class物件
-
Object.newInstance()方法
-
可以用來動態地創建一個類的實體
-
方法呼叫類的默認構造器(沒有引數的構造器),如果沒有默認構造器,就會拋出一個例外
-
如果要呼叫帶引數的構造器,使用Constructor類中的newInstance方法
-
-
例程
-
代碼
System.out.println("靜態創建一個新的物件"); Employee ae = new Employee(); System.out.println(ae); //使用getClass.getName得到類名 String cName = ae.getClass().getName(); //再用forName和newInstance動態創建一個物件 System.out.println("getClass()+forName()動態創建一個新的物件"); Object o = Class.forName(cName).newInstance(); System.out.println(o.getClass());//會輸出實際型別Employee System.out.println(o); //先定義一個類名 再新建物件 String m = "CoreJava.c5_inheritance.Manager"; System.out.println("先定義再forName()動態創建一個新的物件"); Object am = Class.forName(m).newInstance(); System.out.println(am.getClass());//會輸出實際型別Manager System.out.println(am); -
結果

-
-
反射:分析類的能力,相對動態地執行一些方法
-
利用Field類查看任意物件的資料域名稱和型別
-
首先獲得要分析類的Class物件getClass()/forName()
-
getField(String fieldName) 和 getFileds() 能獲取Class物件的對應域(getDeclared(), getDeclaredFields()獲取所有已宣告域,包括私有域)
-
Field.getName()能獲取域名稱,Field.getType()能獲取域型別
-
例程
-
代碼
System.out.println("利用反射獲得所有域"); Manager manager = new Manager(); Class clazz = manager.getClass();//先得到類的運行時資訊 Field[] fields = clazz.getDeclaredFields();//獲得所有宣告的域(包括私有域) for (Field f : fields) System.out.println(f.getType() + " " + f.getName()); -
結果

-
-
-
獲得域中的值并修改
-
Field.get(Object obj)
-
可以獲得obj物件中用Filed物件表示的域值(設 f 是Field的一個實體,表示Manager類中的salary域,那么f.get(m)可以獲得Manager實體m中salary域的值)
-
如果是私有域,需要先設定可訪問標志為true : fild.setAccessible(true)
-
-
Field.set(object obj, Object newValue)
- 用一個新值設定obj物件中Field物件表示的域
-
例程
-
代碼
//獲得域中的值并修改 System.out.println("利用反射獲得域中的值并修改"); System.out.println("使用類方法getSalary():" + manager.getSalary()); Field managerSalaryField = clazz.getDeclaredField("salary");//注意例外處理 managerSalaryField.setAccessible(true);//設定可訪問標志為true,訪問私有域 System.out.println("使用反射獲得salary:" + managerSalaryField.get(manager));//注意例外處理 System.out.println("使用反射修改salary"); managerSalaryField.set(manager, 999); System.out.println("修改后salary:" + manager.getSalary()); -
結果

-
-
-
利用Method類獲得任意方法名稱和回傳值
-
Class.getMethod(String methodName, Class<?>[] paramTypes) 和 Class.getMethods() 分別能獲得類的對應Method物件和所有Method物件
-
Method.getName()獲得方法名
-
Methord.getReturnType()獲得方法回傳型別
-
例程
- 代碼
System.out.println("利用反射獲得所有方法"); Method[] methods = clazz.getMethods(); for (Method method : methods) System.out.println(method.getReturnType().getSimpleName() + " " + method.getName() + " "); - 結果

- 代碼
-
-
呼叫任意方法
-
Method.invoke(Object obj, Object... params)可以呼叫obj物件中Method物件表示的方法,params是方法引數,對于靜態方法第一個引數可以傳入null
-
例程
- 代碼
System.out.println("利用反射呼叫任意方法"); System.out.println("修改前salary:" + manager.getSalary()); Method setManagerSalaryMethod = clazz.getMethod("setSalary", int.class); setManagerSalaryMethod.invoke(manager, 222);//第一個引數為執行物件,靜態方法可傳入null System.out.println("修改后salary:" + manager.getSalary()); - 結果

- 代碼
-
我的疑問
- 利用反射能獲取甚至修改類的私有域,這樣一來私有域不就沒有意義了嗎,在網上搜了一些資料,感徑訓是沒有明白,等到理解了再來補上,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/143473.html
標籤:Java
