反射

反射概述

 

1 什麼是反射

讓我們從Class類開始瞭解反射!


每個加載到方法區中的class文件都對應一個Class類的對象,你可以把Class類的對象理解爲硬盤上的class文件的對應體。

 

 

2 反射的作用

反射是Java中的高級特性,在各種Java框架中都需要使用反射。所以,就算你將來很長一段時間不使用反射,但你使用的框架都大量使用了反射,所以想深入學習框架,那麼就一定要學習反射。

框架通常通過反射來識別一個對象的“類型信息”。當你傳遞給框架一個對象時,框架會通過反射來了解對象的真實類型(對象實體的類型,而不是引用的類型),這個類型有幾個構造器,有什麼樣的屬性,有什麼樣的方法。還可以通過反射調用構造器,調用方法,對屬性進行讀寫操作。

你可能覺得這沒有什麼神奇的,那是你還沒了解我說的是什麼!你需要再想一想,寫一個方法,參數是Object obj,然後你的方法需要創建一個與參數類型相同的對象出來,還要調用這個對象上的方法。需要注意,參數是Object類型,但用戶調用這個方法時,可能傳遞的不是Object實體對象,它的真實類型有可能是任何類型。

public static void fun(Object obj) {

通過反射創建obj相同類型的對象;

調用obj的方法,調用的方法可以是obj真實類型獨有的方法。而不一定非要Object中的方法。

}

3 Class類都有什麼功能

通過面向對象的思想,猜測一個類中應該有什麼樣的方法了。

一個Student類用來表示學生類型,學生應該有名字,那麼學生類就應該有getName()方法。學生也應該有學號,那麼學生類就應該有getNumber()方法

一個Class類用來表示類類型,類應該有屬性,那麼Class類就應該有getField()方法,但一個類可以有多個屬性,那麼Class類有的就不是getField(),而是getFields()方法,返回值爲Field[]類型。相同的道理,Class類也應該有getMethods()getConstructors()方法,用來返回這個類的所有方法和所有構造器的定義。其實今後我們去學習,很可能就只是想使用一個類的一個方法!然後纔去查找這個類,再去學習我們需要的方法,對於這個類的其他就一無所知了!你很可能在編寫某個項目時會有這樣的想法:某某某方法應該是某某某類的!!!然後你就去查找API

描述學生的類型、描述老師的類型、描述計算機的類型,都是很好理解的,但描述類的類型總是讓人感覺怪怪的,這也是初學者需要習慣的地方。還有,我們在學習過程中稱呼這些類型時也需要注意一下,例如Field表示屬性類,Field類有一個屬性叫name,那麼我們需要怎麼說明這個name屬性呢?“屬性類的name屬性”,這是一個不錯的稱呼!

Class:類的反射對象;

Field:屬性反射對象;

Method:方法反射對象;

Constructor:構造器反射對象;

ClassFieldMethodConstrucator:統稱爲“反射對象”。

 

Class

 

1 反射從Class類開始

要想使用反射,首先你需要得到Class對象,然後才能通過Class對象獲取ConstructorFieldMethod等對象。所有的反射對象都不可能自己來new,說白一點,這些反射對象對應的是class文件上的信息,你怎麼可能自己去new呢?如果可以自己去new一個Class類的對象,那麼是不是就不用我們再去編寫.java文件,然後再通過編譯器去編譯成.class文件了呢?當然這是不可能的!

我們需要思考,Class除了可以返回當前對應類型的所有屬性、方法、構造器的反射對象外,還有什麼功能呢?例如對應類型的類名是什麼?對應類型的父類是誰?對應類型是不是public類,是不是final類。對應類型有沒有可能是個數組類型?有沒有可能是接口類型?有沒有可能是基本類型等等!如果你學會了這樣思考,那麼你今後學習新類是就方便多了!

 

2 得到Class對象

通過對象獲取Class對象:obj.getClass()

你很清楚要使用的Class類型:String.classint.class,只需要在類型的後面添加.class就表示一個Class類型的實例了。

通過類名來獲取Class對象:在只有一個字符串(類名)時使用Class.forName(String className)來獲取Class對象。

Class c1 = “”.getClass();

Class c2 = String.class;

Class c3 = Class.forName(“java.lang.String”);

System.out.println(c1 == c2);

System.out.println(c2 == c3);

 

  上面代碼輸出的都是true,這是因爲一個.class文件,在方法區中只對應一個Class對象。

 

3 加載類

我們已經知道,main()方法是程序的入口。那是不是在main()方法開始執行之前,所有的class文件都已經加載到方法區中了呢?答案是:NO!通常只有需要執行到使用某個類的代碼時,纔會去CLASSPATH中加載class文件,如果程序從頭到尾都沒有使用某個類,那麼這個類對應的class文件就不會被加載到內存。

可以導致一個類被加載可能有:

l 使用一個類的靜態方法;

l 使用一個類的靜態屬性;

l 創建這個類的對象;

l 使用Class.forName()方法加載類;

l 反序列化一個類的對象;

l 加載一個類的子類時,也會加載其父類;

l 加載一個類時,也會加載與該類相關的類。

上面給出的幾個可能也只是可能而已,如果當前類沒有被加載過,纔會去加載,如果已經加載到方法區中了,那麼就不可能再去加載。

 

4.Class類方法

方法1

l String getName():返回類名;

l String getSimpleName():返回簡單類名,不包含包名,但數組類型使用它比較方便;

l Class getSuperClass():獲取父類,Object.class.getSupperClass()返回null

l int getModifiers():獲取類的所有修飾符信息;

 

方法2

l Constructor getConstructor(Class… parameterTypes):通過指定的參數類型獲取公有構造器反射對象;

l Constructor[] getConstructors():獲取所有公有構造器對象;

l Constructor getDeclaredConstructor(Class… parameterTypes):通過指定參數類型獲取構造器反射對象。包含私有構造器對象;

l Constructor[] getDeclaredConstructors():獲取所有構造器對象。包含私有構造器;

l Field getField(String name):通過名字獲取公有屬性反射對象,包含父類中聲明的公有屬性;

l Field[] getFields():獲取所有公有屬性反射對象,包含父類中聲明的公有屬性;

l Field getDeclaredField(String name):通過名字獲取本類中某個屬性,包含本類的private屬性,但父類中聲明的任何屬性都不包含;

l Field[] getDeclaredFields():獲取本類中聲明的所有屬性,包含private屬性,但不包含父類中聲明的任何屬性;

l Method getMethod(String name, Class… parameterTypes):通過方法名和方法參數類型獲取方法反射對象,包含父類中聲明的公有方法,但不包含所有私有方法;

l Method[] getMethods():獲取所有公有方法,包含父類中的公有方法,但不包含任何私有方法;

l Method getDeclaredMethod(String name, Class… parameterTypes):通過方法名和方法參數類型獲取本類中聲明的方法的反射對象,包含本類中的私有方法,但不包含父類中的任何方法;

l Method[] getDeclaredMethods():獲取本類中所有方法,包含本類中的私有方法,但不包含父類中的任何方法。

 

方法3

l boolean isArray():是否爲數組類型;

l boolean isAnnotation():是否爲註解類型;

l boolean isAnnotationPresent(Class annotationClass):當前類是否被annotationClass註解了;

l boolean isEnum():是否爲枚舉類型;

l boolean isInterface():是否爲接口類型;

l boolean isPrimitive():是否爲基本類型;

l boolean isSynthetic():是否爲引用類型;

 

方法4

l T newInstance():使用本類無參構造器來創建本類對象;

 

其他反射類

  其他反射類都在java.lang.reflect包下

1.AccessibleObject

AccessibleObject類是ConstructorMethodField三個類的父類。

l Annotation getAnnotation(Class annotationClass):獲取作用在當前成員上的annotationClass類型的註解對象;

l Annotation[] getAnnotations():獲取作用在當前成員上的所有註解對象;

l boolean isAccessible():判斷當前成員是否可訪問;

l void setAccessible(boolean flag):設置當前成員是否可訪問。

 

2.Construcator

String getName():獲取構造器名;

int getModifiers():獲取構造器上的所有修飾符信息;

Class getDeclaringClass():獲取構造器所屬的類型;

Class[] getParameterTypes():獲取構造器的所有參數的類型;

Class[] getExceptionTypes():獲取構造器上聲明的所有異常類型;

T newInstance(Object… initargs):通過構造器反射對象調用構造器。

 

3.Method

l String getName():獲取方法名;

l int getModifiers():獲取方法上的所有修飾符信息;

l Class getDeclaringClass():獲取方法所屬的類型;

l Class[] getParameterTypes():獲取方法的所有參數的類型;

l Class[] getExceptionTypes():獲取方法上聲明的所有異常類型;

l Class getReturnType():獲取方法的返回值類型;

l Object invode(Object obj, Object… args):通過方法反射對象調用方法,如果當前方法是實例方法,那麼當前對象就是obj,如果當前方法是static方法,那麼可以給obj傳遞nullargs表示是方法的參數;

 

4.Field

l String getName():獲取屬性名;

l int getModifiers():獲取屬性上的所有修飾符信息;

l Class getDeclaringClass():獲取屬性所屬的類型;

l Class getType():獲取當前屬性的類型;

l Object get(Object obj):獲取obj對象的當前屬性值;

l void set(Object obj, Object value):設置obj對象的當前屬性值爲value

l XXX getXXX(Object obj):如果當前屬性爲基本類型,可以使用getXXX()系列方法獲取基本類型屬性值。假如當前屬性爲int類型,那麼可以使用getInt(Object obj)方法獲取obj對象的當前屬性值;

l void setXXX(Object obj, XXX value):如果當前屬性爲基本類型,可以使用setXXX()系統方法基本類型屬性值。假如當前屬性爲int類型,那麼可以使用setInt(Object obj, int value)方法設置obj對象的當前屬性值爲value

 

5.Modifier

Modifier類有一系列的static方法用來解析其他getModifiers()方法返回的int值。

Method m = …

int m = m.getModifiers();

boolean b1 = Modifier.isAbstract(m);//解析m中是否包含abstract修飾

boolean b2 = Modifier.isStatic(m);//解析m中是否包含static修飾

String s = Modifiers.toString(m);//把所有修飾都轉換成字符串

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章