淺談java中的反射機制
什麼是反射機制?
Java反射機制是在執行過程中藉助Reflection API,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任何一個物件,都能夠呼叫它的任意一個方法,這種動態獲取的資訊以及動態呼叫物件的方法的功能成為java語言的反射機制。
java反射機制提供了那些功能?
- 在執行時判斷任意一個物件所屬的類;
- 在執行時構造任意一個類的物件;
- 在執行時判斷任意一個類所具有的成員變數和方法;
- 在執行時呼叫任意一個物件的成員變數和方法;
- 生成動態代理。
那麼也許就會有一些小朋友問:這個物件屬於哪個類我直接看以下我寫的程式碼不就知道了,為什麼還要使用反射機制呢?那麼接下來我舉一個例子:
public class Demo {
public Object object;
public Demo(Object object){
this.object = object;
}
}
在上面的例子中,我們通過構造引數給Demo類中的Object型別的屬性進行了注入。呼叫Demo類的此構造方法的時候,就必須傳入一個任意型別的物件進來。但是如果這個物件是外部傳入的,並不知道它是什麼型別的,那麼這個時候我們就應該用到了反射。
跟反射相關的API:
- java.lang.Class: 反射的源頭,要想使用java中的反射機制去完成一些事,那麼必須要面對它。我們建立的類通過編譯(javac.exe)之後會生成對應的.class檔案,之後我們執行(java.exe)的時候JVM中的類載入器就會把.class檔案載入至記憶體中。到了記憶體之後,.class檔案就變成了一個執行時類被儲存在快取區中。而這個執行時類本身就是Class類的一個例項。 對於每一個類而言,JRE都為其保留一個不變的Class型別的物件(一個類在JVM中只會有一個Class的例項),其中包含了特定的某個類的相關資訊,我們以後正是通過它來回去此物件所對應的類的資訊。 又此過程可知,每一個類對應的Class型別的物件都是隻能由系統建立。 只有有了Class例項之後才可以建立對應的執行時類的物件;獲取對應的執行時類的完整結構(屬性,方法,構造器,內部類,父類等資訊);呼叫對應的執行時類的指定的結構。
- java.lang.reflect.Method(java.lang.reflect包中儲存著與反射相關的類):提供關於類或者介面上單獨某個方法以及如何訪問該方法的資訊。
- java.lang.reflect.Field:提供有關類或者介面的單個欄位的資訊以及它的動態訪問許可權。反射的欄位可能是一個類的靜態欄位或者例項欄位。
- java.lang.reflect.Constructor:提供關於類的單個構造方法的資訊以及對它的訪問許可權。
獲取執行時類的Class例項的方法:
1. 呼叫執行時類本身的.class屬性;
//得到Person類的Class類的例項 可以理解成clazz在棧中指向了執行時類Person
Class clazz = Person.class;
2. 通過執行時類的物件的getClass( )獲取;
Person person = new Person();
Class clazz = person.getClass();
3. 通過Class類的靜態方法forName( )獲取;
Class clazz = Class.forName("com.demo.Person");//引數為執行時類的類路徑
上面的1,2在編譯器就會報錯,但是3中如果路徑寫錯的話那麼只有在執行時才可以報錯。
得到執行時類的物件:
//通過Class類的例項的到執行時類的物件
Person person = (Person)clazz.newInstance();
Class類中與執行時類的屬性相關方法:
1. getField("")得到執行時類或者其父類中使用public修飾的屬性:
//獲得Person類的屬性名為name的屬性
Field field = clazz.getField("name");//丟擲NoSuchFieldException
//通過屬性為執行時類的例項的相關屬性進行賦值
//丟擲IllegalArgumentException/IllegalAccessException
//第一個引數指明要操作的類的例項,第二個引數指明屬性值
field.set(person,"張三");
2. getDeclaredFields("")獲取執行時類全部的自身的屬性(包括私有化屬性)
Class clazz = Person.class;
Person person = (Person) clazz.newInstance();
Field field = clazz.getDeclaredField("age");
//對私有化的屬性進行操作setAccessible必須為true 預設為false
field.setAccessible(true);
field.set(person, 80);
3. getFields( )獲取執行時類及其父類使用public修飾的全部屬性
Class clazz = Person.class;
//獲取執行時類被Public修飾的全部屬性和其父類中宣告為Public的屬性
Field[] fields = clazz.getFields();
//遍歷陣列,並輸出
for(Field f : fields){
System.out.println(f);
}
輸出結果如下(Person類只有一個使用public修飾的name屬性):
public java.lang.String po.Person.name
4. getDeclaredFields( )獲取執行時類自身的全部屬性
Field[] fields2 = clazz.getDeclaredFields();
for(int i = 0;i<fields2.length;i++){
System.out.println(fields2[i]);
}
輸出結果如下:
public java.lang.String po.Person.name
private int po.Person.age
5. 在Field[ ]中的到屬性的修飾符:
Field[] fields3 = clazz.getDeclaredFields();
for(Field f : fields3){
//每一個許可權修飾符對應一個int型資料 public-->1 private-->2 default-->0
int i = f.getModifiers();
//將Int型資料轉換成許可權修飾符
String string = Modifier.toString(i);
System.out.println(string);
}
Class類中與執行時類的方法相關方法:
Class clazz = Person.class;
Method[] methods = clazz.getDeclaredMethods();
//關於得到方法中的其他資訊。具體看java.lang.reflect.Method API
for(Method method:methods){
System.out.println(method);
}
其輸出結果如下:
public java.lang.String po.Person.toString()
public int po.Person.compareTo(java.lang.Object)
public java.lang.String po.Person.getName()
public void po.Person.setName(java.lang.String)
public void po.Person.setAge(int)
public void po.Person.show()
public int po.Person.getAge()
public void po.Person.display(java.lang.String) throws java.lang.Exception
Class類中與執行時類的構造器相關方法:
Class clazz = Person.class;
Constructor[] constructors = clazz.getDeclaredConstructors();
for(Constructor con : constructors){
System.out.println(con.getName());
}
由上面的方法可以看出,其中的方法名都是大同小異:
- 由get開頭,由Fields,Constructors或者Methods結尾,則表示得到執行時類及其父類的所有的使用public修飾的屬性,構造器或者方法;
- 由get開頭,由Field,Constructor或者Method結尾,就是得到執行時類及其父類的特定的使用public修飾的屬性,構造器或者方法;
- 由getDeclared開頭,則具有得到執行時類的私有化屬性的“權利”。