Java 反射機制是在運行狀態中,對於任意一個類,都可以得到這個類的全部屬性和方法,對於任意一個對象都可以調用它的任意一個屬性和方法。這種在運行時動態的獲取信息以及動態調用對象的方法的功能稱爲Java 的反射機制。java
Class 類與java.lang.reflect 類庫一塊兒對反射的概念進行了支持,該類庫包含了Field,Method,Constructor類(每一個類都實現了Member 接口)。這些類型的對象時由JVM 在運行時建立的,用以表示未知類裏對應的成員。編程
這樣你就可使用Constructor 建立新的對象,用get() 和set() 方法讀取和修改與Field 對象關聯的字段,用invoke() 方法調用與Method 對象關聯的方法。另外,還能夠調用getFields() getMethods() 和 getConstructors() 等很便利的方法,以返回表示字段,方法,以及構造器的對象的數組。這樣匿名對象的信息就能在運行時被徹底肯定下來,而在編譯時不須要知道任何事情。數組
在Java 中能夠經過三種方法獲取類的字節碼(Class)對象函數
ackage com.jas.reflect; public class ReflectTest { public static void main(String[] args) { Fruit fruit = new Fruit(); Class<?> class1 = fruit.getClass(); //方法一 Class<?> class2 = Fruit.class; //方法二 Class class3 = null; try { //方法三,若是這裏不指定類所在的包名會報 ClassNotFoundException 異常 class3 = Class.forName("com.jas.reflect.Fruit"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(class1 + " " +class2 + " " + class3); } } class Fruit{}
經過反射機制建立對象,在建立對象以前要得到對象的構造函數對象,經過構造函數對象建立對應類的實例。ui
下面這段代碼分別在運行期間建立了一個無參與有參的對象實例。因爲getConstructor() 方法與newInstance() 方法拋出了不少異常(你能夠經過源代碼查看它們),這裏就簡寫了直接拋出一個Exception,下同。this
package com.jas.reflect; import java.lang.reflect.Constructor; public class ReflectTest { public static void main(String[] args) throws Exception { Class clazz = null; clazz = Class.forName("com.jas.reflect.Fruit"); Constructor<Fruit> constructor1 = clazz.getConstructor(); Constructor<Fruit> constructor2 = clazz.getConstructor(String.class); Fruit fruit1 = constructor1.newInstance(); Fruit fruit2 = constructor2.newInstance("Apple"); } } class Fruit{ public Fruit(){ System.out.println("無參構造器Run..........."); } public Fruit(String type){ System.out.println("有參構造器Run..........." + type); } }
輸出:
無參構造器Run………..
有參構造器Run………..Apple
經過反射機制獲取Class 中的屬性spa
package com.jas.reflect; import java.lang.reflect.Field; public class ReflectTest { public static void main(String[] args) throws Exception { Class<?> clazz = null; Field field = null; clazz = Class.forName("com.jas.reflect.Fruit"); //field = clazz.getField("num"); getField() 方法不能獲取私有的屬性 // field = clazz.getField("type"); 訪問私有字段時會報 NoSuchFieldException異常 field = clazz.getDeclaredField("type"); //獲取私有type 屬性 field.setAccessible(true); //對私有字段的訪問取消檢查 Fruit fruit = (Fruit) clazz.newInstance(); //建立無參對象實例 field.set(fruit,"Apple"); //爲無參對象實例屬性賦值 Object type = field.get(fruit); //經過fruit 對象獲取屬性值 System.out.println(type); } } class Fruit{ public int num; private String type; public Fruit(){ System.out.println("無參構造器Run..........."); } public Fruit(String type){ System.out.println("有參構造器Run..........." + type); } }
輸出:
無參構造器Run………..
Apple
經過反射機制獲取Class 中的方法並運行。code
package com.jas.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class ReflectTest { public static void main(String[] args) throws Exception { Class clazz = null; Method method = null; clazz = Class.forName("com.jas.reflect.Fruit"); Constructor<Fruit> fruitConstructor = clazz.getConstructor(String.class); Fruit fruit = fruitConstructor.newInstance("Apple"); //建立有參對象實例 method = clazz.getMethod("show",null); //獲取空參數show 方法 method.invoke(fruit,null); //執行無參方法 method = clazz.getMethod("show",int.class); //獲取有參show 方法 method.invoke(fruit,20); //執行有參方法 } } class Fruit{ private String type; public Fruit(String type){ this.type = type; } public void show(){ System.out.println("Fruit type = " + type); } public void show(int num){ System.out.println("Fruit type = " + type + ".....Fruit num = " + num); } }
輸出: Fruit type = Apple Fruit type = Apple…..Fruit num = 20
Class.forName() 生成的結果是在編譯時不可知的,所以全部的方法特徵簽名信息都是在執行時被提取出來的。反射機制能過建立一個在編譯期徹底未知的對象,並調用該對象的方法。xml
如下是反射機制與泛型的一個應用,經過一個工廠類建立不一樣類型的實例。對象
要建立對象的實例類Apple :
package com.jas.reflect; public interface Fruit {} class Apple implements Fruit{}
加載的配置文件config.properties:
Fruit=com.jas.reflect.Apple
工廠類BasicFactory :
package com.jas.reflect; import java.io.FileReader; import java.util.Properties; public class BasicFactory { private BasicFactory(){} private static BasicFactory bf = new BasicFactory(); private static Properties pro = null; static{ pro = new Properties(); try{ //經過類加載器加載配置文件 pro.load(new FileReader(BasicFactory.class.getClassLoader(). getResource("config.properties").getPath())); }catch (Exception e) { e.printStackTrace(); } } public static BasicFactory getFactory(){ return bf; } //使用泛型得到通用的對象 public <T> T newInstance(Class<T> clazz){ String cName = clazz.getSimpleName(); //得到字節碼對象的類名 String clmplName = pro.getProperty(cName); //根據字節碼對象的類名經過配置文件得到類的全限定名 try{ return (T)Class.forName(clmplName).newInstance(); //根據類的全限定名建立實例對象 }catch (Exception e) { throw new RuntimeException(e); } } }
建立對象實例:
package com.jas.reflect; public class ReflectTest { public static void main(String[] args) throws Exception { Fruit fruit = BasicFactory.getFactory().newInstance(Fruit.class); System.out.println(fruit); } }
輸出
com.jas.reflect.Apple@4554617c
上面這個實例經過一個工廠建立不一樣對象的實例,經過這種方式能夠下降代碼的耦合度,代碼獲得了很大程度的擴展,之前要建立Apple 對象須要經過new 關鍵字建立Apple 對象,若是咱們也要建立Orange 對象呢?是否是也要經過new 關鍵字建立實例並向上轉型爲Fruit ,這樣作是麻煩的。
如今咱們直接有一個工廠,你只要在配置文件中配置你要建立對象的信息,你就能夠建立任何類型你想要的對象,是否是簡單不少了呢?可見反射機制的價值是很驚人的。
Spring 中的 IOC 的底層實現原理就是反射機制,Spring 的容器會幫咱們建立實例,該容器中使用的方法就是反射,經過解析xml文件,獲取到id屬性和class屬性裏面的內容,利用反射原理建立配置文件裏類的實例對象,存入到Spring的bean容器中。
參考書籍: 《Java 編程思想》 Bruce Eckel 著 陳昊鵬 譯