反射知識在我近期的開發中用到的不過,所以知識點也不是很清楚。今天補習了一下反射部分內容,在這里做一個小結。
Java反射的概念
反射含義:可以獲取正在運行的Java對象。Java反射的功能
1)可以判斷運行時對象所屬的類
2)可以判斷運行時對象所具有的成員變量和方法
3)通過反射甚至可以調用到private的方法
4)生成動態代理-
Java反射機制
- 在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;
對于任意一個對象,都能夠調用它的任意一個方法;
這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。 - Java反射機制主要提供了以下功能:
在運行時判斷任意一個對象所屬的類;
在運行時構造任意一個類的對象;
在運行時判斷任意一個類所具有的成員變量和方法;
在運行時調用任意一個對象的方法;
生成動態代理。
- 在運行狀態中,對于任意一個類,都能夠知道這個類的所有屬性和方法;
實現Java反射的類
1) Class:它表示正在運行的Java應用程序中的類和接口
2) Field:提供有關類或接口的屬性信息,以及對它的動態訪問權限
3) Constructor:提供關于類的單個構造方法的信息以及對它的訪問權限
4) Method:提供關于類或接口中某個方法信息
注意:Class類是Java反射中最重要的一個功能類,所有獲取對象的信息(包括:方法/ 屬性/構造方法/訪問權限)都需要它來實現。編寫Java反射程序的步驟:
1) 必須首先獲取一個類的Class對象
例如(推薦第一種):
Class c1 = Test.class;
Class c2 = Class.forName(“com.reflection.Test”);
Class c3 = new Test().getClass();
2) 然后分別調用Class對象中的方法來獲取一個類的屬性/方法/構造方法的結構
注意:如果要能夠正常的獲取類中方法/屬性/構造方法應該重點掌握如下的反射類
Field
Constructor
Method
常用方法
//首先要獲取一個類的Class對象
Class c1 = TestReflection.class;
或:Class<Bean> c1 = (Class<Bean>) Class.forName(className);
或:Class<Bean> c1 = (Class<Bean>) new Bean().getClass();
//生成一個實例
Bean b = (Bean)c1.newInstance();
//獲取指定的包名
String package01 = c1.getPackage().getName();
//獲取類的修飾符
int mod = c1.getModifiers();
//獲取指定類的完全限定名
String className = c1.getName();
//獲取指定類的父類
Class superClazz = c1.getSuperclass();
//獲取實現的接口
Class[] interfaces = c1.getInterfaces();
//獲取指定類的所有成員變量:(類或接口所聲明的所有字段,public, private, protected ,但不包括從基類繼承的字段)
Field[] fields = c1.getDeclaredFields();
for (Field field : fields) {
//獲取每個字段的訪問修飾符
modifier = Modifier.toString(field.getModifiers());
//獲取字段的數據類型所對應的Class對象
Class type = field.getType();
//獲取字段名
String name = field.getName();
//如果是數組類型則需要特別處理
if (type.isArray()) {
String arrType = type.getComponentType().getName() +"[]";
System.out.println("" + modifier + " " + arrType + " "+ name + ";");
} else {
System.out.println("" + modifier + " " + type + " " + name + ";");
}
}
//獲取指定類的指定成員變量(前者為全部,后者僅為公有,但包含基類)
Field field = c1.getDeclaredField("mScroller");
Field field = c1.getField("mScroller");
實例(獲取指定類的指定成員變量):
初始化ViewPager時,利用獲得指定成員變量,來反射修改滑動速度。
public class ViewPagerScroller extends Scroller {
// 設置滑動速度
private int mScrollDuration = 2000;
public void setScrollDuration(int duration){
this.mScrollDuration = duration;
}
public ViewPagerScroller(Context context) {
super(context);
}
public ViewPagerScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
public ViewPagerScroller(Context context, Interpolator interpolator, boolean flywheel) {
super(context, interpolator, flywheel);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, mScrollDuration);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
super.startScroll(startX, startY, dx, dy, mScrollDuration);
}
public void initViewPagerScroll(ViewPager viewPager) {
try {
//在這里使用了反射,獲得ViewPager類的指定成員變量
Field mScroller = ViewPager.class.getDeclaredField("mScroller");
mScroller.setAccessible(true);
//將ViewPager的實例,即傳入作為形參的viewPager對象,也就是主界面程序中的mViewPager對象,中的成員變量mScroller設置為this
mScroller.set(viewPager, this);
} catch(Exception e) {
e.printStackTrace();
}
}
}
//在主界面程序中使用ViewPager的時候,這樣初始化:
mViewPager = (ShowViewPager) getActivity().findViewById(R.id.viewpager);
ViewPagerScroller viewPagerScroller = new ViewPagerScroller(getActivity());
viewPagerScroller.initViewPagerScroll(mViewPager);
獲取指定類的所有方法的兩種方式:
public Method[] getMethods():返回某個類的所有public方法,包括從基類繼承的、從接口實現的所有public方法。
public Method[] getDeclaredMethods():返回某個類自身聲明的所有方法(public, private, protected),包括從所實現接口的方法,但不包括繼承的方法。
getMethod獲取指定方法的用法舉例:
- 執行某對象的方法
public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
//首先得到這個對象的Class
Class ownerClass = owner.getClass();
//配置參數的Class數組,作為尋找Method的條件
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
//得到要執行的Method
Method method = ownerClass.getMethod(methodName,argsClass);
//執行該Method.invoke方法的參數是執行這個方法的對象owner,和參數數組args。可以這么理解:owner對象中帶有參數args的method方法。返回值是Object,也既是該方法的返回值。
return method.invoke(owner, args);
}
- 執行某個類的靜態方法
public Object invokeStaticMethod(String className, String methodName,
Object[] args) throws Exception {
Class ownerClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName,argsClass);
//invoke的一個參數是null,因為這是靜態方法,不需要借助實例運行
return method.invoke(null, args);
}
解釋一下getMethod(String name, Class<?>... parameterTypes)方法中的兩個參數:
- 第一個參數是方法名,第二個參數是該方法的參數類型數組
- 因為存在同方法名不同參數這種情況,所以只有同時指定方法名和參數類型才能唯一確定一個方法。
如一個函數: int test(int a, String str);
對應的getMethod方法:
getMethod("test",int.class, String.class);
或:getMethod("test",new Class[]{ int.class, String.class } );
method.invoke(Object receiver, Object... args)
就是最后一步:執行改類的指定方法了。
需要注意的是其第二個參數,不同于getMethod的第二個參數。
- 前者是該方法參數的實際的值。
- 后者是該方法參數的實際的值的參數類型。
invoke方法的返回值即為該方法實際的返回值類型,包裝成Object,可以向下強轉。
已上面的例子為例引出一個疑問: