Java語法之反射
一、反射機制
在前面Java語法之註解自定義註解時我們也有提到反射,要獲取類方法和字段的註解信息,必須通過Java的反射技術來獲取 Annotation對象。那什麽是反射呢?JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。它有點類似照妖鏡的作用,不管是什麽妖魔鬼怪(類或對象)都能看到它的真面目(獲取類的屬性方法、調用對象的屬性方法)。
二、Class理解
反射機制可以動態獲取類信息以及調用對象方法,那它是通過什麽實現的呢?這就要介紹下Class類了。首先明確Class也是一個類,只是它是一個描述類的類,它也可以生成對象。對於每個類而言,在JRE中有且僅有一個不變的 Class 類型的對象,而這個Class 類型的對象只能由系統建立,封裝了當前對象所對應的類的信息,有哪些屬性,方法,構造器,實現了哪些接口等等。而且每個類的實例都會記得自己是由哪個Class實例所生成。
那要獲取類信息或調用對象方法,肯定首先要獲取到該類或對象對應的Class類的實例。一般獲取Class對象有三種方式。
1. 通過類名獲取 類名.class
2. 通過對象獲取 對象.getClass()
3. 通過全類名獲取 Class.forName(全類名)
這裏我們可以使用用字符串來做驗證。輸出結果都是class java.lang.String。
package Reflection; public class ReflectionTest { public static void main(String[] args) throws ClassNotFoundException {//字符串的例子 Class clazz = null; //類名.class clazz = String.class; System.out.println(clazz); //對象.getClass() clazz = "ReflectionTest".getClass(); System.out.println(clazz); //Class.forName(全類名) clazz = Class.forName("java.lang.String"); System.out.println(clazz); } }
class java.lang.String class java.lang.String class java.lang.String
上面通過三種方式能獲取到Class實例,然後再了解一下Class類常用的方法
方法名 |
功能說明 |
forName(String name) |
返回指定類名 name 的 Class 對象 |
newInstance() |
調用缺省構造函數,返回該Class對象的一個實例 |
newInstance(Object []args) |
調用當前格式構造函數,返回該Class對象的一個實例 |
getName() |
返回此Class對象所表示的實體(類、接口、數組類、基本類型或void)名稱 |
getSuperClass() |
返回當前Class對象的父類的Class對象 |
getInterfaces() |
獲取當前Class對象的接口 |
getClassLoader() |
返回該類的類加載器 |
getSuperclass() |
返回表示此Class所表示的實體的超類的Class |
getFields() |
獲取類中public類型的屬性 |
getField(String name) |
獲取類特定的方法,name參數指定了屬性的名稱 |
getDeclaredFields() |
獲取類中所有的屬性(public、protected、default、private),但不包括繼承的屬性 |
getDeclaredField(String name) |
獲取類特定的方法,name參數指定了屬性的名稱 |
getConstructors() |
獲取類中的公共方法 |
getConstructor(Class[] params) |
獲取類的特定構造方法,params參數指定構造方法的參數類型 |
getDeclaredConstructors() |
獲取類中所有的構造方法(public、protected、default、private) |
getDeclaredConstructor(Class[] params) |
獲取類的特定構造方法,params參數指定構造方法的參數類型 |
getMethods() |
獲得類的public類型的方法 |
getMethod(String name, Class[] params) |
獲得類的特定方法,name參數指定方法的名字,params參數指定方法的參數類型 |
getDeclaredMethods() |
獲取類中所有的方法(public、protected、default、private) |
getDeclaredMethod(String name, Class[] params) |
獲得類的特定方法,name參數指定方法的名字,params參數指定方法的參數類型 |
三、反射的使用
這裏要著重介紹下上面API的使用,因為在後面要學習的Spring中IOC的原理就是反射加工廠模式。學好反射API有助於理解Spring框架內部實現。為了演示Class方法的使用,在註解demo的基礎上對Person、Student類進行了修改。
Person類:
package Reflection; @CustomDescription(description="基類") @CustomDescription(description="人") public class Person { private String Name; public String getName() { return Name; } public void setName(String name) { Name = name; } public String PersonPublicMethod(String str) { return str; } public Person(String name) { Name = name; } public String PersonPrivateMethod(String str) { return str; } public Person() { super(); } }View Code
Student類:
package Reflection; @CustomDescription(description="學生") @CustomDescription(description="人") public class Student extends Person { public String StudentId; public String getStudentId() { return StudentId; } public void setStudentId(String studentId) { StudentId = studentId; } public String StudentPublicMethod(String str) { return str; } private String StudentPrivateMethod(String str) { return str; } public Student(String name, String studentId) { super(name); StudentId = studentId; } public Student() { } }View Code
一、描述方法Method
描述方法的主要是4個獲取方法getMethods、getMethod、getDeclaredMethods、getDeclaredMethod和1個調用方法invoke。
getMethods:獲取clazz對應類中的所有方法,不能獲取private方法,且獲取從父類繼承來的所有方法包括私有父類的私有方法
getMethod:獲取clazz對應類中指定方法名和參數類型的方法,不能獲取private方法,且獲取從父類繼承來的所有方法包括私有父類的私有方法,因為存在同方法名不同參數這種情況,所以只有同時指定方法名和參數類型才能唯一確定一個方法。
getDeclaredMethods:獲取所有方法,包括私有方法,所有聲明的方法,都可以獲取到,且只獲取當前類的方法。
getDeclaredMethod:獲取clazz對應類中指定方法名和參數類型的方法,包括私有方法,所有聲明的方法,都可以獲取到,且只獲取當前類的方法。
Invoke:執行方法,第一個參數表示執行哪個對象的方法,剩下的參數是執行方法時需要傳入的參數,私有方法的執行,必須在調用invoke之前加上一句method.setAccessible(true);
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class clazz = Class.forName("Reflection.Student"); Method method=null; Method[] methods=null; methods = clazz.getMethods(); for(Method mth:methods){ System.out.print(mth.getName()+" "); } System.out.println(); method = clazz.getMethod("StudentPublicMethod",String.class); System.out.print(method.getName()+" "); System.out.println(); methods = clazz.getDeclaredMethods(); for(Method mth:methods){ System.out.print(mth.getName()+" "); } System.out.println(); method = clazz.getDeclaredMethod("StudentPrivateMethod",String.class); System.out.print(method.getName()+" "); System.out.println(); Object obje = clazz.newInstance(); method.setAccessible(true); String result=(String) method.invoke(obje,"inputParams"); System.out.println(result); }
輸出結果:
StudentPublicMethod setStudentId getStudentId getName setName PersonPrivateMethod PersonPublicMethod wait wait wait equals toString hashCode getClass notify notifyAll StudentPublicMethod StudentPrivateMethod StudentPublicMethod setStudentId getStudentId StudentPrivateMethod inputParams
二、描述字段Filed
描述字段Filed方法的使用和描述方法Method中方法的使用有點類似,也是4個獲取字段的方法:getFields、getField、getDeclaredFields、getDeclaredField。
getFields:獲得某個類的所有的公共(public)的字段,包括父類中的字段。
getField:獲取某個類public成員變量中指定變量名的字段,包括基類。
getDeclaredFields:獲得某個類的所有聲明的字段,即包括public、private和proteced,但是不包括父類的申明字段。
getDeclaredField:獲取某個類的所有成員變量指定變量名的字段,不包括基類。
1.字段獲取
Class clazz = Class.forName("Reflection.Student"); System.out.println("---------getDeclaredFields---------"); Field[] fields = clazz.getDeclaredFields(); for(Field field: fields){ System.out.print(field.getName()+" "); } System.out.println(); System.out.println("---------getFields---------"); fields = clazz.getFields(); for(Field field: fields){ System.out.print(field.getName()+" "); } System.out.println(); System.out.println("---------getDeclaredField---------"); Field field = clazz.getDeclaredField("StudentId"); field.setAccessible(true); System.out.println(field.getName()); System.out.println("---------getField--------"); field = clazz.getField("StudentId"); System.out.println(field.getName());
---------getDeclaredFields--------- StudentId ---------getFields--------- StudentId ---------getDeclaredField--------- StudentId ---------getField-------- StudentId
2.字段的使用
Class clazz = Class.forName("Reflection.Person"); Person person = new Person("CYW"); //獲取私有字段的值 Field field = clazz.getDeclaredField("Name"); //由於是私有字段,需要使用setAccessible(true) field.setAccessible(true); Object val = field.get(person); System.out.println(val); //改變私有字段的值 field.set(person, "ivan"); System.out.println(person.getName());
計劃在未來6個月內寫一本Spring入門的書,本篇和Java語法註解篇都是書的一部分,博友有什麽建議或意見可以留言,我也好在後面的章節進行改進。未完,待續!
Java語法之反射