反射API

本文將按如下導圖講述反射的使用

Class對象的獲取

方法一:

Class clazz = test.getClass();

方法二:

Class clazz = Test.class;

方法三:

try {
    Class clazz = Class.forName("com.app.xz.classtest.Test");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

一般使用第三種:

  1. 不依賴Java對象、不用引入包,只需要傳入字符串即可獲取Class對象,可靈活配置。
  2. 即使類加上了@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;

注意點:

  1. 基本數據類型返回數據類型名,類返回包名加類名全路徑。
  2. '['表示數組維度,如二維數組為'[['。
  3. '['后的大寫字母表示當前數據類型關鍵字,映射關系如下所示:
數據類型 關鍵字
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 {

下面通過測試代碼解析getFieldsgetDeclaredFields的區別

測試代碼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) {}

要點:

  1. invoke() 方法中第一個參數 Object 實質上是 Method 所依附的 Class 對應的類的實例,如果這個方法是一個靜態方法,那么 ojb 為 null,后面的可變參數 Object 對應的自然就是參數。

  2. invoke() 返回的對象是 Object,所以實際上執行的時候要進行強制轉換。

  3. 在對 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]

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容