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

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

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

    so true

    心懷未來,開創(chuàng)未來!
    隨筆 - 160, 文章 - 0, 評論 - 40, 引用 - 0
    數(shù)據(jù)加載中……

    [轉(zhuǎn)載]Java批注的發(fā)明起因及代碼應(yīng)用實例

     
            批注能夠消除樣板代碼,讓源代碼的可讀性更高,并能提供級別更高的錯誤檢查。從EJB3到JUnit4,哪里都在使用它。本文就將告訴你如何使用它。
      Java 5向Java引入了批注(Annotations),它的使用迅速成為現(xiàn)代Java開發(fā)中不可缺少的一部分。在正式開始介紹它之前,看看為什么要發(fā)明批注,這是非常值得的。
      自從Java誕生之日起,人們就一直在解決它初期忽視了的一些問題:缺少元數(shù)據(jù);缺乏將Java以外的代碼嵌入到Java源代碼文件里的能力等。當 Java面市的時候,針對這些問題而推出的JavaDoc終于讓它變完整了。JavaDoc使用了在代碼里專門標記注釋的概念,從而讓它能夠提取出額外的信息,說具體點就是文檔,并將它轉(zhuǎn)換成為我們熟悉的JavaDoc文檔。這是一項簡單的技術(shù),人人都可以使用。首先會有Doclet,目的是讓人們擴展文檔的輸出。然后是Xdoclet,它像使用標記一樣使用JavaDoc來生成代碼,從而將整個過程變得輕而易舉。這部分是對J2EE的復(fù)雜性的回應(yīng)。 J2EE原來依靠很多樣板代碼(boilerplate code)把對象捆綁到J2EE框架里。但是這些方案都有一些問題。首先,注釋里的標記從來都不會進入最終的源代碼,所以除非你生成代碼來反映這些標記,否則你無法在運行期間查找到它。其次,它會把整個預(yù)處理層加到(在理想情況下應(yīng)該是)一個簡單編譯過程里。最后,基于注釋的標記在編譯期間并不是很容易檢查,也無法輕易被很多IDE檢查;如果你把注釋標記拼寫錯了,編譯器是不會注意到的,編譯器只會關(guān)注那些它知道確切名字的標記。
      要解決這所有的問題,Java新增了批注。批注是用于Java語言的本機元數(shù)據(jù)標記。它們的輸入嚴格與Java語言的其他部分類似,可以通過反映被發(fā)現(xiàn),更容易地讓 IDE和編譯器的編寫者管理?,F(xiàn)在就讓我們看一些被批注的代碼吧;我們先從BaseExample開始,它是一個簡單類,只帶有一個方法—— myMethod:

      public class BaseExample {

      public BaseExample() {}

      public void myMethod() {

      System.out.println("This is the BaseExample");

      }

      }

      現(xiàn)在,我們想要擴展BaseExample并替代myMethod。下面就是完成這一任務(wù)的Example1代碼:

      public class Example1 extends BaseExample {

      public Example1() {}

      @Overridepublic void myMethod() {

      System.out.println("This is the Example1");

      }

      }

      這樣我們就有了第一個關(guān)于myMethod的批注——@Override。這是一系列內(nèi)置的批注之一。@Override的意思是“方法必須替代其超類中的一個方法;如果做不到這一點,那么就會有東西出錯,使得編譯器產(chǎn)生錯誤”。沒有@Override,代碼照樣會正常工作,但是假設(shè)有人修改BaseExample,讓myMethod帶有參數(shù)。如果你沒有使用@Override批注,代碼仍然會被編譯,隱藏了子類沒有替代超類方法的問題。如果有@Override的話,你會在編譯期間看到發(fā)生錯誤。
      你可能會認為“難道語言的擴展沒有解決這個問題,額外的關(guān)鍵字可能會嗎”,是的,它可能已經(jīng)實現(xiàn)了這一點,但是這不僅沒有給語言帶來任何靈活性,還會導(dǎo)致很多源代碼兼容性的問題。批注這種方式避免了改變Java語言本身(當然除了增加了@markup),并且還能夠放在代碼的不同部分里,而不僅僅是在標記方法里。
      關(guān)于批注還有一點是,你可以創(chuàng)建自己的批注標記,這正是我們馬上要討論的內(nèi)容。想一想下面這個問題:我們有一些簡單的Java Beans程序,它們都帶有不同的字符串字段。我們希望能夠有一些通用窗體顯示代碼,它們能夠用其他顯示提示(比如寬度)來正確地標示這些字段?,F(xiàn)在我們可以編寫一個超類,它能夠提取出這個數(shù)據(jù),比如說從一個在每個類里都帶有一些靜態(tài)支持方法的靜態(tài)數(shù)組里,但是這也意味著要強制給代碼分層。利用批注做到這一點就要簡單得多了?,F(xiàn)在讓我們從定義FormLabel.java里的FormLabel的批注開始:

      import java.lang.annotation.*;

      @Retention(RetentionPolicy.RUNTIME)

      @Target(ElementType.METHOD)

      public@interface FormLabel {String label();

      int width() default 40;

      }

      你應(yīng)該注意到的第一件事是Java使用了它自己內(nèi)置的一些批注來定批注:@Retention和@Target。@Retention用來定義通過設(shè)置 RetentionPolicy的值批注能夠在構(gòu)建-運行過程中存留多久。這里我們使用了RUNTIME,這意味著我們定義的批注將會在運行期間被保留在代碼里。RetentionPolicy.SOURCE將被用于一個我們希望被編譯器使用然后拋棄的批注。RetentionPolicy.CLASS讓它們保留在生成的類文件里,但是能夠在運行期間被Java虛擬機(JVM)訪問到。
      在默認情況下,你可以在代碼里的任何地方都應(yīng)用批注。 @Target批注讓你能夠?qū)⑺拗圃诖a的特定部分里。在本文里,我們把目標瞄準了ElementType.METHOD,這意味著它只能夠與方法關(guān)聯(lián)在一起。其他ElementTypes有CONSTRUCTOR、FIELD、LOCAL_VARIABLE、PACKAGE、PARAMETER和 TYPE,每個都能夠把批注限制到該種類型的Java語言元素,所以例如,設(shè)置TYPE將只允許批注為定義過的這種類型,比如:

      @OurAnnotation

      public class OurAnnotatedClass {…

      值得注意的是,@Target批注能夠接受單個ElementType或者一個ElementType數(shù)組,如果你想要將批注限制為一系列語言元素的話。
      下面一部分是批注接口的定義;這就像是一個普通的接口聲明,除了我們用@interface將其標記為一個批注。在這個接口里,我們?nèi)缓蠖x批注的方法,就像我們希望用在與批注相關(guān)聯(lián)的信息上的抽象方法,所以我們就有了String label(),用于一個叫做label的字符串屬性。如果我們沒有方法,那么批注就只能用于“做標記”,而@Overrides注釋就是這樣一個例子。如果你只有一個屬性,它最好被命名為“value”,因為當帶有一個未命名參數(shù)的批注在設(shè)置這個值時,它工作得最好。屬性還可以有默認值,比如“int width() default 40;”就是在定義一個默認值為40的整數(shù)屬性。
      這就是批注定義。我們現(xiàn)在就可以在代碼里使用它了。下面一個SimpleData類就用到了它。

      public class SimpleData {

      private String firstname;

      private String lastname;

      private String postcode;

      public SimpleData() {}

      @FormLabel(label="First Name")

      public String getFirstname() { return firstname; }

      public void

      setFirstname(String firstname) {this.firstname = firstname;}

      @FormLabel(label="Last Name",width=80)

      public String getLastname() { return lastname; }

      public void setLastname(String lastname) {

      this.lastname = lastname;

      }

      @FormLabel(label="Postal code",width=10)

      public String getPostcode() { return postcode; }

      public void setPostcode(String postcode) {

      this.postcode = postcode;

      }

      }

      當然,如果我們不查找批注,那么它們對代碼的執(zhí)行就不會造成任何不同。我們所需要的是在運行期間使用批注的方式;我們通過Reflection API來達到這一目的?,F(xiàn)在就讓我們創(chuàng)建一個簡單的processForm方法,它能夠在任何對象里查找批注。

      public void processForm(Object o) {

      for(Method m:o.getClass().getMethods()) {

      我們將在傳遞給方法的對象的類里定義所有的方法?,F(xiàn)在,我們需要檢查每個方法,看看它們是否有FormLabel批注,以及是否返回一個String(為了簡單地說明問題,我們給所有的結(jié)果多返回一些代碼):

      if(m.isAnnotationPresent(FormLabel.class) &&

      m.getReturnType()==String.class) {

      現(xiàn)在我們可以通過使用Method的getAnnotation()方法來提取FormLabel批注:

      FormLabel formLabel=

      m.getAnnotation(FormLabel.class);

      現(xiàn)在我們執(zhí)行方法來取得其字符串值,并通過在批注接口里定義的方法訪問批注屬性。下面我們就把它們打印出來:

      try {

      String value=(String)m.invoke(o);

      String label=formLabel.label();

      int width=formLabel.width();

      System.out.printf("%s[%d]:%s\n",label,width,value);

      } catch (IllegalArgumentException ex) {

      ex.printStackTrace();

      }

      catch (IllegalAccessException ex) {

      ex.printStackTrace();}

      catch (InvocationTargetException ex) {

      ex.printStackTrace();

      }

      }

      }

      }

      現(xiàn)在我們可以創(chuàng)建含有@FormLabel批注的新類,并把它們傳遞給processForm方法。這是在運行期間訪問你自己的批注的基礎(chǔ)。
      現(xiàn)在這個時候,我們回頭看看Java 5里面其他關(guān)于批注的內(nèi)容。首先是編譯器指令——@Deprecated和@SuppressWarnings。@Deprecated是把方法標示為被否定的增強方法;不推薦把它用在新代碼里,以防止以后刪除。用@Deprecated可以生成一個來自編譯器的相關(guān)警告。

      @SuppressWarnings會阻止編譯器在封閉代碼元素里警告你,所以你可以在類定義的開始或者對特定的方法使用@SuppressWarnings。它可以帶參數(shù),用來指定需要取消的錯誤的類型,例如:

      @SuppressWarnings("unchecked")

      public List getList() {

      List l=new LinkedList();

      return l;

      }

      這里我們?nèi)∠艘粋€關(guān)于在List和List之間的“未檢查”的強制轉(zhuǎn)換。當你開始用Java編程但是沒有非一般代碼的時候,這就非常有用。在取消警告的時候,盡可能地縮小取消的范圍是值得的;在上面的例子里,我們?nèi)∠苏麄€代碼。我們可以把它變緊湊,只隱藏一個語句的錯誤:

      public List

      getListToo() {

      @SuppressWarnings("unchecked")

      List l=new LinkedList();

      return l;

      }

      要注意的是,你需要在Java2SE 1.5.06或者以上的版本上進行這項工作;這之前的版本沒有提供對@SuppressWarning支持。
      Java 5里其他內(nèi)置的批注都與對批注的支持有關(guān)——@Documented和@Inherited。它們都可以被加到批注定義里。@Documented的作用是,批注的使用應(yīng)該在所有生成的JavaDoc文檔里都反映出來。正如你可能看到的,批注和JavaDoc標記是互補的。@Inherited的意思是,當另外一個類用類來擴展批注時,批注應(yīng)該是可繼承的;在默認情況下,批注是不能被繼承的。

      你可能很希望在自己的開發(fā)項目里使用Java批注的方法。就像我在引言里講到的,批注已經(jīng)成為現(xiàn)代Java框架和應(yīng)用程序的重要一部分;就拿 JUnit4舉個例子,Java批注已經(jīng)允許JUnit的開發(fā)人員有了以更豐富的方式表示測試的方法,而不用要求測試編寫者強制使用統(tǒng)一的命名規(guī)則。還有 Grails,這里批注可以被用來向“類似鐵軌(rails-like)”的框架提供信息。批注的能力有很多,但是要記住,能力越大,責任也越大。批注是為了給開發(fā)人員提供標記信息,而不是用來隱藏運行配置。
     
    完整的Java代碼例子:
     
    package com.shou.www;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @interface FormLabel {
     String label();
     int width() default 40;
    }
    public class AnnotationDemo {
     private String firstname="Zhao";
     private String lastname="Bacoo";
     private String postcode="116023";
     public AnnotationDemo() {
     }
     @FormLabel(label = "First Name")
     public String getFirstname() {
      return firstname;
     }
     public void setFirstname(String firstname) {
      this.firstname = firstname;
     }
     @FormLabel(label = "Last Name", width = 80)
     public String getLastname() {
      return lastname;
     }
     public void setLastname(String lastname) {
      this.lastname = lastname;
     }
     @FormLabel(label = "Postal code", width = 10)
     public String getPostcode() {
      return postcode;
     }
     public void setPostcode(String postcode) {
      this.postcode = postcode;
     }
     public void processForm(Object o) {
      for (Method m : o.getClass().getMethods()) {
       if (m.isAnnotationPresent(FormLabel.class) &&
       m.getReturnType() == String.class) {
        FormLabel formLabel = m.getAnnotation(FormLabel.class);
        try {
         String value = (String) m.invoke(o);
         String label = formLabel.label();
         int width = formLabel.width();
         System.out.printf("%s[%d]:%s\n", label, width, value);
        } catch (IllegalArgumentException ex) {
         ex.printStackTrace();
        }
        catch (IllegalAccessException ex) {
         ex.printStackTrace();
        }
        catch (InvocationTargetException ex) {
         ex.printStackTrace();
        }
       }
      }
     }
     public static void main(String[] args) {
      // TODO Auto-generated method stub
      AnnotationDemo ad=new AnnotationDemo();
      ad.processForm(ad);
     }
     
    }
    //程序運行的結(jié)果如下:
    //First Name[40]:Zhao
    //Last Name[80]:Bacoo
    //Postal code[10]:116023
    最后,在IBM的網(wǎng)站上還有翻譯過來的兩篇不錯的關(guān)于java注釋方面的講解,但是那兩篇文章比較基本,不如上面這篇文章這樣經(jīng)典和深刻,有興趣的讀者可以參考:
    http://www.ibm.com/developerworks/cn/java/j-annotate1/
    http://www.ibm.com/developerworks/cn/java/j-annotate2.html

    posted on 2007-12-20 23:39 so true 閱讀(462) 評論(0)  編輯  收藏 所屬分類: Java

    主站蜘蛛池模板: 24小时日本在线www免费的| 亚洲阿v天堂在线2017免费| 一级毛片免费观看不卡的| 老牛精品亚洲成av人片| 亚洲日本视频在线观看| 国产亚洲成av人片在线观看| 日本免费的一级v一片| 免费观看激色视频网站bd| 久久精品国产影库免费看| 亚洲阿v天堂在线2017免费| 国产精品无码亚洲精品2021 | 四虎亚洲精品高清在线观看| 久久青青成人亚洲精品| 伊人久久亚洲综合| 亚洲AV之男人的天堂| 国产伦一区二区三区免费| 日本一区免费电影| 免费高清小黄站在线观看| 毛片免费观看的视频在线| 久久久久国色AV免费看图片| 亚洲免费在线观看视频| 免费v片在线观看视频网站| 精品熟女少妇av免费久久| 久99久精品免费视频热77| 免费无码成人AV在线播放不卡| a级毛片在线免费| a国产成人免费视频| 三级网站免费观看| 精品免费视在线观看| 免费视频一区二区| 97在线视频免费| 黄色免费网站网址| 久久久久国色AV免费观看性色 | 亚洲香蕉久久一区二区| 亚洲免费二区三区| 亚洲精品第一综合99久久| 亚洲av午夜国产精品无码中文字| 18禁亚洲深夜福利人口| 老司机午夜在线视频免费观| 成人精品综合免费视频| 中国一级全黄的免费观看|