概念
- 反射是可以在一個類運作的時候擷取類的資訊的機制,是Java語言的進階特性。
- 為什麼Java語言具有反射機制呢?
- 因為在編寫Java源代碼(*.java)時類的資訊通過編譯器編譯儲存在Class對象中,而這個Class對象是在程式運作時被類加載器(ClassLoader)動态加載。
- .類加載器(ClassLoader)将類對象中檔案(*.class)中的成員變量,構造方法,成員方法等加載到記憶體中,在運作時階段就可以動态地擷取Class對象的資訊以及動态操作Class對象的屬性和方法了。
擷取Class對象的方式
- 要應用反射機制,首先要擷取由ClassLoader将位元組碼檔案裝載進記憶體的class類對象,擷取該對象會有三種方式:
/**
* 獲了類對象的方式
*/
public class GetClassObject {
public static void main(String[] args) throws ClassNotFoundException {
// 第一種擷取方式:Class.forName
Class c1 = Class.forName("javademo202008.Reflect.Person");
System.out.println(c1);
// 第二種擷取方式:類名.class
Class c2 = Person.class;
System.out.println(c2);
// 第三種擷取方式: 對象.getClass
Person person = new Person();
Class c3 = person.getClass();
System.out.println(c3);
// 驗證三種方式擷取的是否是同一對象
System.out.println(c1 == c2 && c1 == c3);
}
}
/**
* 人員類
*/
class Person {
private String name;
private String age;
public Person() {
}
public Person(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
使用Class對象
擷取到了記憶體中的Class類對象,接下來了解Class對象的各個功能 :
- 擷取成員變量
方法 | 用途 |
getFields() | 獲得某個公有的屬性對象 |
getField(String name) | 獲得所有公有的屬性對象 |
getDeclaredField(String name) | 獲得某個屬性對象 |
getDeclaredFields() | 獲得所有屬性對象 |
- 擷取構造方法
方法 | 用途 |
getConstructor(Class…<?> parameterTypes) | 獲得該類中與參數類型比對的公有構造方法 |
getConstructors() | 獲得該類的所有公有構造方法 |
getDeclaredConstructor(Class…<?> parameterTypes) | 獲得該類中與參數類型比對的構造方法 |
getDeclaredConstructors() | 獲得該類所有構造方法 |
- 擷取成員方法
方法 | 用途 |
getMethod(String name, Class…<?> parameterTypes) | 獲得該類某個公有的方法 |
getMethods() | 獲得該類所有公有的方法 |
getDeclaredMethod(String name, Class…<?> parameterTypes) | 獲得該類某個方法 |
getDeclaredMethods() | 獲得該類所有方法 |
- 擷取類名
/**
* 使用Class對象的各個功能
*/
public class ReflectGet {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
// 擷取Person的Class對象
Class c = Person.class;
// 1.擷取所有成員變量
Field[] fields = c.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i]);
}
// 私有成員變量指派
Field nameField= c.getDeclaredField("name");
nameField.setAccessible(true);
Person person = new Person();
nameField.set(person,"Jack");
System.out.println(person.toString());
// 2.擷取全參構造方法
Constructor constructor = c.getConstructor(String.class,String.class);
System.out.println(constructor);
// 建立對象
Object object = constructor.newInstance("Kate","19");
System.out.println(object.toString());
// 3.擷取成員方法
Method setNameMethod = c.getMethod("setName",String.class);
Method setAgeMethod = c.getMethod("setAge", String.class);
// 執行方法
Person person1 = new Person();
setNameMethod.invoke(person1,"Mike");
setAgeMethod.invoke(person1,"12");
System.out.println(person1.toString());
// 4.擷取類名
String className= c.getName();
System.out.println(className);
}
}
反射的應用案例
- 編寫一個程式架構,可以建立任意類的對象,并且執行任意方法
# 定義一個配置檔案 config.properties 進行類名與方法調用的配置
ClassName=javademo202008.Reflect.Person
MethodName=say
# 通過該配置檔案可以不改變代碼,僅改變配置檔案,即可實作類的更換
# ClassName=javademo202008.Reflect.People
# MethodName=say
- 自定義一個人員類
/**
* 人員類
*/
public class Person {
private String name;
private String age;
public Person() {
}
public Person(String name, String age) {
this.name = name;
this.age = age;
}
public void say(){
System.out.println("歡迎使用反射機制");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
- 架構代碼
/**
* 運用反射建構一個架構
*/
public class FrameWork {
public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
// 加載配置檔案
Properties properties = new Properties();
ClassLoader classLoader = FrameWork.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("config.properties");
properties.load(inputStream);
// 擷取配置檔案中定義類名與方法名
String className = properties.getProperty("ClassName");
String methodName = properties.getProperty("MethodName");
// 加載類
Class c = Class.forName(className);
// 建立對象
Object object = c.newInstance();
// 擷取方法對象
Method method = c.getMethod(methodName);
// 通過反射運作
method.invoke(object);
}
}