舉個例子,把代碼過程看做去一個目標地點,普通代碼調用呢就是事先知道經緯度,而後你坐直升機直接就到了;而經過反射呢就像不知道具體的地點,只知道先去一個地點,而後前往下一個地點,一步步到達目標。這兩種方法異曲同工,反射由於要「尋路」,因此會慢一些,但在找到目標地點後和直接調用是同樣的。code
有時候咱們須要在程序中建立新的對象或是調用一個方法,而對應的細節咱們事先並不知道,也就是說要在運行中動態地得到類的信息和調用方法。下面介紹如何利用反射來實現。orm
須要瞭解的概念對象
RTTI(RunTime Type Information,運行時類型信息)可以在程序運行時發現和使用類型信息blog
Class對象就保存着運行時類型信息RTTI,表示一個特定類的屬性內存
Class類實際上表示的是一個泛型類Class<?>get
當編譯一個新類時JVM會調用類加載器把這個類加載到內存中域名
類加載器首先會檢查這個類的 Class 對象是否已經加載,若是還沒有加載,默認的類加載器就會根據類名查找 .class 文件it
一旦某個類的 Class 對象被載入內存,它就能夠用來建立這個類的全部對象io
JVM爲每一個類型管理一個Class對象編譯
利用Class類獲得類的信息和實例化一個類
Class.getName()方法
返回類的名字,若是類在一個包中還會加上包名
使用getSimpleName()獲得不帶包名的類名
Class.forName(String className)方法
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)
例程
代碼
//得到域中的值並修改 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());