面試題之------Java 反射機制

來源:互聯網
上載者:User

標籤:lang   vat   編程   java 反射機制   擴充性   編程思想   struct   路徑   otf   


 一、反射機制概述

Java 反射機制是在運行狀態中,對於任意一個類,都能夠獲得這個類的所有屬性和方法,對於任意一個對象都能夠調用它的任意一個屬性和方法。這種在運行時動態擷取資訊以及動態調用對象的方法的功能稱為Java 的反射機制。

Class 類與java.lang.reflect 類庫一起對反射的概念進行了支援,該類庫包含了Field,Method,Constructor類(每個類都實現了Member 介面)。這些類型的對象時由JVM 在運行時建立的,用以表示未知類裡對應的成員。

這樣你就可以使用Constructor 建立新的對象,用get() 和set() 方法讀取和修改與Field 對象關聯的欄位,用invoke() 方法調用與Method 對象關聯的方法。另外,還可以調用getFields() getMethods() 和 getConstructors() 等很便利的方法,以返回表示欄位,方法,以及構造器的對象的數組。這樣匿名對象的資訊就能在運行時被完全確定下來,而在編譯時間不需要知道任何事情。

二、擷取位元組碼的方式

在Java 中可以通過三種方法擷取類的位元組碼(Class)對象

  • 通過Object 類中的getClass() 方法,想要用這種方法必須要明確具體的類並且建立該類的對象。
  • 所有資料類型都具備一個靜態屬性.class 來擷取對應的Class 對象。但是還是要明確到類,然後才能調用類中的靜態成員。
  • 只要通過給定類的字串名稱就可以擷取該類的位元組碼對象,這樣做擴充性更強。通過Class.forName() 方法完成,必須要指定類的全限定名,由於前兩種方法都是在知道該類的情況下擷取該類的位元組碼對象,因此不會有異常,但是Class.forName() 方法如果寫錯類的路徑會報 ClassNotFoundException 的異常。
package 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{}

 

三、通過反射機制擷取類資訊

通過反射機制建立對象,在建立對象之前要獲得對象的建構函式對象,通過建構函式對象建立對應類的執行個體。

下面這段代碼分別在運行期間建立了一個無參與有參的對象執行個體。由於getConstructor() 方法與newInstance() 方法拋出了很多異常(你可以通過原始碼查看它們),這裡就簡寫了直接拋出一個Exception,下同。

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 中的屬性。

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 中的方法並運行。

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() 產生的結果是在編譯時間不可知的,因此所有的方法特徵簽名資訊都是在執行時被提取出來的。反射機制能過建立一個在編譯期完全未知的對象,並調用該對象的方法。

以下是反射機制與泛型的一個應用,通過一個工廠類建立不同類型的執行個體。

要建立對象的執行個體類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);    }}

 

輸出
[email protected]

上面這個執行個體通過一個工廠建立不同對象的執行個體,通過這種方式可以降低代碼的耦合度,代碼得到了很大程度的擴充,以前要建立Apple 對象需要通過new 關鍵字建立Apple 對象,如果我們也要建立Orange 對象呢?是不是也要通過new 關鍵字建立執行個體並向上轉型為Fruit ,這樣做是麻煩的。

現在我們直接有一個工廠,你只要在設定檔中配置你要建立對象的資訊,你就可以建立任何類型你想要的對象,是不是簡單很多了呢?可見反射機制的價值是很驚人的。

Spring 中的 IOC 的底層實現原理就是反射機制,Spring 的容器會幫我們建立執行個體,該容器中使用的方法就是反射,通過解析xml檔案,擷取到id屬性和class屬性裡面的內容,利用反射原理建立設定檔裡類的執行個體對象,存入到Spring的bean容器中。

  參考書籍:
 《Java 編程思想》 Bruce Eckel 著 陳昊鵬 譯

面試題之------Java 反射機制

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.