三. 重識Java之夯實反射

jasonhww發表於2018-12-21

不忘初心 砥礪前行, Tomorrow Is Another Day !

相關文章

引言

本文概要:

  1. 類的生命週期
  2. 基本類周邊資訊的獲取
  3. 泛型類周邊資訊的獲取
  4. 類內部資訊獲取

一. 類的生命週期

  1. 編譯:生成Class相關檔案
  2. 執行
    • 裝載 - 連結 - 初始化
  • 裝載: 類的裝載是通過類載入器完成的,載入器將.class檔案的二進位制檔案裝入JVM的方法區,並且在堆區建立描述這個類的java.lang.Class物件。用來封裝資料。 但是同一個類只會被類裝載器裝載一次,重要的話:類只裝載一次
  • 連結: 連結就是把二進位制資料組裝為可以執行的狀態。連結分為校驗,準備,解析這3個階段。校驗一般用來確認此二進位制檔案是否適合當前的JVM(版本),準備就是為靜態成員分配記憶體空間,並設定預設值。解析指的是轉換常量池中的程式碼作為直接引用的過程,直到所有的符號引用都可以被執行程式使用(建立完整的對應關係)。
  • 初始化:初始化就是對類中的變數進行初始化值;完成之後,型別也就完成了初始化,初始化之後類的物件就可以正常使用了,直到一個物件不再使用之後,將被垃圾回收。釋放空間。 當沒有任何引用指向Class物件時就會被解除安裝,結束類的生命週期。如果再次用到就再重新開始裝載、連結和初始化的過程。

以上內容摘抄自 blog.csdn.net/harvic88092…

對於上述一大段理論,我們就記住最重要標黑部分.下面開始來學習反射.

獲取類型別

使用示例

    /**
     * 獲取類型別的四種方式
     */
   private void getClassObjectMethod() throws ClassNotFoundException {
       Person person = new Person();
       Class<?> clazz1 = Person.class;
       Class<?> clazz2 = person.getClass();
       Class<?> clazz3 = Class.forName("reflectionDemo.bean.Person");
       Class<?> clazz4 = getClass().getClassLoader().loadClass("reflectionDemo.bean.Person");
       boolean result = (clazz1 == clazz2 && clazz3 == clazz4 && clazz1 == clazz3);
       System.out.println(result);
    }
複製程式碼

Class.forName與ClassLoader.loadClass區別

  • Class.forName(String className)不僅會將類載入進來,而且會對其進行初始化,而ClassLoader.loadClass(String ClassName)則只是將類載入進來,而沒有對類進行初始化
  • 一般來講,他們兩個是通用的,但如果你載入類依賴初始化值的話,那ClassLoader.loadClass(String ClassName)將不再適用!

二. 基本類周邊資訊的獲取相關API


//獲取類型別物件的幾種方式:
Person person = new Person();  
Class a = person.getClass() //方法一
Class b = Persion.class;//方法二
Class c = Class.forName(String ClassName); //方法三
Class d = context.getClassLoader().loadClass(String ClassName);//方法四:(不建議使用)
 
//獲取包名類名
public String getName();//獲取完整的類名(包含包名)
public String getSimpleName();//僅獲取類名
public Package getPackage()//獲取類型別所對應的package物件
 
//獲取超類Class物件
public Class<?> getSuperclass();//獲取普通父類Class物件(不越級,僅直接繼承的類)
public Type getGenericSuperclass();//獲取泛型父類Type物件(見下篇)
 
//獲取介面Class物件
public Class<?>[] getInterfaces();//獲取普通介面的Class物件(不越級,僅直接實現的直接)
public Type[] getGenericInterfaces();//獲取泛型介面的Type(見下篇)
 
//類訪問修飾符
int modifiers = clazz.getModifiers();//獲取類訪問修飾符對應的int變數
String Modifier.toString(int modifiers) //根據整型變數來生成對應的修飾符字串
boolean Modifier.isAbstract(int modifiers)//isXXX()系列函式用以檢查特定的修飾符是否存在
複製程式碼



三. 泛型類周邊資訊的獲取相關API

瞭解泛型類周邊資訊獲取,需要先了解下Type相關的知識.接下來看Type.

//獲取泛型超類的Type
public Type getGenericSuperclass();
//獲取泛型介面的Type
public Type[] getGenericInterfaces();

複製程式碼

3.1 Type

Type的派生類:

  • Class
  • ParameterizedType,代表的是泛型型別形式的
  • TypeVariable,代表的是泛型變數形式的
  • GenericArrayType,代表的是陣列形式的
    • 強調第一次:如果type對應的是表示式是ArrayList這種的,這個type型別應該是ParameterizedType,而不是GenericArrayType,只有類似Integer[]這種的才是GenericArrayType型別。
  • WildcardType,代表的是萬用字元形式的
  • 小結.
    1. 當Type值代表的是完整的泛型表示式,比如:Point<...>,那它對應的型別就是ParameterizedType.
    2. 當Type值代表的是一個確定的類,比如:Integer,String,Double等,那它所對應的型別就是Class;
    3. 當Type值代表的僅僅是Point中用來填充泛型變數T(暫未填充值時),那它所對應的型別就是TypeVariable.
    4. 當Type值代表的是一個String[]、Interge[]等陣列時,那它對應的型別就是GenericArrayType.
    5. 當type所代表的是一個是萬用字元相關的表示式時?,那它對應的型別就是WildcardType.
3.1.1 ParameterizedType相關API
//獲取填充泛型變數的真實引數列表
Type[] getActualTypeArguments();
//獲取宣告此當前泛型表示式的類或介面的Class物件
Type getRawType();

複製程式碼

繼承Type介面只有下面五個:

  • Class
  • ParameterizedType
  • TypeVariable
  • WildcardType
  • GenericArrayType
    所以這也是Type[ ] getActualTypeArguments()方法中Type[]陣列的所有可能取值.
3.1.2 TypeVariable相關
//就是得到當前泛型變數的名稱;
String  getName();
//返回表示此型別變數上邊界的 Type 物件的陣列。如果沒有上邊界,則預設返回Object;
//上邊界的意思就是extends關鍵字後面的限定條件。
Type[]  getBounds();

複製程式碼
3.1.3 GenericArrayType相關

強調第二次,如果type值對應的是類似於ArrayList、List這樣的型別,那type的型別應該是ParameterizedType,而不是GenericArrayType,所以當且僅當type對應的型別是類似於String[]、Integer[]這樣的陣列時,type的型別才是GenericArrayType

//獲取實際陣列值所對應的Type,一般獲取到後進行強制轉換成Class
Type getGenericComponentType();

複製程式碼
3.1.4 WildcardType相關

當type所代表的表示式是型別萬用字元相關的表示式時,比如,,或者等,這個type的型別就是WildcardType!

//獲取萬用字元的上邊界物件列表
//上邊界就是使用extends關鍵定所做的的限定,如果沒有預設是Object
Type[] getUpperBounds();
//獲取萬用字元的下邊界物件列表
//下邊界是指使用super關鍵字所做的限定,如果沒有,則為Null
Type[] getLowerBounds();

複製程式碼
3.1.4.1 萬用字元的使用範圍

再次重複一次有關萬用字元的知識,具體見[夯實泛型]

  • 萬用字元只是泛型變數的填充型別的一種,不能做為泛型變數使用
  • 萬用字元只能用來在生成泛型例項時使用,如Box<?> box;中,其它位置都是不對的.
3.1.4.2 通用型別轉換函式
private void parseClass(Class<?> c){
    parseTypeParameters(c.getGenericInterfaces());
}

private void parseTypeParameters(Type[] types){
    for(Type type:types){
        parseTypeParameter(type);
    }
}

private void parseTypeParameter(Type type){
    if(type instanceof Class){
        Class<?> c = (Class<?>) type;
        Log.d(TAG, c.getSimpleName());
    } else if(type instanceof TypeVariable){
        TypeVariable<?> tv = (TypeVariable<?>)type;
        Log.d(TAG, tv.getName());
        parseTypeParameters(tv.getBounds());
    } else if(type instanceof WildcardType){
        WildcardType wt = (WildcardType)type;
        Log.d(TAG, "?");
        parseTypeParameters(wt.getUpperBounds());
        parseTypeParameters(wt.getLowerBounds());
    } else if(type instanceof ParameterizedType){
        ParameterizedType pt = (ParameterizedType)type;
        Type t = pt.getOwnerType();
        if(t != null) {
            parseTypeParameter(t);
        }
        parseTypeParameter(pt.getRawType());
        parseTypeParameters(pt.getActualTypeArguments());
    } else if (type instanceof GenericArrayType){
        GenericArrayType arrayType = (GenericArrayType)type;
        Type t = arrayType.getGenericComponentType();
        parseTypeParameter(t);
    }
複製程式碼



四. 類內部資訊獲取

4.1 獲取建構函式Constructor相關API

void setAccessible(boolean flag),它代表的含義是,是否將任何函式或欄位設定為可訪問的。如果設定為true,就不管這個函式或者欄位是宣告為private還是public,都是可以訪問的,預設情況下是false,即只有public型別是可訪問的

/**
* 1. 獲取建構函式Constructor
*/
//獲取public型別的建構函式
Constructor<?>[] getConstructors();
Constructor<T> getConstructor(Class<?>... parameterTypes);//根據引數型別,獲取指定的構造的建構函式,屬於精確匹配,引數宣告順序、型別及個數要完全匹配.
//獲取所有型別的建構函式
Constructor<?>[] getDeclaredConstructors();
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)//同上
 
