本文所引用的代碼爲JDK 1.8版本
反射
Java反射中最常使用到的幾個類:Class,Constructor,Method,Field。
- Class:用於獲取類的字節碼對象,獲取構造方法,普通方法,以及屬性。
- Constructor;用於創建對象。
- Method:調用對象的方法和調用類的靜態方法。獲取方法上配置的註解信息。
- Field:修改對象的屬性或類的靜態屬性,獲取對象或類的屬性值。獲取屬性上配置的註解信息。
在某些被限定不能訪問的類,方法或屬性,當我需要創建對象,調用方法或獲取值時,則可以使用反射來繞過這些非public修飾符的限制。
Class
常用到的幾個方法:
獲取字節碼對象
forName(String className)
該方法是靜態方法,用於獲取類的字節碼對象,className爲類的路徑。使用方式
Class<?> clazz = Class.forName("xx.xx.Xxx");
’xx.xx.Xxx‘是類的路徑。
獲取構造器
getConstructors()
獲取字節碼對象中所有public類型的構造方法,返回值爲Constructor
public A(int i, String s){
...
}
則獲取A的該構造方法的方式(clazz爲類A的字節碼對象)
Constructor<?> con = clazzA.getConstructor(int.class, String.class);
getDeclaredMethods()
獲取字節碼對象中所有類型的構造方法,返回值爲Constructor
獲取方法
getMethods()
獲取字節碼對象中public類型的方法,包含public類型的靜態方法,返回值爲Method[]。
getDeclaredConstructors()
獲取字節碼對象中所有類型的方法,返回值爲Method[]。
getMethod(String name, Class
獲取屬性
getFields()
獲取字節碼對象中public類型的屬性,包含public類型的靜態屬性,返回值爲Field[]。
getDeclaredFields()
獲取字節碼對象中所有類型的屬性,返回值爲Field[]。
getField(String name)
獲取指定名稱的public屬性,返回值爲Field。
getDeclaredField(String name)
獲取指定名稱的屬性,private,protected和缺省類型的都能夠獲取,返回值爲Field。
小結
- 獲取所有public類型的構造器,方法或屬性的方式:getXxxs(),返回數組。
- 獲取所有類型的構造器,方法或屬性的方式:getDeclaredXxxs(),返回數組。
- 獲取指定的public類型的構造器,方法或屬性的方式:getXxx()
- 獲取指定的構造器,方法或屬性的方式:getDeclaredXxx()
獲取的方式都比較相似。
Constructor
創建對象
newInstance(Object … initargs)
構造方法,使用該方法來創建對象,其中initargs爲初始化的參數值。Java中的另外一種創建對象的方式是使用new關鍵字。
如果獲取到的構造方法爲public類型,則可以直接調用newInstance方法創建。如果爲private,protected和缺省,則需要先調用setAccessible(true)
,設置其爲可訪問,然後在調用newInstance方法。
獲取註解
getAnnotation(Class annotationClass)
用於獲取構造器上的註解信息,annotationClass爲註解類的class。返回的爲註解類的對象,通過該對象可以獲取註解上的配置信息。
getParameterAnnotations()
用於獲取構造方法中的註解信息。返回值爲Annotation[][]。
Method
調用方法
invoke(Object obj, Object… args)
obj爲創建或獲取到的對象,args爲調用該方法時需要傳入的參數值。如果該Method對象爲非public的則需要先調用setAccessible(true)
,設置爲可訪問,才能使用invoke方法。如果不清楚method訪問限定類型,可以先使用getModifiers()
獲取方法修飾符類型,再使用Modifier.isPublic()
判斷是否爲public類型。如果Method爲靜態方法,則obj可以爲null。
獲取註解
getAnnotation(Class annotationClass)
獲取方法上配置的註解,annotationClass爲註解類的class。返回值爲註解對象。
Field
設置或獲取屬性
set(Object obj, Object value)
設置Field屬性值。obj爲創建或獲取到的對象,value爲設置到該屬性的值。如果該Field對象爲非public的則需要先調用setAccessible(true)
,設置爲可訪問,才能使用invoke方法。如果不清楚method訪問限定類型,可以先使用getModifiers()
獲取方法修飾符類型,再使用Modifier.isPublic()
判斷是否爲public類型。如果Field爲靜態方法,則obj可以爲null。
get(Object obj)
獲取Field屬性值。使用方式同上。
獲取註解
getAnnotation(Class annotationClass)
獲取屬性上配置的註解,annotationClass爲註解類的class。返回值爲註解對象。
小結
反射使用步驟:
1,通過Class.forName()獲取類的字節對象。
2,通過字節對象獲取到構造器Constructor
代理
代理分爲兩種:靜態代理,動態代理。
靜態代理是直接寫在代碼裏,通過一個類去操作需要代理的類。
動態代理則是通過反射和Proxy及相關的類在代碼運行時動態實現。
動態代理大致可以分爲3個步驟:
1,建立委託,即創建Interface(接口)和完成實現類;
2,實現InvocationHandler,在invoke方法內執行代理操作;
3,通過Proxy.newProxyInstance()方法創建代理對象,將代理對象傳遞給調用者。
Proxy
創建代理對象
newProxyInstance(ClassLoader loader, Class
InvocationHandler
這是一個接口,生成代理對象需要傳入該接口實現類的對象。
執行代理
invoke(Object proxy, Method method, Object[] args)
代理對象會調用這個方法來執行被代理的方法,也就是說我們可以在此根據需求對參數args進行修改或添加,或者執行其它的方法。
參數說明:
proxy:爲代理的對象,即通過Proxy.newProxyInstance()生成的。
method:代理對象需要執行的方法。
args:被調用的方法需要傳入的參數值。
測試
接口和實現類:
package com.reflect.test;
public interface Interface {
void method0(String s);
void method1(int i);
}
//修飾符缺省,只能包內訪問
class InterfaceImpl implements Interface{
public InterfaceImpl(){}
@Override
public void method0(String s) {
System.out.println(" s = " + s);
}
@Override
public void method1(int i) {
System.out.println(" i = " + i);
}
}
InvocationHandler實現類和測試代碼
package com.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.reflect.test.Interface;
public class ProxyTest {
public static void main(String[] args) {
try {
//獲取InterfaceImpl字節碼對象
Class<?> clazz = Class.forName("com.reflect.test.InterfaceImpl");
Constructor<?> cons = clazz.getConstructor();
//設置爲可訪問
cons.setAccessible(true);
//創建對象
Object obj = cons.newInstance();
//創建代理對象
Interface interf = (Interface) Proxy.newProxyInstance(
clazz.getClassLoader(),
new Class<?>[]{Interface.class},
new InterfaceIH(obj));
//代理對象調用方法
interf.method0("call method0");
interf.method1(1);
clazz = Proxy.getProxyClass(clazz.getClassLoader(), new Class<?>[]{Interface.class});
System.out.println(clazz.getCanonicalName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
//InvocationHandler實現類
class InterfaceIH implements InvocationHandler{
//目標對象
Object target;
public InterfaceIH(Object obj){
target = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String name = method.getName();
//被調用的方法名稱
System.out.println("call method: " + name);
//傳入的參數信息
if(args != null && args.length > 0){
System.out.println("arg = " + args[0]);
}
//修改參數
if("method0".equals(name)){
args[0] = "proxy " + args[0];
}else if("method1".equals(name)){
args[0] = 10 + (Integer)args[0];
}
return method.invoke(target, args);
}
}
測試結果:
call method: method0
arg = call method0
s = proxy call method0
call method: method1
arg = 1
i = 11
com.sun.proxy.$Proxy0
參考博客:
公共技術點之 Java 動態代理
http://a.codekk.com/detail/Android/Caij/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20Java%20%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86
深度剖析JDK動態代理機制
http://www.cnblogs.com/MOBIN/p/5597215.html