天天看點

Java架構設計的靈魂--反射的基礎知識

概念

  • 反射是可以在一個類運作的時候擷取類的資訊的機制,是Java語言的進階特性。
  • 為什麼Java語言具有反射機制呢?
  • 因為在編寫Java源代碼(*.java)時類的資訊通過編譯器編譯儲存在Class對象中,而這個Class對象是在程式運作時被類加載器(ClassLoader)動态加載。
  • .類加載器(ClassLoader)将類對象中檔案(*.class)中的成員變量,構造方法,成員方法等加載到記憶體中,在運作時階段就可以動态地擷取Class對象的資訊以及動态操作Class對象的屬性和方法了。
  • Java架構設計的靈魂--反射的基礎知識

擷取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對象的各個功能 :

  1. 擷取成員變量
方法 用途
getFields() 獲得某個公有的屬性對象
getField(String name) 獲得所有公有的屬性對象
getDeclaredField(String name) 獲得某個屬性對象
getDeclaredFields() 獲得所有屬性對象
  1. 擷取構造方法
方法 用途
getConstructor(Class…<?> parameterTypes) 獲得該類中與參數類型比對的公有構造方法
getConstructors() 獲得該類的所有公有構造方法
getDeclaredConstructor(Class…<?> parameterTypes) 獲得該類中與參數類型比對的構造方法
getDeclaredConstructors() 獲得該類所有構造方法
  1. 擷取成員方法
方法 用途
getMethod(String name, Class…<?> parameterTypes) 獲得該類某個公有的方法
getMethods() 獲得該類所有公有的方法
getDeclaredMethod(String name, Class…<?> parameterTypes) 獲得該類某個方法
getDeclaredMethods() 獲得該類所有方法
  1. 擷取類名
方法 用途
getName()
/**
 * 使用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);

    }

}