一、定義
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱爲java語言的反射機制。
JAVA有着一個非常突出的動態相關機制:Reflection,用在Java身上指的是我們可以於運行時加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods。
在面向對象的世界裏萬事萬物皆對象,類也是對象,是java.lang.Class的實例對象。我們創建一個類的實例對象可以通過new關鍵字創建比如:User user = new User()。new對象是靜態加載類,在編譯時刻就得加載所有可能使用的類,當我們不想在編譯時刻加載所有的類,而是在運行時候需要哪個就動態的加載哪個比如:Class.froName(“類的全稱”)。
二、用法
(一) 場景
1.在運行時判斷任意一個對象所屬的類。
2.在運行時構造任意一個類的對象。
3.在運行時判斷任意一個類所具有的成員變量和方法。
4.在運行時調用任意一個對象的方法。
5.生成動態代理。
6.獲取泛型信息。
7.獲取註解信息。
8.獲取配置文件信息。
(二) 如何使用
1. Class對象的獲取(類類型,描述其他類所具有的這些特性)。
try{
// 類的.class(最安全/性能最好)屬性(不會自動初始化該Class對象),任何一個類都一個隱含的靜態成員變量
Class c1 = User.class;
// 已知該類的對象通過getClass方法
Class c2 = user.getClass();
// 運用Class.forName(String className)動態加載類,className需要是類的全限定名(最常用)。
Class c3 = Class.forName(“business.User”);
// 我們完全可以通過類的類類型(一個類只有一個類類型)創建該類的實例對象
User user = (User)c1.newInstance()
}catch(ClassNotFoundExpcetion e){
e.printStackTrace();
}
2.從Class類獲取信息(Class類提供了大量的實例方法來獲取該Class對象所對應的詳細信息,Class類大致包含如下方法,其中每個方法都包含多個重載版本) 。
1)Constructor<T>getConstructor(Class<?>... parameterTypes)。
2)Method getMethod(String name,Class<?>... parameterTypes)。
3)Field getField(String name)。
4)Method Constructor Field這些類都實現了java.lang.reflect.Member接口,程序可以通過Method對象來執行相應的方法,通過Constructor對象來調用對應的構造器創建實例,通過Filed對象直接訪問和修改對象的成員變量值。
3.創建對象
1)使用Class對象的newInstance()方法來創建該Class對象對應類的實例(這種方式要求該Class對象的對應類有默認構造器)。
2)先使用Class對象獲取指定的Constructor對象, 再調用Constructor對象的newInstance()方法來創建該Class對象對應類的實例(通過這種方式可以選擇指定的構造器來創建實例)。
4.訪問成員變量
1)通過Class對象的的getField()方法可以獲取該類所包含的全部或指定的成員變量Field,Filed提供瞭如下兩組方法來讀取和設置成員變量值。
PS:getDeclaredXxx方法可以獲取所有的成員變量,無論private/protected。
2)getXxx(Objectobj): 獲取obj對象的該成員變量的值,此處的Xxx對應8中基本類型,如果該成員變量的類型是引用類型,則取消get後面的Xxx。
3)setXxx(Objectobj, Xxx val): 將obj對象的該成員變量值設置成val值.此處的Xxx對應8種基本類型,如果該成員類型是引用類型,則取消set後面的Xxx。
5.調用方法
1)當獲取到某個類對應的Class對象之後,就可以通過該Class對象的getMethod來獲取一個Method數組或Method對象,每個Method對象對應一個方法,在獲得Method對象之後,就可以通過調用invoke方法來調用該Method對象對應的方法。
public class MethodDemo1(){
public static void main(String[] args){
// 要獲取print方法就是獲取類的信息,要先獲取類的類類型
A a1 = new A();
Class c = a1.getClass();
/*
*獲取方法由方法名和參數決定
*getMethod獲取的是public方法
*getDeclaredMethod自己聲明的方法
*/
try{
//Method m = c.getMethod(“print”, new Class[]{int.class, int.class});
Method m = c.getMethod(“print”, int.class, int.class);
// 方法的反射操作是作用於m對象來進行方法的調用達到和a1.print調用的效果相同
// a1.print(10,10);
// 如果方法有參數則放回具體參數,沒有則放null
Object o = m.invoke(a1, 10, 10); // 輸出20
} catch (Exception e){
e.printStackTrace();
}
}
}
class A(){
public void print(int a, int b){
System.out.println(a+b);
}
public void print(String a, String b){
System.out.println(a.toUpperCase()+”,”+b.toLowerCase());
}
}
三、Java反射了解集合泛型的本質
public class MethodDemo2(){
public static void main(String[] args){
ArrayList list = new ArrayList();
ArrayList<String> list1 = new ArrayList<String>();
Class c1 = list.getClass();
Class c2 = list.getClass();
System.out.printlf(c1 == c2);
//反射的操作都是在編譯以後
/*
*返回結果爲true則說明編譯以後集合的泛型是去泛型化的;
*集合的泛型是爲了防止輸入錯誤,java的泛型只在編譯階段有效,編譯以後無效
*/
try{
Method e = c2.getMethod(“add”,Object.class);
e.invoke(list1,10);
System.out.println(list1.size());
//會發現,size=1說明此時泛型失效了,並且注意list1此時不能用for循環遍歷
}catch(Exception e){
e.printStackTrace();
}
}
}