標籤:java java反射 反射api getmethod 反射機制
Java反射機制簡介
通過反射API可以擷取程式在運行時刻的內部結構。反射API中提供的動態代理可以原生實現AOP中的方法攔截功能。通過反射擷取到的Java類內部結構後再進行運用,和直接運用這個類效果相同,但額外的提供了運行時刻的靈活性。反射的最大一個弊端是效能比較差。相同的操作,用反射API所需的時間大概比直接的使用要慢一兩個數量級。可以考慮在適當的時機來使用反射API。
基本用法
Java反射機制主要有兩個作用。第一個主要作用是擷取程式再運行時刻的內部結構。只需要少量的代碼就能便利出一個Java類的內部結構來,其中包括構造方法、聲明的域和定義的方法等。第二個主要的作用是在運行時刻對一個Java類進行操作。可以動態建立一個Java類的對象,擷取某個域的值以及調用某個方法。在Java原始碼中編寫的對類和對象的操作,都能通過反射來實現。
例如現在有一個簡單的Java類:
class MyClass { public int count; public MyClass(int start) { count = start; } public void increase(int step) { count += step; }}
通過一般的做法和反射API來操作類及其對象都非常的簡單。
MyClass myClass = new MyClass(0);myClass.increase(2);System.out.println("Normal -> " + myClass.count);try { Counstructor constructor = MyClass.class.getConstructor(int.class); //擷取構造方法 MyClass myClassReflect = constructor.newInstance(10); //建立對象 Method method = MyClass.class.getMethod("increase", int.class); //擷取方法 Field field = MyClass.class.getField("count"); //擷取域,也就是變數 System.out.println("Reflect -> " + field.getInt(myClassReflect)); //擷取域的值} catch (Exception e) { e.printStackTrace();}
上面的例子中用到了四個反射的API:getConstructor()、newInstance()、getMethod()以及getField()。其中值得說明的是getMethod方法的一些注意事項。看下面的例子:
import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;class Pet {private int age;private String name;public void eat() { System.out.println("eat.....");}private void sound() { System.out.println("sound.....");}}class Dogge extends Pet {private String gender;public void run() { System.out.println("run.....");}@Overridepublic void eat() { System.out.println("eat meat.....");}private void bark() { System.out.println("woo.....");}public Dogge() {}public Dogge(String gender) { this.gender = gender;}}public class JavaTest{public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchMethodException { Class<?> c1 = Dogge.class; Constructor<?>[] c = c1.getConstructors(); Dogge d1 = (Dogge) c[1].newInstance("heheda"); /** * eat方法是繼承類重寫之後的public方法 * 可以通過reflect調用。 */ Method eat = c1.getMethod("eat") ; // eat.invoke(d1); /** * sound方法是子類的private方法,通過普通的訪問渠道調用不了 * 但是通過reflect渠道(像這裡直接reflect是不行的)能夠調用,在下一個例子中將講到。 */ //Method sound = c1.getDeclaredMethod("sound") ; //sound.invoke(d1); /** * bark方法在繼承類中是private的 * 這裡如果用getMethod()方法來reflect該方法,是無法擷取到的,原因在後面 * 注意要用到setAccessible方法,因為是private方法,需要賦予許可權。 */ Method bark = c1.getDeclaredMethod("bark") ; bark.setAccessible(true); bark.invoke(d1); /** * run方法在繼承類中,是public方法 * 直接reflect即可 */ Method run = c1.getMethod("run") ; run.invoke(d1); }}程式結果為: eat meat.....woo.....run.....
上面的例子說明了通過普通的reflect能夠調用到子類public,父類public,private(注意這個要設定響應的許可權setAccessible(true))。下面就說明下為什麼父類的private方法不能直接getMethod方法獲得,而是要通過getDeclaredMethod()方法去反射得到,先來看看官方文檔中對getMethod()的介紹,:
/** * Returns a Method object that reflects the specified public * member method of the class or interface represented by this * Class object. The name parameter is a * String specifying the simple name of the desired method. The * parameterTypes parameter is an array of Class * objects that identify the method‘s formal parameter types, in declared * order. If parameterTypes is null, it is * treated as if it were an empty array. * * Let C be the class represented by this object: * C is searched for any matching methods. If no matching * method is found, the algorithm of step 1 is invoked recursively on * the superclass of C. * If no method was found in step 1 above, the superinterfaces of C * are searched for a matching method. If any such method is found, it * is reflected. * * * To find a matching method in a class C: If C declares exactly one * public method with the specified name and exactly the same formal * parameter types, that is the method reflected. If more than one such * method is found in C, and one of these methods has a return type that is * more specific than any of the others, that method is reflected; * otherwise one of the methods is chosen arbitrarily. * */public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {// be very careful not to change the stack depth of this// checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader()); 兩種方法在這一行出現了分歧 Method method = getMethod0(name, parameterTypes); if (method == null) { throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); } return method;}此處直接調用了native方法getMethod0方法,繼續追蹤代碼就能發現:private Method getMethod0(String name, Class[] parameterTypes) { // Note: the intent is that the search algorithm this routine // uses be equivalent to the ordering imposed by // privateGetPublicMethods(). It fetches only the declared // public methods for each class, however, to reduce the // number of Method objects which have to be created for the // common case where the method being requested is declared in // the class which is being queried. Method res = null; // Search declared public methods 關鍵地方出現了,和下面的getDeclaredMethod方法可以進行對比,問題出現在該方法第一個參數的不同,此處是true,getDeclaredMethod方法此處的參數為false. if ((res = searchMethods(privateGetDeclaredMethods(true), name, parameterTypes)) != null) { return res; } // Search superclass‘s methods if (!isInterface()) { Class c = getSuperclass(); if (c != null) { if ((res = c.getMethod0(name, parameterTypes)) != null) { return res; } } } // Search superinterfaces‘ methods Class[] interfaces = getInterfaces(); for (int i = 0; i < interfaces.length; i++) { Class c = interfaces[i]; if ((res = c.getMethod0(name, parameterTypes)) != null) { return res; } } // Not found return null;}
下面是對getDeclaredMethod()的介紹:
/** * Returns a Method object that reflects the specified * declared method of the class or interface represented by this * Class object. The name parameter is a * String that specifies the simple name of the desired * method, and the parameterTypes parameter is an array of * Class objects that identify the method‘s formal parameter * types, in declared order. If more than one method with the same * parameter types is declared in a class, and one of these methods has a * return type that is more specific than any of the others, that method is * returned; otherwise one of the methods is chosen arbitrarily. * */public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException {// be very careful not to change the stack depth of this// checkMemberAccess call for security reasons // see java.lang.SecurityManager.checkMemberAccess checkMemberAccess(Member.DECLARED, ClassLoader.getCallerClassLoader()); 兩種方法在這一行出現了分歧,這裡調用的是privateGetDeclaredMethods(false),因此我們需要進一步去調查該方法。 Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes); if (method == null) { throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes)); } return method;}
進入了privateGetDeclaredMethods()方法的定義處,其中的代碼如下:
// Returns an array of "root" methods. These Method objects must NOT// be propagated to the outside world, but must instead be copied// via ReflectionFactory.copyMethod.// 接受傳入參數的名稱為publicOnly,因此能明白了為什麼getMethod()方法無法反射出類中的private方法了。private Method[] privateGetDeclaredMethods(boolean publicOnly) { checkInitted(); Method[] res = null; if (useCaches) { clearCachesOnClassRedefinition(); if (publicOnly) { //這個if裡面的代碼就是getMethod()方法調用時所執行的代碼,將所有的public方法都返回了 if (declaredPublicMethods != null) { res = (Method[]) declaredPublicMethods.get(); } } else { //else裡面的方法即在調用getDeclaredMethod方法時所執行的。返回了所有定義的方法。 if (declaredMethods != null) { res = (Method[]) declaredMethods.get(); } } if (res != null) return res; } // No cached value available; request value from VM res = getDeclaredMethods0(publicOnly); if (useCaches) { if (publicOnly) { declaredPublicMethods = new SoftReference(res); } else { declaredMethods = new SoftReference(res); } } return res;}
看到這裡,就應該差不多能弄明白了getMethod()和getDeclaredMethod()方法之間的區別。
由於數組的特殊性,Array類提供了一系列的靜態方法用來建立數組和對其中元素進行操作和訪問。例如如下的操作:
Object array = Array.newInstance(String.class,10); //等價 new String[10]Array.set(array,0,"Hello");Array.set(array, 1, "world");System.out.println(Array.get(array, 0));
使用Java反射API的時候可以繞過Java預設的存取控制檢查,比如可以直接擷取到對象的私人域的值或是調用私人方法。只需要在擷取到Constructor、Field和Method類的對象之後,調用setAccessible方法並設為true即可。有了這種機制,就可以很方便的在運行時刻擷取到程式的內部狀態。
Java反射機制學習筆記