所謂反射就是在程序運行期間,能夠動態獲取到類的屬性和方法,以及動態操作對象的屬性和方法。
反射技術其實應用很廣泛,尤其是各種框架技術都離不開反射,一些常用的 jar 包中間件(比如各個數據庫廠商提供的 JDBC 訪問驅動程序)也使用反射技術。之所以要總結一下反射技術,主要還是為了能夠看懂相關 Java 框架相關源碼,領悟其設計思想的巧妙之處,更好的學習和掌握各種框架。
首先我們先准備一個 Person 類,下面所有的代碼演示,都使用該類,代碼如下:
package com.jobs.reflect;
public class Person {
//公開字段
public String name;
public int age;
//私有字段
private int salary;
//------------------------------
//無參構造函數
public Person() { }
//全參構造函數
public Person(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
//私有構造函數
private Person(String name, int age) {
this.name = name;
this.age = age;
this.salary = 100;
System.out.println("私有構造函數被調用...");
}
//------------------------------
//私有方法
private void method1() {
System.out.println("私有方法 method1 被調用,無參無返回值");
}
public String method2(String name) {
System.out.println("method2 被調用,有參有返回值,參數為" + name);
return "method2 返回值為:" + name + " fastoder...";
}
}
一、獲取一個類的 Class 對象的三種方式
為什么要獲取一個類的 Class 對象?
一個類的 Class 對象,可以理解為模板。
獲取到一個類的 Class 對象后,就相當於獲取到了一個類的模板,然后就可以進行以下操作:
- 獲取 Class 對象(模板)中的任意屬性和方法。即使是私有屬性和方法,也能夠獲取到。
- 通過 Class 對象(模板)創建對象實例,參照模板中的屬性和方法,可以為實例對象的屬性賦值,以及調用其方法
獲取一個類的 Class 模板對象,有三種方式:
- 從文件中獲取,調用 Class.forName(全類名) 方法
- 在內存中獲取,可以調用一個類的 Class 屬性
- 對於一個實例化的對象,可以調用其 getClass() 方法
無論采用哪種方式獲取,獲取到的對象,都是同一個對象。代碼演示如下:
public class reflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
//第 1 種方式獲取 Person 的 Class 類對象
Class cla1 = Class.forName("com.jobs.reflect.Person");
System.out.println(cla1);
//第 2 種方式獲取 Person 的 Class 類對象
Class cla2 = Person.class;
System.out.println(cla2);
//第 3 種方式獲取 Person 的 Class 類對象
Person per = new Person();
Class cla3 = per.getClass();
System.out.println(cla3);
//無論哪種方式獲取,獲取到的都是 Person 的同一個類對象
System.out.println(cla1 == cla2); //true
System.out.println(cla1 == cla3); //true
}
}
二、獲取構造函數並創建實例
通過 getConstructors 方法,可以獲取到所有的 public 修飾的構造方法
通過 getDeclaredConstructors 方法,可以獲取到所有構造方法,包括 private 修飾的構造方法
通過 getConstructor(參數類型...) 方法,可以獲取到 public 修飾的具體的一個構造方法
通過 getDeclaredConstructor(參數類型...) 方法,可以獲取到具體的一個構造方法,包括私有構造方法
通過構造方法對象的 newInstance(參數值...) 方法,可以創建對象實例。
需要注意:如果想使用私有的構造方法對象創建實例對象,需要調用私有構造方法對象的 setAccessible(boolean flag) 方法,並傳入 true 即可。(true 表示取消安全訪問限制)
具體演示代碼如下:
public class reflectDemo2 {
public static void main(String[] args) {
try {
//獲取 Person 的 Class 類對象
Class cla = Class.forName("com.jobs.reflect.Person");
System.out.println("Person類的所有構造方法為:");
//獲取所有的構造方法,包括私有構造方法
Constructor[] constructors = cla.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("--------------------------");
//獲取無參構造方法,創建實例對象
Constructor constructor1 = cla.getConstructor();
Person per1 = (Person) constructor1.newInstance();
System.out.println(per1); //打印實例化對象的地址值
//獲取全參構造方法,創建實例對象
Constructor constructor2 = cla.getConstructor(String.class, int.class, int.class);
Person per2 = (Person) constructor2.newInstance("侯胖胖", 40, 22000);
System.out.println(per2); //打印實例化對象的地址值
//獲取私有構造方法,創建實例對象
Constructor constructor3 = cla.getDeclaredConstructor(String.class, int.class);
//取消安全訪問限制
constructor3.setAccessible(true);
Person per3 = (Person) constructor3.newInstance("任肥肥", 38);
System.out.println(per3); //打印實例化對象的地址值
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/*
打印出的結果如下:
Person類的所有構造方法為:
private com.jobs.reflect.Person(java.lang.String,int)
public com.jobs.reflect.Person(java.lang.String,int,int)
public com.jobs.reflect.Person()
--------------------------
com.jobs.reflect.Person@12edcd21
com.jobs.reflect.Person@34c45dca
私有構造函數被調用...
com.jobs.reflect.Person@52cc8049
*/
三、獲取字段並賦值
通過 getFields() 方法,可以獲取到所有的 public 修飾的字段
通過 getDeclaredFields() 方法,可以獲取到所有字段,包括私有字段
通過 getField(String name) 方法,可以獲取到 public 修飾的具體一個字段
通過 getDeclaredField(String name) 方法,可以獲取到具體一個字段,包括私有字段
通過 set(Object obj, Object value) 方法,給字段賦值,第一個參數是具體的實例化對象
通過 get(Object obj) 方法,獲取具體一個字段的值,該參數是具體的實例化對象
需要注意:如果想要獲取私有字段的值,以及設置私有字段的值,需要調用私有字段對象的 setAccessible(boolean flag) 方法,並傳入 true 即可。(true 表示取消安全訪問限制)
具體演示代碼如下:
public class reflectDemo3 {
public static void main(String[] args) {
try {
//獲取 Person 的 Class 類對象
Class cla = Class.forName("com.jobs.reflect.Person");
System.out.println("Person類的所有字段為:");
Field[] fields = cla.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("--------------------------");
//獲取無參構造方法,創建實例對象
Constructor cst = cla.getConstructor();
Person per = (Person) cst.newInstance();
//給 name 賦值
Field fieldName = cla.getField("name");
fieldName.set(per, "喬豆豆");
//給 age 賦值
Field fieldAge = cla.getField("age");
fieldAge.set(per, 38);
//給私有字段 salary 復制
Field fieldSalary = cla.getDeclaredField("salary");
//取消安全訪問限制
fieldSalary.setAccessible(true);
fieldSalary.set(per, 28000);
//獲取相應字段的值
System.out.println("Person實例化對象的字段值為:");
System.out.println(fieldName.getName() + " ---> " + fieldName.get(per));
System.out.println(fieldAge.getName() + " ---> " + fieldAge.get(per));
System.out.println(fieldSalary.getName() + " ---> " + fieldSalary.get(per));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/*
打印出的結果如下:
Person類的所有字段為:
public java.lang.String com.jobs.reflect.Person.name
public int com.jobs.reflect.Person.age
private int com.jobs.reflect.Person.salary
--------------------------
Person實例化對象的字段值為:
name ---> 喬豆豆
age ---> 38
salary ---> 28000
*/
四、獲取方法並調用方法
通過 getMethods() 方法,可以獲取到所有 public 修飾的方法,包括繼承的方法
通過 getDeclaredMethods() 方法,可以獲取到所有方法,但是不包括繼承的方法
通過 getMethod(方法名, 參數類型...) 方法,可以獲取到具體一個 public 修飾的方法
通過 getDeclaredMethod(方法名, 參數類型...) 方法,可以獲取到具體一個方法,包括私有方法
通過 invoke(Object obj, 參數值...) 方法,可以調用具體一個方法,第一個參數是具體的實例化對象
需要注意:如果想要調用私有方法,需要調用私有方法對象的 setAccessible(boolean flag) 方法,並傳入 true 即可。(true 表示取消安全訪問限制)
具體演示代碼如下:
public class reflectDemo4 {
public static void main(String[] args) {
try {
//獲取 Person 的 Class 類對象
Class cla = Class.forName("com.jobs.reflect.Person");
System.out.println("Person類的所有方法為:");
Method[] methods = cla.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("--------------------------");
//獲取無參構造方法,創建實例對象
Constructor cst = cla.getConstructor();
Person per = (Person) cst.newInstance();
//調用私有方法,私有方法無參數
Method method1 = cla.getDeclaredMethod("method1");
//取消私有方法的安全訪問限制
method1.setAccessible(true);
method1.invoke(per);
//調用有參數,有返回值的共有方法
Method method2 = cla.getMethod("method2", String.class);
String result = (String) method2.invoke(per, "任天蓬");
System.out.println(result);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/*
打印出的結果如下:
Person類的所有方法為:
public java.lang.String com.jobs.reflect.Person.method2(java.lang.String)
private void com.jobs.reflect.Person.method1()
--------------------------
私有方法 method1 被調用,無參無返回值
method2 被調用,有參有返回值,參數為任天蓬
method2 返回值為:任天蓬 fastoder...
*/
到此為止,有關 Java 的一些簡單的反射技術,已經介紹完畢,以上代碼都經過測試無誤,希望能夠對大家有所幫助。