<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-0  評論-3  文章-28  trackbacks-0

    一、反射的概念 :

    反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測和修改它本身狀態(tài)或行為的一種能力。這一概念的提出很快引發(fā)了計算機科學領域關于應用反射性的研究。它首先被程序語言的設計領域所采用,并在Lisp和面向?qū)ο蠓矫嫒〉昧顺煽?。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射機制的語言。最近,反射機制也被應用到了視窗系統(tǒng)、操作系統(tǒng)和文件系統(tǒng)中。 

    反射本身并不是一個新概念,它可能會使我們聯(lián)想到光學中的反射概念,盡管計算機科學賦予了反射概念新的含義,但是,從現(xiàn)象上來說,它們確實有某些相通之處,這些有助于我們的理解。在計算機科學領域,反射是指一類應用,它們能夠自描述和自控制。也就是說,這類應用通過采用某種機制來實現(xiàn)對自己行為的描述(self-representation)和監(jiān)測(examination),并能根據(jù)自身行為的狀態(tài)和結果,調(diào)整或修改應用所描述行為的狀態(tài)和相關的語義。可以看出,同一般的反射概念相比,計算機科學領域的反射不單單指反射本身,還包括對反射結果所采取的措施。所有采用反射機制的系統(tǒng)(即反射系統(tǒng))都希望使系統(tǒng)的實現(xiàn)更開放??梢哉f,實現(xiàn)了反射機制的系統(tǒng)都具有開放性,但具有開放性的系統(tǒng)并不一定采用了反射機制,開放性是反射系統(tǒng)的必要條件。一般來說,反射系統(tǒng)除了滿足開放性條件外還必須滿足原因連接(Causally-connected)。所謂原因連接是指對反射系統(tǒng)自描述的改變能夠立即反映到系統(tǒng)底層的實際狀態(tài)和行為上的情況,反之亦然。開放性和原因連接是反射系統(tǒng)的兩大基本要素。13700863760

    Java中,反射是一種強大的工具。它使您能夠創(chuàng)建靈活的代碼,這些代碼可以在運行時裝配,無需在組件之間進行源代表鏈接。反射允許我們在編寫與執(zhí)行時,使我們的程序代碼能夠接入裝載到JVM中的類的內(nèi)部信息,而不是源代碼中選定的類協(xié)作的代碼。這使反射成為構建靈活的應用的主要工具。但需注意的是:如果使用不當,反射的成本很高。

    二、Java中的類反射:

    Reflection 是 Java 程序開發(fā)語言的特征之一,它允許運行中的 Java 程序?qū)ψ陨磉M行檢查,或者說“自審”,并能直接操作程序的內(nèi)部屬性。Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數(shù)定義相關的信息。

    1.檢測類:

    1.1 reflection的工作機制

    考慮下面這個簡單的例子,讓我們看看 reflection 是如何工作的。

    import java.lang.reflect.*;
    public class DumpMethods {
        public static void main(String args[]) {
            try {
                Class c = Class.forName(args[0]);
                Method m[] = c.getDeclaredMethods();
                for (int i = 0; i < m.length; i++)
                    System.out.println(m[i].toString());
            } catch (Throwable e) {
                System.err.println(e);
            }
        }
    }

    按如下語句執(zhí)行:

    java DumpMethods java.util.Stack

    它的結果輸出為:

    public java.lang.Object java.util.Stack.push(java.lang.Object)

    public synchronized java.lang.Object java.util.Stack.pop()

    public synchronized java.lang.Object java.util.Stack.peek()

    public boolean java.util.Stack.empty()

    public synchronized int java.util.Stack.search(java.lang.Object)

    這樣就列出了java.util.Stack 類的各方法名以及它們的限制符和返回類型。

    這個程序使用 Class.forName 載入指定的類,然后調(diào)用 getDeclaredMethods 來獲取這個類中定義了的方法列表。java.lang.reflect.Methods 是用來描述某個類中單個方法的一個類。

    1.2 Java類反射中的主要方法

    對于以下三類組件中的任何一類來說 -- 構造函數(shù)、字段和方法 -- java.lang.Class 提供四種獨立的反射調(diào)用,以不同的方式來獲得信息。調(diào)用都遵循一種標準格式。以下是用于查找構造函數(shù)的一組反射調(diào)用:

    l         Constructor getConstructor(Class[] params) -- 獲得使用特殊的參數(shù)類型的公共構造函數(shù),

    l         Constructor[] getConstructors() -- 獲得類的所有公共構造函數(shù)

    l         Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定參數(shù)類型的構造函數(shù)(與接入級別無關)

    l         Constructor[] getDeclaredConstructors() -- 獲得類的所有構造函數(shù)(與接入級別無關) 

    獲得字段信息的Class 反射調(diào)用不同于那些用于接入構造函數(shù)的調(diào)用,在參數(shù)類型數(shù)組中使用了字段名:

    l         Field getField(String name) -- 獲得命名的公共字段

    l         Field[] getFields() -- 獲得類的所有公共字段

    l         Field getDeclaredField(String name) -- 獲得類聲明的命名的字段

    l         Field[] getDeclaredFields() -- 獲得類聲明的所有字段 

    用于獲得方法信息函數(shù):

    l         Method getMethod(String name, Class[] params) -- 使用特定的參數(shù)類型,獲得命名的公共方法

    l         Method[] getMethods() -- 獲得類的所有公共方法

    l         Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數(shù)類型,獲得類聲明的命名的方法

    l         Method[] getDeclaredMethods() -- 獲得類聲明的所有方法 

     

    1.3開始使用 Reflection:

    用于 reflection 的類,如 Method,可以在 java.lang.relfect 包中找到。使用這些類的時候必須要遵循三個步驟:第一步是獲得你想操作的類的 java.lang.Class 對象。在運行中的 Java 程序中,用 java.lang.Class 類來描述類和接口等。

    下面就是獲得一個 Class 對象的方法之一:

    Class c = Class.forName("java.lang.String");

    這條語句得到一個 String 類的類對象。還有另一種方法,如下面的語句:

    Class c = int.class;

    或者

    Class c = Integer.TYPE;

    它們可獲得基本類型的類信息。其中后一種方法中訪問的是基本類型的封裝類 (如 Integer) 中預先定義好的 TYPE 字段。

    第二步是調(diào)用諸如 getDeclaredMethods 的方法,以取得該類中定義的所有方法的列表。

    一旦取得這個信息,就可以進行第三步了——使用 reflection API 來操作這些信息,如下面這段代碼:

    Class c = Class.forName("java.lang.String");

    Method m[] = c.getDeclaredMethods();

    System.out.println(m[0].toString());

    它將以文本方式打印出 String 中定義的第一個方法的原型。

    2.處理對象:

    如果要作一個開發(fā)工具像debugger之類的,你必須能發(fā)現(xiàn)filed values,以下是三個步驟:

    a.創(chuàng)建一個Class對象
    b.通過getField 創(chuàng)建一個Field對象
    c.調(diào)用Field.getXXX(Object)方法(XXX是Int,Float等,如果是對象就省略;Object是指實例).

    例如:
    import java.lang.reflect.*;
    import java.awt.*;

    class SampleGet {

       public static void main(String[] args) {
          Rectangle r = new Rectangle(100, 325);
          printHeight(r);

       }

       static void printHeight(Rectangle r) {
          Field heightField;
          Integer heightValue;
          Class c = r.getClass();
          try {
            heightField = c.getField("height");
            heightValue = (Integer) heightField.get(r);
            System.out.println("Height: " + heightValue.toString());
          } catch (NoSuchFieldException e) {
              System.out.println(e);
          } catch (SecurityException e) {
              System.out.println(e);
          } catch (IllegalAccessException e) {
              System.out.println(e);
          }
       }
    }

     

    三、安全性和反射

    在處理反射時安全性是一個較復雜的問題。反射經(jīng)常由框架型代碼使用,由于這一點,我們可能希望框架能夠全面接入代碼,無需考慮常規(guī)的接入限制。但是,在其它情況下,不受控制的接入會帶來嚴重的安全性風險,例如當代碼在不值得信任的代碼共享的環(huán)境中運行時。

    由于這些互相矛盾的需求,Java編程語言定義一種多級別方法來處理反射的安全性。基本模式是對反射實施與應用于源代碼接入相同的限制:

    n         從任意位置到類公共組件的接入 

    n         類自身外部無任何到私有組件的接入 

    n         受保護和打包(缺省接入)組件的有限接入 

    不過至少有些時候,圍繞這些限制還有一種簡單的方法。我們可以在我們所寫的類中,擴展一個普通的基本類java.lang.reflect.AccessibleObject 類。這個類定義了一種setAccessible方法,使我們能夠啟動或關閉對這些類中其中一個類的實例的接入檢測。唯一的問題在于如果使用了安全性管理器,它將檢測正在關閉接入檢測的代碼是否許可了這樣做。如果未許可,安全性管理器拋出一個例外。

    下面是一段程序,在TwoString 類的一個實例上使用反射來顯示安全性正在運行:

    public class ReflectSecurity {

        public static void main(String[] args) {

            try {

                TwoString ts = new TwoString("a", "b");

                Field field = clas.getDeclaredField("m_s1");

    //          field.setAccessible(true);

                System.out.println("Retrieved value is " +

                    field.get(inst));

            } catch (Exception ex) {

                ex.printStackTrace(System.out);

            }

        }

    }

    如果我們編譯這一程序時,不使用任何特定參數(shù)直接從命令行運行,它將在field .get(inst)調(diào)用中拋出一個IllegalAccessException異常。如果我們不注釋field.setAccessible(true)代碼行,那么重新編譯并重新運行該代碼,它將編譯成功。最后,如果我們在命令行添加了JVM參數(shù)-Djava.security.manager以實現(xiàn)安全性管理器,它仍然將不能通過編譯,除非我們定義了ReflectSecurity類的許可權限。

    四、反射性能

    反射是一種強大的工具,但也存在一些不足。一個主要的缺點是對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類操作總是慢于只直接執(zhí)行相同的操作。

    下面的程序是字段接入性能測試的一個例子,包括基本的測試方法。每種方法測試字段接入的一種形式 -- accessSame 與同一對象的成員字段協(xié)作,accessOther 使用可直接接入的另一對象的字段,accessReflection 使用可通過反射接入的另一對象的字段。在每種情況下,方法執(zhí)行相同的計算 -- 循環(huán)中簡單的加/乘順序。

    程序如下:

    public int accessSame(int loops) {

        m_value = 0;

        for (int index = 0; index < loops; index++) {

            m_value = (m_value + ADDITIVE_VALUE) *

                MULTIPLIER_VALUE;

        }

        return m_value;

    }

     

    public int accessReference(int loops) {

        TimingClass timing = new TimingClass();

        for (int index = 0; index < loops; index++) {

            timing.m_value = (timing.m_value + ADDITIVE_VALUE) *

                MULTIPLIER_VALUE;

        }

        return timing.m_value;

    }

     

    public int accessReflection(int loops) throws Exception {

        TimingClass timing = new TimingClass();

        try {

            Field field = TimingClass.class.

                getDeclaredField("m_value");

            for (int index = 0; index < loops; index++) {

                int value = (field.getInt(timing) +

                    ADDITIVE_VALUE) * MULTIPLIER_VALUE;

                field.setInt(timing, value);

            }

            return timing.m_value;

        } catch (Exception ex) {

            System.out.println("Error using reflection");

            throw ex;

        }

    }

    在上面的例子中,測試程序重復調(diào)用每種方法,使用一個大循環(huán)數(shù),從而平均多次調(diào)用的時間衡量結果。平均值中不包括每種方法第一次調(diào)用的時間,因此初始化時間不是結果中的一個因素。下面的圖清楚的向我們展示了每種方法字段接入的時間:

    圖 1:字段接入時間 

    我們可以看出:在前兩副圖中(Sun JVM),使用反射的執(zhí)行時間超過使用直接接入的1000倍以上。通過比較,IBM JVM可能稍好一些,但反射方法仍舊需要比其它方法長700倍以上的時間。任何JVM上其它兩種方法之間時間方面無任何顯著差異,但IBM JVM幾乎比Sun JVM快一倍。最有可能的是這種差異反映了Sun Hot Spot JVM的專業(yè)優(yōu)化,它在簡單基準方面表現(xiàn)得很糟糕。反射性能是Sun開發(fā)1.4 JVM時關注的一個方面,它在反射方法調(diào)用結果中顯示。在這類操作的性能方面,Sun 1.4.1 JVM顯示了比1.3.1版本很大的改進。

    如果為為創(chuàng)建使用反射的對象編寫了類似的計時測試程序,我們會發(fā)現(xiàn)這種情況下的差異不象字段和方法調(diào)用情況下那么顯著。使用newInstance()調(diào)用創(chuàng)建一個簡單的java.lang.Object實例耗用的時間大約是在Sun 1.3.1 JVM上使用new Object()的12倍,是在IBM 1.4.0 JVM的四倍,只是Sun 1.4.1 JVM上的兩部。使用Array.newInstance(type, size)創(chuàng)建一個數(shù)組耗用的時間是任何測試的JVM上使用new type[size]的兩倍,隨著數(shù)組大小的增加,差異逐步縮小。

    結束語

    Java語言反射提供一種動態(tài)鏈接程序組件的多功能方法。它允許程序創(chuàng)建和控制任何類的對象(根據(jù)安全性限制),無需提前硬編碼目標類。這些特性使得反射特別適用于創(chuàng)建以非常普通的方式與對象協(xié)作的庫。例如,反射經(jīng)常在持續(xù)存儲對象為數(shù)據(jù)庫、XML或其它外部格式的框架中使用。Java reflection 非常有用,它使類和數(shù)據(jù)結構能按名稱動態(tài)檢索相關信息,并允許在運行著的程序中操作這些信息。Java 的這一特性非常強大,并且是其它一些常用語言,如 C、C++、Fortran 或者 Pascal 等都不具備的。

    但反射有兩個缺點。第一個是性能問題。用于字段和方法接入時反射要遠慢于直接代碼。性能問題的程度取決于程序中是如何使用反射的。如果它作為程序運行中相對很少涉及的部分,緩慢的性能將不會是一個問題。即使測試中最壞情況下的計時圖顯示的反射操作只耗用幾微秒。僅反射在性能關鍵的應用的核心邏輯中使用時性能問題才變得至關重要。

    許多應用中更嚴重的一個缺點是使用反射會模糊程序內(nèi)部實際要發(fā)生的事情。程序人員希望在源代碼中看到程序的邏輯,反射等繞過了源代碼的技術會帶來維護問題。反射代碼比相應的直接代碼更復雜,正如性能比較的代碼實例中看到的一樣。解決這些問題的最佳方案是保守地使用反射——僅在它可以真正增加靈活性的地方——記錄其在目標類中的使用。

     

     

    利用反射實現(xiàn)的動態(tài)加載


    Bromon原創(chuàng) 請尊重版權

    最近在成都寫一個移動增值項目,俺負責后臺server端。功能很簡單,手機用戶通過GPRS打開Socket與服務器連接,我則根據(jù)用戶傳過來的數(shù)據(jù)做出響應。做過似項目的兄弟一定都知道,首先需要定義一個似于MSNP的通訊協(xié)議,不過今天的話題是如何把這個系統(tǒng)設計得具有高度的擴展性。由于這個項目本身沒有進行過較為完善的客戶溝通和需求分析,所以以后肯定會有很多功能上的擴展,通訊協(xié)議肯定會越來越龐大,而我作為一個不那么勤快的人,當然不想以后再去修改寫好的程序,所以這個項目是實踐面向?qū)ο笤O計的好機會。

    首先定義一個接口來隔離

    package org.bromon.reflect;

    public interface Operator

    {

    public java.util.List act(java.util.List params)

    }

    根據(jù)設計模式的原理,我們可以為不同的功能編寫不同的,每個都繼承Operator接口,客戶端只需要針對Operator接口編程就可以避免很多麻煩。比如這個

    package org.bromon.reflect.*;

    public class Success implements Operator

    {

    public java.util.List act(java.util.List params)

    {

    List result=new ArrayList();

    result.add(new String(“操作成功”));

    return result;

    }

    }

    我們還可以寫其他很多,但是有個問題,接口是無法實例化的,我們必須手動控制具體實例化哪個,這很不爽,如果能夠向應用程序傳遞一個參數(shù),讓自己去選擇實例化一個,執(zhí)行它的act方法,那我們的工作就輕松多了。

    很幸運,我使用的是Java,只有Java才提供這樣的反射機制,或者說內(nèi)省機制,可以實現(xiàn)我們的無理要求。編寫一個配置文件emp.properties:

    #成功響應

    1000=Success

    #向客戶發(fā)送普通文本消息

    2000=Load

    #客戶向服務器發(fā)送普通文本消息

    3000=Store

    文件中的鍵名是客戶將發(fā)給我的消息頭,客戶發(fā)送1000給我,那么我就執(zhí)行Success的act方法,似的如果發(fā)送2000給我,那就執(zhí)行Load的act方法,這樣一來系統(tǒng)就完全符合開閉原則了,如果要添加新的功能,完全不需要修改已有代碼,只需要在配置文件中添加對應規(guī)則,然后編寫新的,實現(xiàn)act方法就ok,即使我棄這個項目而去,它將來也可以很好的擴展。這樣的系統(tǒng)具備了非常良好的擴展性和可插入性。

    下面這個例子體現(xiàn)了動態(tài)加載的功能,程序在執(zhí)行過程中才知道應該實例化哪個

    package org.bromon.reflect.*;

    import java.lang.reflect.*;

    public class TestReflect

    {

    //加載配置文件,查詢消息頭對應的

    private String loadProtocal(String header)

    {

    String result=null;

    try

    {

    Properties prop=new Properties();

    FileInputStream fis=new FileInputStream("emp.properties");

    prop.load(fis);

    result=prop.getProperty(header);

    fis.close();

    }catch(Exception e)

    {

    System.out.println(e);

    }

    return result;

    }

    //針對消息作出響應,利用反射導入對應的

    public String response(String header,String content)

    {

    String result=null;

    String s=null;

    try 

    {

    /*

    * 導入屬性文件emp.properties,查詢header所對應的的名字

    * 通過反射機制動態(tài)加載匹配的,所有的都被Operator接口隔離

    * 可以通過修改屬性文件、添加新的(繼承MsgOperator接口)來擴展協(xié)議

    */

    s="org.bromon.reflect."+this.loadProtocal(header);

    //加載

    Class c=Class.forName(s);

    //創(chuàng)建的事例

    Operator mo=(Operator)c.newInstance();

    //構造參數(shù)列表

    Class params[]=new Class[1];

    params[0]=Class.forName("java.util.List");

    //查詢act方法

    Method m=c.getMethod("act",params);

    Object args[]=new Object[1];

    args[0]=content;

    //調(diào)用方法并且獲得返回

    Object returnObject=m.invoke(mo,args);

    }catch(Exception e)

    {

    System.out.println("Handler-response:"+e);

    }

    return result;

    }

    public static void main(String args[])

    {

    TestReflect tr=new TestReflect();

    tr.response(args[0],”消息內(nèi)容”);

    }

    }

    posted on 2007-06-21 12:03 閱讀(144) 評論(0)  編輯  收藏 所屬分類: java基礎
    主站蜘蛛池模板: 丰满妇女做a级毛片免费观看| 69国产精品视频免费| 成年女人18级毛片毛片免费观看| 亚洲国产高清人在线| 一级做a爰全过程免费视频| 处破痛哭A√18成年片免费| 亚洲夜夜欢A∨一区二区三区| 边摸边吃奶边做爽免费视频网站| 97久久免费视频| 久久精品国产亚洲精品2020| 蜜桃成人无码区免费视频网站| 亚洲国产人成中文幕一级二级| 亚洲A∨无码无在线观看| 四虎影视在线影院在线观看免费视频| 久久青草亚洲AV无码麻豆| 99热免费在线观看| 国产亚洲sss在线播放| 宅男666在线永久免费观看| 黄色免费在线观看网址| 亚洲精品国产美女久久久| 日本在线看片免费| 亚洲日韩av无码| 一级毛片在线免费看| 99亚偷拍自图区亚洲| 亚洲国产免费综合| 亚洲国产精品日韩av不卡在线| 亚洲 无码 在线 专区| 中文文字幕文字幕亚洲色| 国产片免费在线观看| 亚洲人成网站在线在线观看| 国产好大好硬好爽免费不卡| 亚洲乱码在线视频| 亚洲男女内射在线播放| 一级特黄aa毛片免费观看| 亚洲一区二区三区丝袜| 成人A级毛片免费观看AV网站| 亚洲精品一区二区三区四区乱码| 处破痛哭A√18成年片免费| 久久九九全国免费| 亚洲gay片在线gv网站| 久久香蕉国产线看观看亚洲片|