反射快速入門

北冥有鱼要继续奋斗發表於2024-07-18

反射就是透過位元組碼檔案獲取類的成員變數、構造方法和成員方法的所有資訊。
利用反射,我們可以獲取成員變數的修飾符、名字、型別、取值。我們可以獲取構造方法的名字、形參,並利用透過反射獲取的構造方法建立物件。我們可以獲取成員方法的修飾符、名字、形參、返回值、丟擲的異常、註解,並執行透過反射獲取的方法。

比如idea中的自動提示就是透過反射獲取的,idea透過反射獲取該物件的所有能呼叫的方法,並將它顯示出來,又比如idea中函式的形參提示也是透過反射獲取的。

反射是透過位元組碼檔案物件獲取成員變數、成員方法、構造方法的所有資訊,所以,我們先要獲取位元組碼檔案物件,再去從位元組碼檔案物件中獲取成員變數、構造方法和成員方法,最後再進行解剖獲取所有資訊。

獲取class物件的3種方式:

(1)class.forName("全類名");
(2)類名.class;
(3)物件.getClass();

那我們應該如何選擇呢?

建立一個類的物件,我們需要經歷以下3個階段:

  • 原始碼階段:Java->class,在這個階段,虛擬機器是沒有把程式碼載入到記憶體當中的,全都是硬碟中進行操作。在這個階段用第一種方式獲取class位元組碼檔案物件。第一種方式最為常用。
  • 載入階段:把位元組碼檔案載入到記憶體中。這個階段使用第二種方式來獲取位元組碼檔案物件。第二種方式通常都是當作引數進行傳遞。
  • 執行階段:在該階段使用第三種方式。當我們已經有了這個類的物件時才可以使用第三種方式。

利用反射獲取構造方法:

Constructor<?>[] getConstructors()//返回所有公共構造方法物件的陣列
Constructor<?>[] getDeclaredConstructors()//返回所有構造方法物件的陣列
Constructor<T> getConstructor(Class<?>..parameterTypes)//返回單個公共構造方法物件
Constructor<T> getDeclaredConstructor(Class<?>.. parameterTypes)//返回單個構造方法物件
T newInstance(Object... initargs)//根據指定的構造方法建立物件
setAccessible(boolean flag)//設定為true可以繞過訪問控制許可權,使得 private 屬性或方法也可以被訪問。

//setAccessible(boolean flag)使用示例
public class Example {
    private String privateField = "privateValue";
}
public class AnotherClass {
    public static void main(String[] args) throws Exception {
        Example instance = new Example();
        Field field = Example.class.getDeclaredField("privateField");
        field.setAccessible(true);
        System.out.println("Private Field Value: " + field.get(instance)); // 可以透過反射訪問私有欄位
    }
}

Class類中用於獲取成員變數的方法:

Field[] getFields()//返回所有公共成員變數物件的陣列
Field[] getDeclaredFields()//返回所有成員變數物件的陣列
Field getField(String name)//返回單個公共成員變數物件
Field getDeclaredField(Stringname)//返回單個成員變數物件
void set(Object obj, Object value)//給成員變數賦值
Object get(Object obj)//獲取成員變數的值。
setAccessible(boolean flag)//使用 setAccessible(true) 可以繞過訪問控制許可權,使得 private 屬性或方法也可以被訪問。

//set,get示例
public class Example {
    private String name;
    private int age;

    public static void main(String[] args) throws Exception {
        Example example = new Example();

        Field nameField = Example.class.getDeclaredField("name");
        Field ageField = Example.class.getDeclaredField("age");

        // 設定欄位值
        nameField.set(example, "John Doe");
        ageField.set(example, 30);

        // 獲取欄位值
        System.out.println("Name: " + nameField.get(example));
        System.out.println("Age: " + ageField.get(example));
    }
}

Class類中用於獲取成員方法的方法:

Method[] getMethods()//返回類中所有公共成員方法物件的陣列,包括繼承的
Method[] getDeclaredMethods()//返回類中所有成員方法物件的陣列,不包括繼承的
Method getMethod(String name, Class<?>... parameterTypes)//返回單個公共成員方法物件
Method getDeclaredMethod(String name, Class<?>... parameterTypes)//返回單個成員方法物件
Object invoke(Object obj,Object... args)//執行方法
//引數一:用obj物件呼叫該方法
//引數二:呼叫方法的傳遞的引數(如果沒有就不寫)
//返回值:方法的返回值(如果沒有就不寫)
setAccessible(boolean flag)//使用 setAccessible(true) 可以繞過訪問控制許可權,使得 private 屬性或方法也可以被訪問。

//invoke使用示例:
public class Example {
    public void greet(String name) {
        System.out.println("Hello, " + name + "!");
    }

    public static void main(String[] args) throws Exception {
        Example example = new Example();
        Method method = Example.class.getMethod("greet", String.class);

        // 呼叫 greet 方法
        method.invoke(example, "John");
    }
}

注意:獲取公共方法時也會獲取父類中的所有公共方法。獲取所有許可權的方法時,就不會獲取父類的方法