Java反射機制,是一個基礎的內容章節,在對於之後的框架學習中起到了至關重要的作用,現在比較流行的是spring
框架 ,其中的IOC(自動注入)以及AOP(動態代理),在AOC中代理模式又分爲動態代理和byteCode
instrument(插樁)或者是CGLIB 。
在學習Java反射機制前,首先問一個問題:在Java運行時,對於任意一個類,能否知道這個類有那些屬性和方法?對
於任意一個對象,能否調用它任意一個方法?
答案是肯定的。可以!
在這裏要去區別一個事情:如果說在自己寫的類中去改一個數據類型或者說屬性,那只是在編譯時,並不是在運行
時。
反射機制的幾大功能:
- 在運行時,判斷任意一個對象所屬類。
- 在運行時,構造任意一個對象。
- 在運行時,判斷任意一個類所具有的成員變量和方法
- 在運行時,調用任意一個對象的方法。
一般而言:程序運行時,允許改變程序結構和變量類型成爲動態語言。由此可見Java並不是動態語言,但是Java有一
個動態相關的機制Reflection,可以運行時加載、探知、使用編譯期間完全未知的classes(但是methods定義不知
道)。所以Reflection是Java視爲動態語言的關鍵性質,允許在程序運行時通過Reflection APIs 取得任何一個已知名
稱的class的內部信息。(包括父類,接口,變量,方法等)。
接下來我們用代碼來舉一個例子。
public class DumpMethods {
//從命令行接受一個字符串(該字符串是某個類的全名)
//打印該類的所有方法
public static void main(String[] args) throws ClassNotFoundException {
//class類是所有反射的入口
Class<?> classType = Class.forName(args[0]);//編譯時不知道args是什麼?
Method[] methods = classType.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
在這裏,我們可以看到,Java虛擬機並不知道你所要傳入的參數是什麼,這個時候我們類的全類型名
稱 Java.lang.String輸入進去,我們可以看到如下圖片
其中,標紅的那一行可以看到,直接將私有的方法也可顯示出來。這一個是非常有用的。那麼這些代碼就可以很明顯
的讓我們知道,在Java運行時,就可以判斷任意一個對象所屬類。
接下來,我們來看一下反射的相關代碼:
public class InvokeTester {
public int add(int param1, int param2) {
return param1 + param2;
}
public String echo(String msg) {
return msg + "hello";
}
public static void main(String[] args) throws Exception {
Class<?> classType = InvokeTester.class; //獲取類對象
Object invokeTester = classType.newInstance();//創建此 Class 對象所表示的類的一個新實例。
//調用前提是 必須有一個不帶參數的構造器
//以上兩行代碼就相當於new invokeTester
Method addmethod = classType.getMethod("add", int.class, int.class);//確定這個方法需要輸入 方法名 參數
Object result = addmethod.invoke(invokeTester, new Object[]{100, 200});
//以上兩行相當於i.add(100,200)
System.out.println((Integer) result);
Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
Object result2 = echoMethod.invoke(invokeTester, "hello world");
System.out.println((String) result2);
}
}
在這裏,着重說獲取類對象時,創建的class對象新的實例時,必須有一個不帶參數的構造器,否則會報錯,那麼怎
沒有應該怎麼辦呢?下面會講到。確認一個方法,我們通過重載機制就可以知道,需要通過這個方法的名字和他的參
數來確認。上一個我們通過classType.getDeclareMethods()來獲取了所有方法,而現在我們可以通過
classType.getMethods()方法來獲得這個我們需要已知方法的名字,在這裏參數類型是int.class,String.class.在之
後,我們通過revoke方法,來獲得我們調用方法的結果,invoke方法中的兩個參數,第一個參數是我們需要調用的對
象,第二個參數就是調用方法的參數,這個參數應該與getmethod方法的參數所寫的類型一致,下面我們來看一下結
果:
我們通過這個例子,就可以發現,通過反射機制我們完全可以調用一個方法。
接下來,整一個複雜點的!
public class ReflectTester {
public Object copy(Object object) throws Exception {
Class<?> classType = object.getClass();
System.out.println(classType.getName());
Object objectCopy = classType.getConstructor(new Class[]{})
.newInstance(new Object[]{});
//相當於是 object object2 = classType.newInstance();
//獲得對象的所有屬性
Field[] fields = classType.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
String fieldName = field.getName();
//獲得屬性的首字母並轉換爲大寫
String firstLetter = fieldName.substring(0, 1).toUpperCase();
//獲得和屬性對應的getxxx()方法
String getMethodName = "get" + firstLetter + fieldName.substring(1);
//獲得個屬性對應的setxxx()方法
String setMethodName = "set" + firstLetter + fieldName.substring(1);
//獲得get方法
Method getMethod = classType.getMethod(getMethodName, new Class[]{});
//獲得set方法
Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()});
Object value = getMethod.invoke(object, new Object[]{});
System.out.println(fieldName + ":" + value);
setMethod.invoke(objectCopy, new Object[]{value});
}
return objectCopy;
}
public static void main(String[] args) throws Exception {
Customer customer = new Customer();
customer.setId(new Long(1));
customer.setName("zhangsan");
customer.setAge(20);
Customer customerCopy = (Customer) new ReflectTester().copy(customer);
System.out.println(customerCopy.getId() + "," + customerCopy.getName() + "," + customerCopy.getAge());
}
}
class Customer {
private long id;
private String name;
private int age;
public Customer() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
以上代碼創造了一個Customer類,並通過反射進行了一次copy,調用getter,setter方法,成功。此處一定要在
Customer類中寫入構造器,否則容易造成權限不通過導致沒有辦法讀取。我們可以看到 Object object copy =
classType.getConstructor(new Class[]{}) .newInstance(new Object[]{});這一行代碼就相當於是object object2 =
classType.newInstance();如果沒有無參構造器時,則可運用這個方法