/*
* 2. Constructor之獲取對應類的例項
× @params args 傳入的引數型別、順序及個數都必須與當前的Constructor物件一一對應
*/
public T newInstance(Object... args);
 
/**
* 3. Constructor之獲取引數型別
*/
//用於解析一般函式
Class<?>[] getParameterTypes();
//用於解析泛型物件
Type[] getGenericParameterTypes();
 
 
/***公有的***/ 
// Constructor之獲取訪問修飾符
int getModifiers()
//獲取宣告Constructor的類Class物件;
Class<T> getDeclaringClass()
複製程式碼
4.2 獲取成員變數Field相關API
/**
* 1. 獲取Field物件
*/
//獲取public型別的成員變數
Field[] getFields();
Field getField(String name);//根據變數名,獲取對應的Field物件
//獲取所有型別的成員變數
Field[] getDeclaredFields();
Field getDeclaredField(String name)//同上
 
/**
* 2. get,set方法系列
* 當獲取或設定指定類物件中某變數的值
*/
//object:要設定的類物件(類例項),value:是此Field物件所對應的值
void set(Object object, Object value)
//object:要獲取值的類的物件,返回值Object為:在此類的例項中,對應成員變數的值
Object get(Object object)

/**
 × 3. 針對基本資料型別
 ×/
//設定與獲取int型別的值
void setInt(Object object, int value)
int getInt(Object object)
//設定與獲取double型別的值
void setDouble(Object object, double value)
double getDouble(Object object)
//設定與獲取float型別的值
void setFloat(Object object, float value)
float getFloat(Object object)
//設定與獲取bool型別的值
void setBoolean(Object object, boolean value)
boolean getBoolean(Object object)
//設定與獲取short型別的值
void setShort(Object object, short value)
short getShort(Object object)
//設定與獲取long型別的值
void setLong(Object object, long value)
long getLong(Object object)
//設定與獲取byte型別的值
void setByte(Object object, byte value)
byte getByte(Object object)
//設定與獲取char型別的值
void setChar(Object object, char value)
char getChar(Object object)
 
/**
* 4. 判斷當前field是否為列舉常量;
*/
boolean isEnumConstant()

/***公有的***/
//獲取訪問修飾符
int getModifiers()
//獲取宣告該Field的類class物件
Class<?> getDeclaringClass()
複製程式碼
4.3 獲取成員方法相關API
/*
* 1. 獲取指定類中的成員函式Method物件
*/
//獲取public的成員函式
Method[] getMethods()
Method getMethod(String name, Class<?>... parameterTypes)//name為方法名,parameterTypes為引數型別,精確匹配
//獲取所有型別成員函式
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, Class<?>... parameterTypes)//同上

/*
* 2. 執行某個方法
× @params receicer: 指要執行該函式的類物件(類例項),比如我們的Person類的某一例項
* @params args: 用於傳入該函式宣告中的引數所對應的值的列表
* @return Object: 將函式的結果返回,Object物件就是執行此函式後返回的結果
*/
Object invoke(Object receiver, Object... args)
 
/**
* 3. 獲取引數型別
*/
Class<?>[] getParameterTypes()
Type[] getGenericParameterTypes()
 
/**
* 4. 獲取返回值型別
*/
Class<?> getReturnType()
Type getGenericReturnType()

複製程式碼

由於本人技術有限,如有錯誤的地方,麻煩大家給我提出來,本人不勝感激,大家一起學習進步.

為方便日後查詢,本文只記載了重難點知識API使用,如需詳細瞭解學習.敬請參考,這裡感謝原作者啟艦的部落格對本人的幫助.

相關文章