一、概述
反射:指程序可以訪問,檢測和修改它本身狀態或行爲的一種能力,並能根據自身行爲的狀態和結果,調整或修改應用所描述行爲的狀態和相關的語義。也就是可以獲取正在運行的Java對象。
反射作爲Java中一個強大的工具,不僅可以很方便創建靈活的代碼,而且對一些其他第三方代碼可以進行增強。
其主要功能主要有:
1、可以判斷運行時對象所屬的類
2、可以判斷運行時對象所擁有的成員屬性和方法
3、生成動態代理
光看概念有些晦澀,我們先說一個簡單的應用反射的例子:
public class Item {
private String id;
private String name;
private double price;
public Item(String id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public String getId() { return id; }
public String getName() { return name; }
public double getPrice() { return price; }
}
上面這部分代碼不管出於什麼目的,其成員屬性只在new對象時接收,其他情況一律修改不了。我們可以通過反射來修改類的私有屬性。
public static void main(String[] args) throws Exception {
Item item = new Item("1", "a", 1.0);
Field field = item.getClass().getDeclaredField("id");
field.setAccessible(true);
field.set(item, "2");
System.out.println(item.getId()); // 2
}
運行可以發現,我們已經把其對象的id值更新到了2。
二、具體功能實現
1、獲取類並創建其對象:反射中獲取類通常有三種方法
Class clz1 = Class.forName("java.lang.String"); // 1.通過包和類名
Class clz2 = String.class; // 2.直接通過類名
String str = new String();
Class clz3 = str.getClass(); // 3.根據對象(運行時類)
clz1.newInstance();
clz2.newInstance();
其他方法名 | 含義 |
---|---|
getEnclosingClass() | 返回底層類的立即封閉類 |
getDeclaredClasses() | 返回 Class 對象的一個數組,這些對象反映聲明爲此 Class 對象所表示的類的成員的所有類和接口 |
getDeclaringClass() | 如果此 Class 對象所表示的類或接口是另一個類的成員,則返回的 Class 對象表示該對象的聲明類 |
2、獲取類的屬性
方法名 | 含義 |
---|---|
getFields() | 獲取類的所有public屬性,包括其父類 |
getField() | 獲取某一個屬性 |
getDeclaredFields() | 獲取類的所有聲明的字段,不包括父類 |
getDeclaredField() | 獲取某一個屬性 |
3、獲取類的方法
方法名 | 含義 |
---|---|
getMethods() | 獲取類的所有public方法,包括其父類 |
getMethod() | 獲取某一個方法 |
getDeclaredMethods() | 獲取類的所有聲明的方法,不包括父類 |
getDeclaredMethod() | 獲取某一個方法 |
getConstructors() | 獲取訪問權限是public的所有構造方法 |
getConstructor() | 獲取某一個構造方法 |
getDeclaredConstructors() | 獲取類的所有構造方法 |
getConstructors() | 獲取某一個構造方法 |
4、總結
- 反射裏的Class, Field, Method, Constructor必須結合對象使用,除非是靜態的。
- 獲取非public對象須用類似getDeclaredMethod()而不是getMethod()。
- Field和Method都能設置accessible(true),之後能訪問到私有權限。
三、一個簡單的例子
下面是一個Person類,其中有兩個私有屬性id和name;擁有一個構造方法和一個私有方法print;
- 還有一個public內部類BaseInfo:含有兩個私有屬性nation和bloodType;擁有一個構造方法和一個私有方法print;
- 還有一個private內部類FamilyInfo:含有兩個私有屬性nativePlace和address,擁有一個構造方法和一個私有方法print。
由於FamilyInfo爲私有的,因此爲了可以初始化其屬性,在Person中添加了一個私有屬性,並在構造方法中對其進行了初始化。
public class Person {
private String id;
private String name;
private FamilyInfo familyInfo;
public Person(String id, String name) {
this.id = id;
this.name = name;
this.familyInfo = this.new FamilyInfo("beijing", "beijing");
}
private void print() {
System.out.println("Person{id=" + id + ", name=" + name + "}");
}
public class BaseInfo {
private String nation;
private String bloodType;
public BaseInfo(String nation, String bloodType) {
this.nation = nation;
this.bloodType = bloodType;
}
private void print() {
System.out.println("BaseInfo{nation=" + nation + ", bloodType=" + bloodType + "}");
}
}
private class FamilyInfo {
private String nativePlace; // 籍貫
private String address; // 住址
private FamilyInfo(String nativePlace, String address) {
this.nativePlace = nativePlace;
this.address = address;
}
private void print() {
System.out.println("FamilyInfo{nativePlace=" + nativePlace + ", address=" + address + "}");
}
}
}
下面的Main方法中,初始化了Person類,通過一個getFiledFromPerson()
方法打印出其私有屬性、私有方法、內部類私有屬性及方法。
public class Main {
private static void getFiledFromPerson(Person person, Person.BaseInfo baseInfo) {
try {
// 獲取外部類的私有屬性
Field idField = person.getClass().getDeclaredField("id");
idField.setAccessible(true);
String id = (String) idField.get(person);
System.out.println("id:" + id);
Field familyInfoField = person.getClass().getDeclaredField("familyInfo");
familyInfoField.setAccessible(true);
Object familyInfo = familyInfoField.get(person);
// 獲取外部類的私有方法
Method printPersonMethod = person.getClass().getDeclaredMethod("print");
printPersonMethod.setAccessible(true);
printPersonMethod.invoke(person);
// 獲取內部類的私有屬性
Class baseInfoClz = Class.forName("demo.Person$BaseInfo");
Field nationField = baseInfoClz.getDeclaredField("nation");
nationField.setAccessible(true);
String nation = (String) nationField.get(baseInfo);
System.out.println("nation:" + nation);
Class familyInfoClz = Class.forName("demo.Person$FamilyInfo");
Field field = familyInfoClz.getDeclaredField("address");
field.setAccessible(true);
String address = (String) field.get(familyInfo);
System.out.println("address:" + address);
// 獲取內部類的私有方法
Method printBaseInfoMethod = baseInfoClz.getDeclaredMethod("print");
printBaseInfoMethod.setAccessible(true);
printBaseInfoMethod.invoke(baseInfo);
Method printFamilyInfoMethod = familyInfoClz.getDeclaredMethod("print");
printFamilyInfoMethod.setAccessible(true);
printFamilyInfoMethod.invoke(familyInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Person person = new Person("1001", "John"); // 初始化Person以及默認FamilyInfo
Person.BaseInfo baseInfo = person.new BaseInfo("han", "A"); // 初始化BaseInfo
getFiledFromPerson(person, baseInfo);
}
}