java反射——實現程式的動態性

lengtianxue發表於2016-09-14

轉自http://uuhorse.iteye.com/blog/1706466

① 建立物件
    如果知道一個型別,很容易使用new操作符建立一個類的例項。但是如果在編譯時並不知道具體要例項化的是哪個類的物件,如何建立該例項呢?
    Java中提供Class.forName(String className)從一個字串(含包的類全名稱)載入一個類,再利用newInstance方法建立該類的例項。 

//動態建立類物件  
public static Object createObject(String className) {  
    Object object = null;  
    try {  
        Class classDefinition = Class.forName(className);  
        object = classDefinition.newInstance();  
    } catch (InstantiationException e) {  
        System.out.println(e);  
    } catch (IllegalAccessException e) {  
        System.out.println(e);  
    } catch (ClassNotFoundException e) {  
        System.out.println(e);  
    }  
    return object;  
}

當然,也可以先獲取一個類的Constructor,再呼叫其newInstance方法建立物件。不同的是,constructor物件的newInstance方法需要傳遞一個物件陣列作為構造方法的引數列表。

//使用Constructor動態建立物件  
public static Object createObject(Constructor constructor, Object[] arguments) {  
    System.out.println("Constructor: " + constructor.toString());  
    Object object = null;  
    try {  
        object = constructor.newInstance(arguments);  
        System.out.println("Object: " + object.toString());  
        return object;  
    } catch (InstantiationException e) {  
        System.out.println(e);  
    } catch (IllegalAccessException e) {  
        System.out.println(e);  
    } catch (IllegalArgumentException e) {  
        System.out.println(e);  
    } catch (InvocationTargetException e) {  
        System.out.println(e);  
    }  
    return object;  
} 
② 獲取/設定欄位值
    利用Reflection API獲取或者設定欄位的值,首先要得到Class物件,然後利用Class物件的getField方法取得相應的欄位Field物件,然後呼叫Field物件對應的getXXX/setXXX方法獲取或者設定屬性的值。Filed物件提供getInt/setInt、getLong/setLong等方法對基本型別的屬性進行值的獲取和設定,可以直接使用get/set方法獲取複雜型別屬性的值(返回一個物件值/傳遞一個物件值作為引數)。
    具體操作方法見如下程式碼片段。當然,若試圖獲取/設定一個非public的欄位值(getField方法也不能獲取非pubic的屬性物件,可以嘗試使用getDeclaredField方法),將產生IllegalAccessException,可以通過setAccessible(true)使非public的欄位可見,然後對欄位值進行訪問。

        // 獲取欄位值  
        static void getFiledValue(Object o, String filedName) {  
            Class c = o.getClass();  
            Field filed;  
            Object value;  
            try {  
                filed = c.getField(filedName);  
    //          filed = c.getDeclaredField(filedName);  
    //          filed.setAccessible(true);  //修改欄位訪問許可權  
                value = filed.get(o);   //可使用getInt、getLong等(若知曉欄位基本型別)  
                System.out.println(filedName + ": " + value.toString());  
            } catch (NoSuchFieldException e) {  
                System.out.println(e);  
            } catch (SecurityException e) {  
                System.out.println(e);  
            } catch (IllegalAccessException e) {  
                System.out.println(e);  
            }  
        }  

 // 修改欄位值  
    public static void setFieldValue(Object o, String filedName, Object value) {  
        Field filed;  
        Class c = o.getClass();  
        try {  
            filed = c.getField(filedName);  
//          filed = c.getDeclaredField(filedName);  
//          filed.setAccessible(true);  //修改欄位訪問許可權  
            filed.set(o, value);    //可使用setInt、setLong等(若知曉欄位基本型別)  
        } catch (NoSuchFieldException e) {  
            System.out.println(e);  
        } catch (IllegalAccessException e) {  
            System.out.println(e);  
        }  
    }  


③ 呼叫方法
    方法呼叫的過程類似於設定欄位值的過程,首先要得到Class物件,然後呼叫getMethod方法得到方法Method的物件,getMethod方法要求傳遞兩個引數,一個是方法名,第二個是Class[],即方法引數列表中各引數對應的型別的陣列。然後執行Method物件的invoke方法,進行方法的呼叫,invoke方法需要傳遞兩個引數,第一個是方法繫結的物件,第二個是方法的引數列表。如果是static的方法,則第一個引數將自動被忽略(可為null)。
    同理,對於非public的方法,可以使用getDeclaredMethod方法獲取Method物件,並使用setAccessible設定其可見性。 

   //動態呼叫方法  
    public static Object callMethod(Object o, String methodName,  
            Class paramsType[], Object paramsValue[]) {  
        Object result = null;  
        Class c;  
        Method method;  
        try {  
            c = o.getClass();  
            method = c.getMethod(methodName, paramsType);  
//          method = c.getDeclaredMethod(methodName, paramsType);  
//          method.setAccessible(true);  
            result = method.invoke(o, paramsValue);  
        } catch (NoSuchMethodException e) {  
            System.out.println(e);  
        } catch (IllegalAccessException e) {  
            System.out.println(e);  
        } catch (InvocationTargetException e) {  
            System.out.println(e);  
        }  
        return result;  
    }  




相關文章