標籤:java反射
最近的學習發現在很多方面,基礎知識掌握的還很不牢固,所以對於架構、知識點等屬於那種問啥啥知道,做啥啥不出來的那種類型。前些日子,老師一直在抓基礎,做什麼都要從最簡單的demo開始,只有懂了原理之後再去用一些高深的東西如架構等才會理解的更深刻。現在首先需要理解的就是基本上每個Java架構都在用的反射技術。
要想理解反射,首先得瞭解類的載入過程,看:
我們的原始碼經過編譯之後變成位元組碼,然後在JVM中運行時通過類載入器載入位元組碼在記憶體中產生Class類對象,這個Class類對象內包含有field對象(類的成員變數產生)、constructor對象(類的構造方法產生)和method對象(類的方法產生)。當我們拿到一個類或者對象的時候就可以通過反射對它們進行操作,下面再來看反射:
- 什麼是反射
- Java反射主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力,是Java被視為動態(或准動態)語言的一個關鍵性質。這個機制允許程式在運行時透過Reflection APIs取得任何一個已知名稱的class的內部資訊,包括其modifiers(諸如public, static 等)、superclass(例如Object)、實現之interfaces(例如Cloneable),也包括fields和methods的所有資訊,並可於運行時改變fields內容或喚起methods。Java反射機制容許程式在運行時載入、探知、使用編譯期間完全未知的classes。換言之,Java可以載入一個運行時才得知名稱的class,獲得其完整結構。
- 反射用在哪兒
- 多用於架構和組件,寫出複用性高的通用程式。
- 如struts的form只要有了form對象和 property名字就可以利用反射給property賦值和取值 對這類操作 一個方法就可以搞定。如果hibernate不用欄位進行反射映射 那麼每個HQL的編譯和結果處理 將無法進行等等。
- 怎麼用
- 針對我們所知的不同情況分別有3種方法擷取Class位元組碼對象
- 當已知類名的時候,通過 “類名.class”獲得
- 當已知對象的時候,通過 “對象.getClass”獲得
- 當已知包括包名在內的完整類名(假設為String格式)的時候,可通過 “Class.forName(String)”獲得
- 擷取Class位元組碼對象之後可以構造對象執行個體、擷取對象中的屬性對象、方法對象和建構函式對象
- 擷取以上需要的各種對象之後就可以操作它們,進行增刪改查等操作了。
- 優點與缺點是什麼
為什麼要用反射機制?直接建立對象不就可以了嗎,這就涉及到了動態與靜態概念, 靜態編譯:在編譯時間確定類型,綁定對象,即通過。 動態編譯:運行時確定類型,綁定對象。動態編譯最大限度發揮了java的靈活性,體現了多態的應用,用以降低類之間的藕合性。 一句話,反射機制的優點就是可以實現動態建立對象和編譯,體現出很大的靈活性,特別是在J2EE的開發中 它的靈活性就表現的十分明顯。比如,一個大型的軟體,不可能一次就把把它設計的很完美,當這個程式編譯後,發布了,當發現需要更新某些功能時,我們不可能要使用者把以前的卸載,再重新安裝新的版本,假如這樣的話,這個軟體肯定是沒有多少人用的。採用靜態話,需要把整個程式重新編譯一次才可以實現功能的更新,而採用反射機制的話,它就可以不用卸載,只需要在運行時才動態建立和編譯,就可以實現該功能。
它的缺點是對效能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它滿足我們的要求。這類操作總是慢於只直接執行相同的操作。
<span style="font-size:18px;"><span style="font-size:14px;">/** * 擷取位元組碼Class對象的方法 * @throws ClassNotFoundException */@Testpublic void demo1() throws ClassNotFoundException{//擷取Class對象三種方式//1.已知類Class c1 = ReflectTest.class;//2.已知對象Object o = new ReflectTest();Class c2 = o.getClass();//3.未知類和對象,知道完整類名String className = "com.lc.reflect.ReflectTest";Class c3 = Class.forName(className);System.out.println(c1);System.out.println(c2);System.out.println(c3);}</span></span>
<span style="font-size:18px;"><span style="font-size:14px;">/** * 擷取構造方法練習 * @throws Exception */@SuppressWarnings({ "unchecked", "rawtypes" })@Testpublic void demo2() throws Exception{//擷取的Person類位元組碼對象String className = "com.lc.reflect.Person";Class c = Class.forName(className);//通過位元組碼對象獲得所有的構造方法Constructor[] constructors = c.getConstructors();for (int i = 0; i < constructors.length; i++) {System.out.println(constructors[i]);}//擷取指定的構造方法Constructor constructorDefault = c.getConstructor();System.out.println(constructorDefault);//Sring.class為String的位元組碼對象Constructor constructor = c.getConstructor(String.class); //帶參數類型為String的構造方法System.out.println(constructor);//建立對象執行個體的正常寫法:Person person1 = new Person();Person person2 = new Person("lc");//使用反射構造Person對象的執行個體Person reflectPerson1 = (Person)constructorDefault.newInstance(); //無參構造方法Person reflectPerson1_1 = (Person)c.newInstance(); //通過Class對象直接newInstance,將會預設調用目標類無參構造方法Person reflectPerson2 = (Person)constructor.newInstance("lc");//參數為String類型的構造方法}</span></span>
<span style="font-size:18px;"><span style="font-size:14px;">/** * 使用反射操作類成員變數的練習 */@SuppressWarnings("rawtypes")@Testpublic void demo3() throws Exception{//物件導向的寫法是對象調用屬性,而反射就正好相反了。。Person p = new Person("lc");System.out.println("對象調用屬性的寫法=====>:"+p.getName());//使用反射操作類成員變數 --Field類//1.必須獲得目標類的位元組碼對象Class c = Class.forName("com.lc.reflect.Person");//2.操作成員執行個體變數name--獲得name代表Field對象Field[] f1 = c.getFields(); //擷取所有public成員變數,包括父類繼承for (int i = 0; i < f1.length; i++) {System.out.println(f1[i]);}Field[] f2 = c.getDeclaredFields(); //擷取當前類定義的所有成員,包括privatefor (int i = 0; i < f2.length; i++) {System.out.println(f2[i]);}//獲得name成員變數Field field = c.getDeclaredField("name"); //當前field是private//設定private變數可以訪問field.setAccessible(true);//獲得p對象指定name屬性值Object value = field.get(p); //相當於p.getName();System.out.println("反射操作成員變數的寫法=====>"+value);}/** * 使用反射改變成員變數的值(包括私人) * @throws Exception */@Testpublic void demo4() throws Exception{Person p = new Person();//調用p對象中setName設定name的值//1.擷取位元組碼對象Class c = Class.forName("com.lc.reflect.Person");//2.操作setName獲得setName對象反射對象的Method對象//String型別參數setName方法Method setName = c.getDeclaredMethod("setName", String.class);//調用p對象中setNamesetName.invoke(p, "sky"); //相當於p.setName("sky");//3.讀取name的值getName方法Method getName = c.getDeclaredMethod("getName");Object name = getName.invoke(p); //相當於p.getName();System.out.println("反射擷取成員變數的值======>"+name);}</span></span>
<span style="font-size:18px;"><span style="font-size:14px;">/** * 操作方法對象 * @throws Exception */@Testpublic void demo5() throws Exception{//已知String類型完整類名---獲得位元組碼對象String className = "com.lc.reflect.Person";Class c = Class.forName(className);//已知Class對象,構造執行個體Object obj = c.newInstance(); //調用無參構造方法//獲得位元組碼對象中指定屬性和方法//獲得name屬性Field f = c.getDeclaredField("name");//獲得setName方法Method setName = c.getDeclaredMethod("setName", String.class);//修改屬性的值,執行相應方法f.setAccessible(true);f.set(obj, "sky");setName.invoke(obj, "sky_lc");//以上代碼等價於下面的代碼Person p = new Person();//p.name = "sky";p.setName("sky_lc");}</span></span> Java語言反射提供一種動態連結程式組件的多功能方法。它允許程式建立和控制任何類的對象,無需提前寫入程式碼目標類。這些特性使得反射特別適用於建立以非常普通的方式與對象協作的庫。Java reflection 非常有用,它使類和資料結構能按名稱動態檢索相關資訊,並允許在運行著的程式中操作這些資訊。Java 的這一特性非常強大,並且是其它一些常用語言,如 C、C++、Fortran 或者 Pascal 等都不具備的。 由於用於欄位和方法接入時反射要遠慢於直接代碼,反射在效能上會有所影響,但效能問題的程度取決於程式中是如何使用反射的。如果它作為程式運行中相對很少涉及的部分,緩慢的效能將不會是一個問題。即使測試中最壞情況下的計時圖顯示的反射操作只耗用幾微秒。僅反射在效能關鍵的應用的核心邏輯中使用時效能問題才變得至關重要。所以,合理的使用反射將大大提高我們程式的通用性和複用性。
Java反射-再次認識