Java 反射 Reflection

更多 Java 高級知識方面的文章,請參見文集《Java 高級知識》


Java 反射

在運行時:

  • 獲取類的成員變量和成員方法
  • 調用類的成員變量和成員方法
  • 通過構造函數構造一個類的對象

缺點:性能較差

getXX() VS getDeclaredXX():

  • getXX():包括繼承的
  • getDeclaredXX():不包括繼承的

Java 反射的使用

首先通過 Class.forName("") 顯示加載某個類,獲得 Class 對象,然后調用 Class 對象的如下方法:

  • Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
    • 通過參數列表,獲得某個構造方法
    • 隨后可以調用 Constructor 類的 newInstance() 方法來創建對象
  • Method[] getDeclaredMethods()
    • 獲得所有的成員方法
    • Method 類包含如下方法:
      • String getName()
      • Class<?>[] getParameterTypes()
      • Class<?>[] getExceptionTypes()
      • Class<?> getReturnType()
      • Annotation[] getDeclaredAnnotations()
      • Class<?> getDeclaringClass()
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    • 通過方法名和參數列表,獲得某個成員方法
    • 如果只是通過方法名來獲得某個成員方法,則難以處理方法重載的情況
  • Field[] getDeclaredFields()
    • 獲得所有的成員變量
  • Field getDeclaredField(String name)
    • 通過變量名,獲得某個成員變量

使用 Java 反射 API 的時候可以繞過 Java 的訪問控制檢查,可以訪問私有成員變量和私有成員方法,只需在獲取 ConstructorMethodField 之后調用 .setAccessible(true)

示例:

public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void print() {
        System.out.println(name + "-" + age);
    }
}

public static void main(String[] args) throws Exception {
    // 加載類,字節碼轉換為 Class 對象
    Class c = Class.forName("Student");

    // 獲取構造方法并通過構造方法構造對象
    Constructor<Student> constructor = c.getDeclaredConstructor(String.class, int.class);
    Student student = constructor.newInstance("Tom", 18);

    // 獲取字段及對應的值
    Field[] fields = c.getDeclaredFields();
    for (Field field : fields) {
        // 設置可見性
        field.setAccessible(true);

        System.out.println(field.getName() + "=" + field.get(student));
    }

    // 獲取方法及執行方法
    Method[] methods = c.getDeclaredMethods();
    for (Method method : methods) {
        method.invoke(student);
    }
}

反射中對泛型的處理

由于類型擦除機制,泛型中的類型參數在運行時是不存在的,JVM 只看到原始類型。
因此 Java5 在編譯后的 .class 中添加了 Signature 屬性,用來包含不在 JVM 類型系統中的類型信息,提供給反射 API 來使用。
例如:

public class Reflection_Test2 {

    public static void main(String[] args) throws Exception {
        // 加載類,字節碼轉換為 Class 對象
        Class c = Class.forName("advanced.Ref");

        // 獲取字段及對應的值
        Field field = c.getDeclaredField("map");
        Type type = field.getType();
        // 輸出 java.util.HashMap
        System.out.println(type.getTypeName());

        Type genericType = field.getGenericType();
        // 輸出 java.util.HashMap<java.lang.String, java.lang.Integer>
        System.out.println(genericType.getTypeName());

        if (genericType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genericType;

            Type[] actualTypeArguments = pt.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                // 依次輸出 java.lang.String java.lang.Integer
                System.out.println(actualTypeArgument.getTypeName());
            }
        }
    }
}

class Ref {
    private HashMap<String, Integer> map;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容