1. 程式人生 > >Java reflect 反射學習筆記

Java reflect 反射學習筆記

clas parameter 告訴 關鍵字 對象 報錯 getclass exc ==

  1. class 類的使用
  • 萬事萬物皆對象 (基本數據類型, 靜態成員不是面向對象), 所以我們創建的每一個類都是對象, 即類本身是java.lang.Class類的實例對象, 但是這些對象不需要 new 出來, 因為java.lang.Class類的構造方法是私有的;
  • 任何一個類都是Class類的實例對象.這個實例對象有三種表達方式: (我們新建一個Student類)
Class c1 = Student.class; // 實際告訴我們任何一個類都有一個隱含的靜態成員變量class(知道類名時用)
Student stu = new Student();
Class c2 = stu.getClass(); // 已知該類的對象通過getClass方法(知道對象時用)
try {
   Class c3 = Class.forName("com.cnblogs.reflect.Student");// 會有一個ClassNotFoundException異常
} catch (ClassNotFoundException e) {
   e.printStackTrace();
}

ps: 官網解釋說, c1, c2表示 Student類的類類型(class type), 類也是對象, 是Class類的實例對象, 這個對象我們稱之為該類的類類型

這裏有一點值得註意的是: 當我們執行System.out.println(c1 == c2) 時, 結果是true, 這是為什麽? 原因是不管c1, c2都代表了Student類的類類型, 一個類可能是Class類的一個實例對象.

我們完全可以通過類的類類型創建該類的對象實例, 即通過c1或者c2創建Student的實例;

Student stu = (Student) c1.newInstance(); // 前提是Student裏必須要有無參構造方法, 否則會報異常 
  1. 動態加載類
  • 編譯時加載類是靜態加載類
    new 創建對象是靜態加載類, 在編譯時刻就需要加載所有可能使用到的類, 如果有一個缺失, 那麽整個文件都無法通過編譯;
  • 運行時加載類是動態加載類
    Class c = Class.forName(“類全名”), 不僅表示了類的類型, 還表示了動態加載類, 編譯不會報錯, 在運行時才會加載, 使用接口標準能更方便胴體加載類的實現. 功能性的類盡量使用動態加載, 而不用靜態加載.
  1. 獲取方法信息
  • 基本的數據類型, void關鍵字都存在類類型
Class c1 = int.class; // int的類類型
Class c2 = String.class; // String的類類型, 可以理解為編譯生成生成的那個String.class字節碼文件
Class c3 = double.class;
Class c4 = Double.class;
Class c5 = void.class; 
  • Class類的基本API操作

public static void printClassMessage(Object obj) {
        Class c = obj.getClass();
        // 獲取類的類名稱;
        System.out.println("類的名稱是: " + c.getName());
        System.out.println("---------------------------------");

        Method[] methods = c.getMethods();
        for (Method method : methods) {
            // 得到方法返回值類型的類類型
            Class returnType = method.getReturnType();
            System.out.println("returnType = " + returnType.getName());
            // 得到方法的名稱
            System.out.println("method = " + method.getName());
            // 獲取參數類型 -> 得到的是參數列表的類類型
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (Class parameterClass : parameterTypes) {
                System.out.println("parameterClass = " + parameterClass.getName());
            }
            System.out.println("---------------------------------");
        }
}
  1. 獲取成員變量構造函數信息
public static void printFieldMessgae(Object obj) {

        Class<?> c = obj.getClass();
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            // 得到成員變量的類型的類類型
            Class<?> fieldType = field.getType();
            String typeName = fieldType.getName();
            System.out.println("typeName = " + typeName);
            // 得到成員變量的名稱
            String fieldName = field.getName();
            System.out.println("fieldName = " + fieldName);
        }
        /**
         * 獲取構造函數
         */
        Constructor<?>[] constructors = c.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("constructor = " + constructor.getName());
            /**
             * 獲取構造函數的參數的類類型
             */
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("parameterType = " + parameterType.getName());
            }
        }
}
  1. 方法反射的基本操作
  • 如何獲取某個方法
    方法的名稱和方法的參數列表才能決定某個方法
    Method m = c.getDeclaredMethod("方法名", 可變參數列表(參數類型.class))
  • 方法的反射操作
    m.invoke(對象, 參數列表)
    方法如果沒有返回值, 返回null, 如果有返回值, 返回Object,
    然後再強轉為原函數的返回值類型;
  1. 通過反射了解集合泛型的本質
ArrayList list1 = new ArrayList();
ArrayList<String> list2 = new ArrayList();

Class c1 = list1.getClass();
Class c2 = list2.getClass();
 
System.out.println(c1 == c2); // -> true

Ps: 因為反射的操作都是編譯之後的操作, 也就是運行時的操作, c1 == c2返回true, 說明編譯之後集合的泛型是去泛型化的.

那麽我們可以理解為, Java集合中的泛型, 是用於泛指錯誤類型元素輸入的, 比如在list2中我們add一個int,

Java reflect 反射學習筆記