Java 反射機制原理與用法詳解
本文實例講述了Java 反射機制原理與用法。分享給大家供大家參考,具體如下:
反射反射,程序員的快樂!
1、什么是反射?
Java反射就是在運行狀態(tài)中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調用它的任意方法和屬性;并且能改變它的屬性。而這也是Java被視為動態(tài)(或準動態(tài),為啥要說是準動態(tài),因為一般而言的動態(tài)語言定義是程序運行時,允許改變程序結構或變量類型,這種語言稱為動態(tài)語言。從這個觀點看,Perl,Python,Ruby是動態(tài)語言,C++,Java,C#不是動態(tài)語言。)語言的一個關鍵性質。
2、反射能做什么?
我們知道反射機制允許程序在運行時取得任何一個已知名稱的class的內部信息,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,并可于運行時改變fields內容或調用methods。那么我們便可以更靈活的編寫代碼,代碼可以在運行時裝配,無需在組件之間進行源代碼鏈接,降低代碼的耦合度;還有動態(tài)代理的實現等等;但是需要注意的是反射使用不當會造成很高的資源消耗!
3、反射的具體實現
下面是一個基本的類 Person
package com.ys.reflex; public class Person { //私有屬性 private String name = "Tom"; //公有屬性 public int age = 18; //構造方法 public Person() { } //私有方法 private void say(){ System.out.println("private say()..."); } //公有方法 public void work(){ System.out.println("public work()..."); } }
①、得到 Class 的三種方式
//1、通過對象調用 getClass() 方法來獲取,通常應用在:比如你傳過來一個 Object // 類型的對象,而我不知道你具體是什么類,用這種方法 Person p1 = new Person(); Class c1 = p1.getClass(); //2、直接通過 類名.class 的方式得到,該方法最為安全可靠,程序性能更高 // 這說明任何一個類都有一個隱含的靜態(tài)成員變量 class Class c2 = Person.class; //3、通過 Class 對象的 forName() 靜態(tài)方法來獲取,用的最多, // 但可能拋出 ClassNotFoundException 異常 Class c3 = Class.forName("com.ys.reflex.Person");
需要注意的是:一個類在 JVM 中只會有一個 Class 實例,即我們對上面獲取的 c1,c2,c3進行 equals 比較,發(fā)現都是true
②、通過 Class 類獲取成員變量、成員方法、接口、超類、構造方法等
查閱 API 可以看到 Class 有很多方法:
- getName():獲得類的完整名字。
- getFields():獲得類的public類型的屬性。
- getDeclaredFields():獲得類的所有屬性。包括private 聲明的和繼承類
- getMethods():獲得類的public類型的方法。
- getDeclaredMethods():獲得類的所有方法。包括private 聲明的和繼承類
- getMethod(String name, Class[] parameterTypes):獲得類的特定方法,name參數指定方法的名字,parameterTypes 參數指定方法的參數類型。
- getConstructors():獲得類的public類型的構造方法。
- getConstructor(Class[] parameterTypes):獲得類的特定構造方法,parameterTypes 參數指定構造方法的參數類型。
- newInstance():通過類的不帶參數的構造方法創(chuàng)建這個類的一個對象。
我們通過一個例子來綜合演示上面的方法:
//獲得類完整的名字 String className = c2.getName(); System.out.println(className);//輸出com.ys.reflex.Person //獲得類的public類型的屬性。 Field[] fields = c2.getFields(); for(Field field : fields){ System.out.println(field.getName());//age } //獲得類的所有屬性。包括私有的 Field [] allFields = c2.getDeclaredFields(); for(Field field : allFields){ System.out.println(field.getName());//name age } //獲得類的public類型的方法。這里包括 Object 類的一些方法 Method [] methods = c2.getMethods(); for(Method method : methods){ System.out.println(method.getName());//work waid equls toString hashCode等 } //獲得類的所有方法。 Method [] allMethods = c2.getDeclaredMethods(); for(Method method : allMethods){ System.out.println(method.getName());//work say } //獲得指定的屬性 Field f1 = c2.getField("age"); System.out.println(f1); //獲得指定的私有屬性 Field f2 = c2.getDeclaredField("name"); //啟用和禁用訪問安全檢查的開關,值為 true,則表示反射的對象在使用時應該取消 java 語言的訪問檢查;反之不取消 f2.setAccessible(true); System.out.println(f2); //創(chuàng)建這個類的一個對象 Object p2 = c2.newInstance(); //將 p2 對象的 f2 屬性賦值為 Bob,f2 屬性即為 私有屬性 name f2.set(p2,"Bob"); //使用反射機制可以打破封裝性,導致了java對象的屬性不安全。 System.out.println(f2.get(p2)); //Bob //獲取構造方法 Constructor [] constructors = c2.getConstructors(); for(Constructor constructor : constructors){ System.out.println(constructor.toString());//public com.ys.reflex.Person() }
4、根據反射獲取父類屬性
父類 Parent.java
public class Parent { public String publicField = "parent_publicField"; protected String protectField = "parent_protectField"; String defaultField = "parent_defaultField"; private String privateField = "parent_privateField"; }
子類 Son.java
public class Son extends Parent { } public class ReflectionTest { @Test public void testGetParentField() throws Exception{ Class c1 = Class.forName("com.ys.model.Son"); //獲取父類私有屬性值 System.out.println(getFieldValue(c1.newInstance(),"privateField")); } public static Field getDeclaredField(Object obj,String fieldName) { Field field = null; Class c = obj.getClass(); for(; c != Object.class ; c = c.getSuperclass()){ try { field = c.getDeclaredField(fieldName); field.setAccessible(true); return field; }catch (Exception e){ //這里甚么都不要做!并且這里的異常必須這樣寫,不能拋出去。 //如果這里的異常打印或者往外拋,則就不會執(zhí)行c = c.getSuperclass(),最后就不會進入到父類中了 } } return null; } public static Object getFieldValue(Object object,String fieldName) throws Exception{ Field field = getDeclaredField(object,fieldName); return field.get(object); } }
通過執(zhí)行上述代碼,我們獲得了父類的私有屬性值,這里要注意的是直接通過反射獲取子類的對象是不能得到父類的屬性值的,必須根據反射獲得的子類 Class 對象在調用 getSuperclass() 方法獲取父類對象,然后在通過父類對象去獲取父類的屬性值。
5、反射總結
靈活使用反射能讓我們代碼更加靈活,這里比如JDBC原生代碼注冊驅動,hibernate 的實體類,Spring 的 AOP等等都有反射的實現。但是凡事都有兩面性,反射也會消耗系統(tǒng)的性能,增加復雜性等,合理使用才是真!
更多java相關內容感興趣的讀者可查看本站專題:《Java面向對象程序設計入門與進階教程》、《Java數據結構與算法教程》、《Java操作DOM節(jié)點技巧總結》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對大家java程序設計有所幫助。
相關文章
SpringBoot+JUnit5+MockMvc+Mockito單元測試的實現
今天聊聊如何在 SpringBoot 中集成 Junit5、MockMvc、Mocktio。Junit5 是在 Java 棧中應用最廣的測試框架,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09Spring Boot學習入門之統(tǒng)一異常處理詳解
我們在做Web應用的時候,請求處理過程中發(fā)生錯誤是非常常見的情況。下面這篇文章主要給大家介紹了關于Spring Boot學習入門之統(tǒng)一異常處理的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下。2017-09-09Java類加載器與雙親委派機制和線程上下文類加載器專項解讀分析
類加載器負責讀取Java字節(jié)代碼,并轉換成java.lang.Class類的一個實例的代碼模塊。本文主要和大家聊聊JVM類加載器ClassLoader的使用,需要的可以了解一下2022-12-12java微信企業(yè)號開發(fā)之發(fā)送消息(文本、圖片、語音)
這篇文章主要為大家詳細介紹了java微信企業(yè)號開發(fā)之發(fā)送消息,發(fā)送類型包括文本、圖片、語音,感興趣的小伙伴們可以參考一下2016-06-06