本文將按如下導圖講述反射的使用
Class對象的獲取
方法一:
Class clazz = test.getClass();
方法二:
Class clazz = Test.class;
方法三:
try {
Class clazz = Class.forName("com.app.xz.classtest.Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
一般使用第三種:
- 不依賴Java對象、不用引入包,只需要傳入字符串即可獲取Class對象,可靈活配置。
- 即使類加上了@hide,也能通過這種方式在java虛擬機中去尋找這個類有沒有被加載。
Class對象的使用
通過Class對象獲取類名
class.getName();
示例:
Log.e(TAG, int.class.getName());
Log.e(TAG, String.class.getName());
Log.e(TAG, Test.class.getName());
//數組
int[] is = new int[3];
Log.e(TAG, is.getClass().getName());
String[] ss = new String[3];
Log.e(TAG, ss.getClass().getName());
Test[] ts = new Test[3];
Log.e(TAG, ts.getClass().getName());
//Logcat輸出
int
java.lang.String
com.app.xz.testclass.Test
[I
[Ljava.lang.String;
[Lcom.app.xz.testclass.Test;
注意點:
- 基本數據類型返回數據類型名,類返回包名加類名全路徑。
- '['表示數組維度,如二維數組為'[['。
- '['后的大寫字母表示當前數據類型關鍵字,映射關系如下所示:
數據類型 | 關鍵字 |
---|---|
int | I |
long | J |
short | S |
float | F |
boolean | Z |
byte | B |
char | C |
double | D |
類、接口 | L |
通過Class對象獲取類的修飾符
int modifiers = clazz.getModifiers();
Modifier.toString(modifiers);
Modifier.isPublic(modifiers);
//Modifier.toString的輸出結果如:
public abstract
通過Class對象獲取類的成員變量Field
獲取指定名字的Field
public Field getDeclaredField(String name)
throws NoSuchFieldException,
SecurityException;
public Field getField(String name)
throws NoSuchFieldException,
SecurityException
獲取所有Field
public Field[] getDeclaredFields() throws SecurityException {}
public Field[] getFields() throws SecurityException {
下面通過測試代碼解析getFields
和getDeclaredFields
的區別
測試代碼1:
//Bean類
public class Child {
int a = 0;
private int b = 1;
protected int c = 2;
public int d = 3;
}
//測試代碼
Class clazz = Child.class;
for (Field field : clazz.getFields()) {
Log.e(TAG, field.getName());
}
for (Field field : clazz.getDeclaredFields()) {
Log.e(TAG, field.getName());
}
//getFields輸出
d
//getDeclaredFields輸出
a
b
c
d
測試代碼2:
//Bean類
public class Child extends Father{}
public class Father {
int a = 0;
private int b = 1;
protected int c = 2;
public int d = 3;
}
//測試代碼
Class clazz = Child.class;
for (Field field : clazz.getFields()) {
Log.e(TAG, field.getName());
}
for (Field field : clazz.getDeclaredFields()) {
Log.e(TAG, field.getName());
}
//getFields輸出
d
//getDeclaredFields無輸出
結論:
getFields
獲得所有 public 修飾的成員變量,包括父類。
getDeclaredFields
獲得所有成員變量,但不包括父類。
下面是Field的用法。
通過Field獲取成員變量的類型
public Type getGenericType() {}
public Class<?> getType() {}
區別參照測試代碼
//Bean類
public class Child<T> {
public int a;
public T data;
public List<String> strings;
public Test test;
}
//測試代碼
for (Field field : clazz.getDeclaredFields()) {
Log.e(TAG, field.getName());
Log.e(TAG, field.getGenericType().toString());
Log.e(TAG, field.getType().getName());
Log.e(TAG, "");
}
//輸出結果
a
int
int
data
T
java.lang.Object
strings
java.util.List<java.lang.String>
java.util.List
test
class com.app.xz.testclass.Test
com.app.xz.testclass.Test
通過Field獲取成員變量的修飾符
同class
public int getModifiers() {}
通過Field對成員變量執行get、set
Field是類的抽象成員變量,不涉及對象。
如果要對某對象的成員變量進行讀取和賦值,需要傳入對象。
如'鳥'類有'羽毛'這個成員變量,則'羽毛'代表一個Field。對于鳥A,'羽毛'這個Field是白色;對于鳥B,'羽毛'這個Field是花色。即Field代表類中的一個成員變量,它的取值和賦值需要對象才有意義。
get
public Object get(Object obj);
public int getInt(Object obj);
public long getLong(Object obj)
throws IllegalArgumentException, IllegalAccessException;
public float getFloat(Object obj)
throws IllegalArgumentException, IllegalAccessException;
public short getShort(Object obj)
throws IllegalArgumentException, IllegalAccessException;
public double getDouble(Object obj)
throws IllegalArgumentException, IllegalAccessException;
public char getChar(Object obj)
throws IllegalArgumentException, IllegalAccessException;
public byte getByte(Object obj)
throws IllegalArgumentException, IllegalAccessException;
public boolean getBoolean(Object obj)
throws IllegalArgumentException, IllegalAccessException
set
public void set(Object obj, Object value);
public void setInt(Object obj,int value);
public void setLong(Object obj,long value)
throws IllegalArgumentException, IllegalAccessException;
public void setFloat(Object obj,float value)
throws IllegalArgumentException, IllegalAccessException;
public void setShort(Object obj,short value)
throws IllegalArgumentException, IllegalAccessException;
public void setDouble(Object obj,double value)
throws IllegalArgumentException, IllegalAccessException;
public void setChar(Object obj,char value)
throws IllegalArgumentException, IllegalAccessException;
public void setByte(Object obj,byte b)
throws IllegalArgumentException, IllegalAccessException;
public void setBoolean(Object obj,boolean b)
throws IllegalArgumentException, IllegalAccessException
示例代碼:
try {
Field f = Bird.class.getDeclaredField("feather");
//私有的成員變量,需要通過下面代碼賦予操作權限
f.setAccessible(true);
//傳入test對象以獲取該對象的car成員變量
Feather feather = (Feather) f.get(bird);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
通過Class對象獲取類的方法Method
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
public Method getMethod(String name, Class<?>... parameterTypes)
public Method[] getDeclaredMethods() throws SecurityException
public Method getMethod(String name, Class<?>... parameterTypes)
區別同上,getMethod
獲得包括父類在內的所有 public 修飾的方法,getDeclaredMethods
獲得所有方法,但不包括父類。
示例代碼:
//Bean類
public class Child {
void sayDefault() {
}
private void sayPrivate() {
}
protected void sayProtect() {
}
public void sayPublic() {
}
}
//測試代碼
Class clazz = Child.class;
for (Method method : clazz.getMethods()) {
Log.e(TAG, method.getName());
}
for (Method method : clazz.getDeclaredMethods()) {
Log.e(TAG, method.getName());
}
//getMethods輸出結果
equals
getClass
hashCode
notify
notifyAll
sayPublic
toString
wait
wait
//getDeclaredMethods輸出結果
sayPrivate
sayDefault
sayProtect
sayPublic
通過Method獲取方法名
getName();
通過Method獲取方法參數
public Parameter[] getParameters() {}
public Class<?>[] getParameterTypes() {}
public Type[] getGenericParameterTypes() {}
其中Paramter還可以獲得參數的名字和修飾符:
// 獲取參數名字
public String getName() {}
// 獲取參數類型
public Class<?> getType() {}
// 獲取參數的修飾符
public int getModifiers() {}
使用視情況而定。
通過Method獲取方法返回值類型
// 獲取返回值類型
public Class<?> getReturnType() {}
// 獲取返回值類型包括泛型
public Type getGenericReturnType() {}
通過Method獲取方法異常類型
public Class<?>[] getExceptionTypes() {}
public Type[] getGenericExceptionTypes() {}
通過Method獲取方法修飾符
public int getModifiers() {}
通過Method執行方法
public Object invoke(Object obj, Object... args) {}
要點:
invoke() 方法中第一個參數 Object 實質上是 Method 所依附的 Class 對應的類的實例,如果這個方法是一個靜態方法,那么 ojb 為 null,后面的可變參數 Object 對應的自然就是參數。
invoke() 返回的對象是 Object,所以實際上執行的時候要進行強制轉換。
在對 Method 調用 invoke() 的時候,如果方法本身會拋出異常,那么這個異常就會經過包裝,由 Method 統一拋出 InvocationTargetException。而通過 InvocationTargetException.getCause() 可以獲取真正的異常。
示例代碼:
//Bean類
public class Child {
private static void say(Context context, String text) {
Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
}
}
//測試代碼
Class clazz = Child.class;
try {
Method say = clazz.getDeclaredMethod("say", Context.class, String.class);
say.setAccessible(true);
say.invoke(null, this, "six!");
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
通過反射的方式,可以調用Android一些私有的方法。
通過Class對象獲取構造函數Constructor
參數為Class,表示構造函數參數的類型,無參可不傳。
//獲取指定構造函數
Constructor<T> constructor = class.getConstructor(Class<?>...params);
Constructor<T> constructor = class.getDeclaredConstructor(Class<?>...params);
//獲取所有構造函數
Constructor<?>[] constructors = class.getConstructors();
Constructor<?>[] constructors = class.getDeclaredConstructors();
區別是:
getDeclaredConstructors
可以獲取所有構造函數,但是不包括父類。
getConstructors
可以獲得 public 修飾的所有構造函數,也不包括父類。
這里和Field、Method有所區別,Field、Method是可以獲取到父類的 public 修飾的變量和方法的。
仔細想想:子類本來就可以調用父類的非私有成員變量和方法,但子類不能利用父類的構造函數初始化自己,反射也是基于此。
通過Constructor生成對象實例
獲取對象有倆種方式:
方法一:利用class
class.newInstance();
方法二:利用constructor
constructor.setAccessible(true);
constructor.newInstance(Object...params);
區別是:
class.newInstance()
僅能通過非private的無參構造函數生成對象。
Constructor
上面說過,通過不同API,可以獲取所有的構造函數,并通過構造函數生成對象。
通過Constructor獲取構造函數參數類型
public Parameter[] getParameters() {}
public Class<?>[] getParameterTypes() {}
public Type[] getGenericParameterTypes() {}
通過Constructor獲取構造函數異常類型
public Class<?>[] getExceptionTypes() {}
public Type[] getGenericExceptionTypes() {}
通過Constructor獲取構造函數修飾符
public int getModifiers() {}
TOC
[TOC]