反射反射,程序員的快樂!java
Java反射就是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意方法和屬性;而且能改變它的屬性。而這也是Java被視爲動態(或準動態,爲啥要說是準動態,由於通常而言的動態語言定義是程序運行時,容許改變程序結構或變量類型,這種語言稱爲動態語言。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。)語言的一個關鍵性質。程序員
咱們知道反射機制容許程序在運行時取得任何一個已知名稱的class的內部信息,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,並可於運行時改變fields內容或調用methods。那麼咱們即可以更靈活的編寫代碼,代碼能夠在運行時裝配,無需在組件之間進行源代碼連接,下降代碼的耦合度;還有動態代理的實現等等;可是須要注意的是反射使用不當會形成很高的資源消耗!安全
下面是一個基本的類 Personide
1 package com.ys.reflex; 2 public class Person { 3 //私有屬性 4 private String name = "Tom"; 5 //公有屬性 6 public int age = 18; 7 //構造方法 8 public Person() { 9 } 10 //私有方法 11 private void say(){ 12 System.out.println("private say()..."); 13 } 14 //公有方法 15 public void work(){ 16 System.out.println("public work()..."); 17 } 18 }
①、獲得 Class 的三種方式性能
1 //一、經過對象調用 getClass() 方法來獲取,一般應用在:好比你傳過來一個 Object 2 // 類型的對象,而我不知道你具體是什麼類,用這種方法 3 Person p1 = new Person(); 4 Class c1 = p1.getClass(); 5 6 //二、直接經過 類名.class 的方式獲得,該方法最爲安全可靠,程序性能更高 7 // 這說明任何一個類都有一個隱含的靜態成員變量 class 8 Class c2 = Person.class; 9 10 //三、經過 Class 對象的 forName() 靜態方法來獲取,用的最多, 11 // 但可能拋出 ClassNotFoundException 異常 12 Class c3 = Class.forName("com.ys.reflex.Person");
須要注意的是:一個類在 JVM 中只會有一個 Class 實例,即咱們對上面獲取的 c1,c2,c3進行 equals 比較,發現都是true測試
②、經過 Class 類獲取成員變量、成員方法、接口、超類、構造方法等flex
查閱 API 能夠看到 Class 有不少方法:spa
getName():得到類的完整名字。
getFields():得到類的public類型的屬性。
getDeclaredFields():得到類的全部屬性。包括private 聲明的和繼承類
getMethods():得到類的public類型的方法。
getDeclaredMethods():得到類的全部方法。包括private 聲明的和繼承類
getMethod(String name, Class[] parameterTypes):得到類的特定方法,name參數指定方法的名字,parameterTypes 參數指定方法的參數類型。
getConstructors():得到類的public類型的構造方法。
getConstructor(Class[] parameterTypes):得到類的特定構造方法,parameterTypes 參數指定構造方法的參數類型。
newInstance():經過類的不帶參數的構造方法建立這個類的一個對象。hibernate
咱們經過一個例子來綜合演示上面的方法:代理
1 //得到類完整的名字 2 String className = c2.getName(); 3 System.out.println(className);//輸出com.ys.reflex.Person 4 5 //得到類的public類型的屬性。 6 Field[] fields = c2.getFields(); 7 for(Field field : fields){ 8 System.out.println(field.getName());//age 9 } 10 11 //得到類的全部屬性。包括私有的 12 Field [] allFields = c2.getDeclaredFields(); 13 for(Field field : allFields){ 14 System.out.println(field.getName());//name age 15 } 16 17 //得到類的public類型的方法。這裏包括 Object 類的一些方法 18 Method [] methods = c2.getMethods(); 19 for(Method method : methods){ 20 System.out.println(method.getName());//work waid equls toString hashCode等 21 } 22 23 //得到類的全部方法。 24 Method [] allMethods = c2.getDeclaredMethods(); 25 for(Method method : allMethods){ 26 System.out.println(method.getName());//work say 27 } 28 29 //得到指定的屬性 30 Field f1 = c2.getField("age"); 31 System.out.println(f1); 32 //得到指定的私有屬性 33 Field f2 = c2.getDeclaredField("name"); 34 //啓用和禁用訪問安全檢查的開關,值爲 true,則表示反射的對象在使用時應該取消 java 語言的訪問檢查;反之不取消 35 f2.setAccessible(true); 36 System.out.println(f2); 37 38 //建立這個類的一個對象 39 Object p2 = c2.newInstance(); 40 //將 p2 對象的 f2 屬性賦值爲 Bob,f2 屬性即爲 私有屬性 name 41 f2.set(p2,"Bob"); 42 //使用反射機制能夠打破封裝性,致使了java對象的屬性不安全。 43 System.out.println(f2.get(p2)); //Bob 44 45 //獲取構造方法 46 Constructor [] constructors = c2.getConstructors(); 47 for(Constructor constructor : constructors){ 48 System.out.println(constructor.toString());//public com.ys.reflex.Person() 49 }
父類 Parent.java
1 public class Parent { 2 public String publicField = "parent_publicField"; 3 protected String protectField = "parent_protectField"; 4 String defaultField = "parent_defaultField"; 5 private String privateField = "parent_privateField"; 6 7 }
子類 Son.java
1 public class Son extends Parent { 2 }
測試類:
1 public class ReflectionTest { 2 3 @Test 4 public void testGetParentField() throws Exception{ 5 Class c1 = Class.forName("com.ys.model.Son"); 6 //獲取父類私有屬性值 7 System.out.println(getFieldValue(c1.newInstance(),"privateField")); 8 } 9 10 public static Field getDeclaredField(Object obj,String fieldName) { 11 Field field = null; 12 Class c = obj.getClass(); 13 for(; c != Object.class ; c = c.getSuperclass()){ 14 try { 15 field = c.getDeclaredField(fieldName); 16 field.setAccessible(true); 17 return field; 18 }catch (Exception e){ 19 //這裏甚麼都不要作!而且這裏的異常必須這樣寫,不能拋出去。 20 //若是這裏的異常打印或者往外拋,則就不會執行c = c.getSuperclass(),最後就不會進入到父類中了 21 } 22 } 23 return null; 24 } 25 public static Object getFieldValue(Object object,String fieldName) throws Exception{ 26 Field field = getDeclaredField(object,fieldName); 27 28 return field.get(object); 29 } 30 }
經過執行上述代碼,咱們得到了父類的私有屬性值,這裏要注意的是直接經過反射獲取子類的對象是不能獲得父類的屬性值的,必須根據反射得到的子類 Class 對象在調用 getSuperclass() 方法獲取父類對象,而後在經過父類對象去獲取父類的屬性值。
靈活使用反射能讓咱們代碼更加靈活,這裏好比JDBC原生代碼註冊驅動,hibernate 的實體類,Spring 的 AOP等等都有反射的實現。可是凡事都有兩面性,反射也會消耗系統的性能,增長複雜性等,合理使用纔是真!