隨筆 - 312, 文章 - 14, 評(píng)論 - 1393, 引用 - 0
          數(shù)據(jù)加載中……

          J2SE5.0中最有趣的新特性:注釋(annotation)

          本文為原創(chuàng),如需轉(zhuǎn)載,請(qǐng)注明作者和出處,謝謝!

          本文曾發(fā)表于IT168:http://tech.it168.com/j/e/2006-09-29/200609291054707.shtml

              本文將向你介紹J2SE5.0中的新特性之一:注釋。本文將從什么是注釋;J2SE5.0中預(yù)定義的注釋;如何自定義注釋;如何對(duì)注釋進(jìn)行注釋以及如何在程序中讀取注釋5個(gè)方面進(jìn)行討論。

          一、什么是注釋

              說起注釋,得先提一提什么是元數(shù)據(jù)(metadata)。所謂元數(shù)據(jù)就是數(shù)據(jù)的數(shù)據(jù)。也就是說,元數(shù)據(jù)是描述數(shù)據(jù)的。就象數(shù)據(jù)表中的字段一樣,每個(gè)字段描述了這個(gè)字段下的數(shù)據(jù)的含義。而J2SE5.0中提供的注釋就是java源代碼的元數(shù)據(jù),也就是說注釋是描述java源代碼的。在J2SE5.0中可以自定義注釋。使用時(shí)在@后面跟注釋的名字。

          二、J2SE5.0中預(yù)定義的注釋

              J2SE5.0java.lang包中預(yù)定義了三個(gè)注釋。它們是OverrideDeprecatedSuppressWarnings。下面分別解釋它們的含義。

              Override

              這個(gè)注釋的作用是標(biāo)識(shí)某一個(gè)方法是否覆蓋了它的父類的方法。那么為什么要標(biāo)識(shí)呢?讓我們來看看如果不用Override標(biāo)識(shí)會(huì)發(fā)生什么事情。

              假設(shè)有兩個(gè)類Class1ParentClass1,用Class1中的myMethod1方法覆蓋ParentClass1中的myMethod1方法。


           class ParentClass1
           {
              
          public void myMethod1() {}
           }

           
          class Class1 extends ParentClass1
           {
              
          public void myMethod2() {}        
           }

           建立Class1的實(shí)例,并且調(diào)用myMethod1方法

              ParentClass1 c1 = new Class1();

              c1.myMethod1();

              以上的代碼可以正常編譯通過和運(yùn)行。但是在寫Class1的代碼時(shí),誤將myMethod1寫成了myMethod2,然而在調(diào)用時(shí),myMethod1并未被覆蓋。因此,c1.myMethod1()調(diào)用的還是ParentClass1myMethod1方法。更不幸的是,程序員并未意識(shí)到這一點(diǎn)。因此,這可能會(huì)產(chǎn)生bug

              如果我們使用Override來修飾Class1中的myMethod1方法,當(dāng)myMethod1被誤寫成別的方法時(shí),編譯器就會(huì)報(bào)錯(cuò)。因此,就可以避免這類錯(cuò)誤。

          class Class1 extends ParentClass1
          {
              @Override   
          // 編譯器產(chǎn)生一個(gè)錯(cuò)誤
              public void myMethod2() {}        
          }


              以上代碼編譯不能通過,被Override注釋的方法必須在父類中存在同樣的方法程序才能編譯通過。也就是說只有下面的代碼才能正確編譯。

          class Class1 extends ParentClass1
          {
              @Override
              
          public void myMethod1() {}        
          }


          Deprecated

          這個(gè)注釋是一個(gè)標(biāo)記注釋。所謂標(biāo)記注釋,就是在源程序中加入這個(gè)標(biāo)記后,并不影響程序的編譯,但有時(shí)編譯器會(huì)顯示一些警告信息。

          那么Deprecated注釋是什么意思呢?如果你經(jīng)常使用eclipseIDE編寫java程序時(shí),可能會(huì)經(jīng)常在屬性或方法提示中看到這個(gè)詞。如果某個(gè)類成員的提示中出現(xiàn)了個(gè)詞,就表示這個(gè)并不建議使用這個(gè)類成員。因?yàn)檫@個(gè)類成員在未來的JDK版本中可能被刪除。之所以在現(xiàn)在還保留,是因?yàn)榻o那些已經(jīng)使用了這些類成員的程序一個(gè)緩沖期。如果現(xiàn)在就去了,那么這些程序就無法在新的編譯器中編譯了。

          說到這,可能你已經(jīng)猜出來了。Deprecated注釋一定和這些類成員有關(guān)。說得對(duì)!使用Deprecated標(biāo)注一個(gè)類成員后,這個(gè)類成員在顯示上就會(huì)有一些變化。在eclipse中非常明顯。讓我們看看圖1有哪些變化。




          加上@Deprecated后的類成員在eclipse中的變化

          從上圖可以看出,有三個(gè)地方發(fā)生的變化。紅色框里面的是變化的部分。

          1.       方法定義處

          2.       方法引用處

          3.       顯示的成員列表中

          發(fā)生這些變化并不會(huì)影響編譯,只是提醒一下程序員,這個(gè)方法以后是要被刪除的,最好別用。

          Deprecated注釋還有一個(gè)作用。就是如果一個(gè)類從另外一個(gè)類繼承,并且override被繼承類的Deprecated方法,在編譯時(shí)將會(huì)出現(xiàn)一個(gè)警告。如test.java的內(nèi)容如下:

          class Class1
          {
              @Deprecated
              
          public void myMethod(){}
          }

          class Class2 extends Class1
          {
              
          public void myMethod(){}
          }


          運(yùn)行javac test.java 出現(xiàn)如下警告:

          注意:test.java 使用或覆蓋了已過時(shí)的 API

          注意:要了解詳細(xì)信息,請(qǐng)使用 -Xlint:deprecation 重新編譯

          使用-Xlint:deprecation顯示更詳細(xì)的警告信息:

          test.java:4: 警告:[deprecation] Class1 中的 myMethod() 已過時(shí)

                  public void myMethod()

                              ^

          1 警告

          這些警告并不會(huì)影響編譯,只是提醒你一下盡量不要用myMethod方法。

          SuppressWarnings

          這個(gè)世界的事物總是成對(duì)出現(xiàn)。即然有使編譯器產(chǎn)生警告信息的,那么就有抑制編譯器產(chǎn)生警告信息的。

          SuppressWarnings注釋就是為了這樣一個(gè)目的而存在的。讓我們先看一看如下的代碼。

          public void myMethod()
          {
              List wordList 
          = new ArrayList();
              wordList.add(
          "foo");
          }

          這是一個(gè)類中的方法。編譯它,將會(huì)得到如下的警告。

          注意:Testannotation.java 使用了未經(jīng)檢查或不安全的操作。

          注意:要了解詳細(xì)信息,請(qǐng)使用 -Xlint:unchecked 重新編譯。

          這兩行警告信息表示List類必須使用范型才是安全的,才可以進(jìn)行類型檢查。如果想不顯示這個(gè)警告信息有兩種方法。一個(gè)是將這個(gè)方法進(jìn)行如下改寫:


          public void myMethod()
          {
              List
          <String> wordList = new ArrayList<String>();
                  wordList.add(
          "foo");
          }

          另外一種方法就是使用@SuppressWarnings


          @SuppressWarnings (value={"unchecked"})
          public void myMethod()
          {
              List wordList 
          = new ArrayList();
              wordList.add(
          "foo");
          }

          要注意的是SuppressWarnings和前兩個(gè)注釋不一樣。這個(gè)注釋有一個(gè)屬性。當(dāng)然,還可以抑制其它警告,如@SuppressWarnings(value={"unchecked", "fallthrough"})

          三、如何自定義注釋

          注釋的強(qiáng)大之處是它不僅可以使java程序變成自描述的,而且允許程序員自定義注釋。注釋的定義和接口差不多,只是在interface前面多了一個(gè)“@”。

          public @interface MyAnnotation

          {

          }

          上面的代碼是一個(gè)最簡單的注釋。這個(gè)注釋沒有屬性。也可以理解為是一個(gè)標(biāo)記注釋。就象Serializable接口一樣是一個(gè)標(biāo)記接口,里面未定義任何方法。

          當(dāng)然,也可以定義而有屬性的注釋。


          public @interface MyAnnotation

          {
              String value();
          }

          可以按如下格式使用MyAnnotation


          @MyAnnotation(“abc”)

          public void myMethod()

          {
          }

          看了上面的代碼,大家可能有一個(gè)疑問。怎么沒有使用value,而直接就寫”abc”了。那么”abc”到底傳給誰了。其實(shí)這里有一個(gè)約定。如果沒有寫屬性名的值,而這個(gè)注釋又有value屬性,就將這個(gè)值賦給value屬性,如果沒有,就出現(xiàn)編譯錯(cuò)誤。

          除了可以省略屬性名,還可以省略屬性值。這就是默認(rèn)值。


          public @interface MyAnnotation

          {
              
          public String myMethod(){} default “xyz”;
          }

          可以直接使用MyAnnotation


          @MyAnnotation // 使用默認(rèn)值xyz

          public void myMethod()

          {

          }

          也可以這樣使用


          @MyAnnotation(myMethod=”abc”)

          public void myMethod()

          {

          }

          如果要使用多個(gè)屬性的話。可以參考如下代碼。

          public @interface MyAnnotation
          {
              
          public enum MyEnum{A, B, C}
              
          public MyEnum.value1() {}
              
          public String value2() {}
          }

          @MyAnnotation(value1
          =MyAnnotation.MyEnum.A, value2 = “xyz”)
          public void myMethod()
          {
          }

          這一節(jié)討論了如何自定義注釋。那么定義注釋有什么用呢?有什么方法對(duì)注釋進(jìn)行限制呢?我們能從程序中得到注釋嗎?這些疑問都可以從下面的內(nèi)容找到答案。

          四、如何對(duì)注釋進(jìn)行注釋

          這一節(jié)的題目讀起來雖然有些繞口,但它所蘊(yùn)涵的知識(shí)卻對(duì)設(shè)計(jì)更強(qiáng)大的java程序有很大幫助。

          在上一節(jié)討論了自定義注釋,由此我們可知注釋在J2SE5.0中也和類、接口一樣。是程序中的一個(gè)基本的組成部分。既然可以對(duì)類、接口進(jìn)行注釋,那么當(dāng)然也可以對(duì)注釋進(jìn)行注釋。

          使用普通注釋對(duì)注釋進(jìn)行注釋的方法和對(duì)類、接口進(jìn)行注釋的方法一樣。所不同的是,J2SE5.0為注釋單獨(dú)提供了4種注釋。它們是TargetRetention、Documented和Inherited。下面就分別介紹這4種注釋。

          Target

          這個(gè)注釋理解起來非常簡單。由于target的中文意思是“目標(biāo)”,因此,我們可能已經(jīng)猜到這個(gè)注釋和某一些目標(biāo)相關(guān)。那么這些目標(biāo)是指什么呢?大家可以先看看下面的代碼。


          @Target({ElementType.METHOD})
          @
          interface MyAnnotation {}

          @MyAnnotation         
          // 錯(cuò)誤的使用
          public class Class1
          {
              @MyAnnotation        
          // 正確的使用
              public void myMethod1() {}
          }

          以上代碼定義了一個(gè)注釋MyAnnotation和一個(gè)類Class1,并且使用MyAnnotation分別對(duì)Class1myMethod1進(jìn)行注釋。如果編譯這段代碼是無法通過的。也許有些人感到驚訝,沒錯(cuò)啊!但問題就出在@Target({ElementType.METHOD})上,由于Target使用了一個(gè)枚舉類型屬性,它的值是ElementType.METHOD。這就表明MyAnnotation只能為方法注釋。而不能為其它的任何語言元素進(jìn)行注釋。因此,MyAnnotation自然也不能為Class1進(jìn)行注釋了。

          說到這,大家可能已經(jīng)基本明白了。原來target所指的目標(biāo)就是java的語言元素。如類、接口、方法等。當(dāng)然,Target還可以對(duì)其它的語言元素進(jìn)行限制,如構(gòu)造函數(shù)、字段、參數(shù)等。如只允許對(duì)方法和構(gòu)造函數(shù)進(jìn)行注釋可以寫成:

          @Target({ElementType.METHOD,  ElementType.CONSTRUCTOR})

          @interface MyAnnotation {}

          Retention

          既然可以自定義注釋,當(dāng)然也可以讀取程序中的注釋(如何讀取注釋將在下一節(jié)中討論)。但是注釋只有被保存在class文件中才可以被讀出來。而Retention就是為設(shè)置注釋是否保存在class文件中而存在的。下面的代碼是Retention的詳細(xì)用法。

          @Retention(RetentionPolicy.SOURCE)

          @interface MyAnnotation1 { }

          @Retention(RetentionPolicy.CLASS)

          @interface MyAnnotation2 {}

          @Retention(RetentionPolicy.RUNTIME)

          @interface MyAnnotation3 {}

          其中第一段代碼的作用是不將注釋保存在class文件中,也就是說象“//”一樣在編譯時(shí)被過濾掉了。第二段代碼的作用是只將注釋保存在class文件中,而使用反射讀取注釋時(shí)忽略這些注釋。第三段代碼的作用是即將注釋保存在class文件中,也可以通過反射讀取注釋。

          Documented

           

          這個(gè)注釋和它的名子一樣和文檔有關(guān)。在默認(rèn)的情況下在使用javadoc自動(dòng)生成文檔時(shí),注釋將被忽略掉。如果想在文檔中也包含注釋,必須使用Documented為文檔注釋。


          @interface MyAnnotation{ }

          @MyAnnotation
          class Class1
          {    
              
          public void myMethod() { }
          }

          使用javadoc為這段代碼生成文檔時(shí)并不將@MyAnnotation包含進(jìn)去。生成的文檔對(duì)Class1的描述如下:

           class Class1extends java.lang.Object

          而如果這樣定義MyAnnotation將會(huì)出現(xiàn)另一個(gè)結(jié)果。

          @Documented

          @interface MyAnnotation {}

          生成的文檔:

          @MyAnnotation // 這行是在加上@Documented后被加上的

          class Class1extends java.lang.Object

          Inherited

          繼承是java主要的特性之一。在類中的protectedpublic成員都將會(huì)被子類繼承,但是父類的注釋會(huì)不會(huì)被子類繼承呢?很遺憾的告訴大家,在默認(rèn)的情況下,父類的注釋并不會(huì)被子類繼承。如果要繼承,就必須加上Inherited注釋。


          @Inherited
          @
          interface MyAnnotation { }

          @MyAnnotation
          public class ParentClass {}

          public class ChildClass extends ParentClass { }

          在以上代碼中ChildClassParentClass一樣都已被MyAnnotation注釋了。

          五、如何使用反射讀取注釋

          前面討論了如何自定義注釋。但是自定義了注釋又有什么用呢?這個(gè)問題才是J2SE5.0提供注釋的關(guān)鍵。自定義注釋當(dāng)然是要用的。那么如何用呢?解決這個(gè)問題就需要使用java最令人興奮的功能之一:反射(reflect)

          在以前的JDK版本中,我們可以使用反射得到類的方法、方法的參數(shù)以及其它的類成員等信息。那么在J2SE5.0中同樣也可以象方法一樣得到注釋的各種信息。

          在使用反射之前必須使用import java.lang.reflect.* 來導(dǎo)入和反射相關(guān)的類。

          如果要得到某一個(gè)類或接口的注釋信息,可以使用如下代碼:

          Annotation annotation = TestAnnotation.class.getAnnotation(MyAnnotation.class);

          如果要得到全部的注釋信息可使用如下語句:

          Annotation[] annotations = TestAnnotation.class.getAnnotations();

          Annotation[] annotations = TestAnnotation.class.getDeclaredAnnotations();

          getDeclaredAnnotationsgetAnnotations類似,但它們不同的是getDeclaredAnnotations得到的是當(dāng)前成員所有的注釋,不包括繼承的。而getAnnotations得到的是包括繼承的所有注釋。

          如果要得到其它成員的注釋,可先得到這個(gè)成員,然后再得到相應(yīng)的注釋。如得到myMethod的注釋。

          Method method = TestAnnotation.class.getMethod("myMethod", null);

          Annotation annotation = method.getAnnotation(MyAnnotation.class);

          注:要想使用反射得到注釋信息,這個(gè)注釋必須使用

          @Retention(RetentionPolicy.RUNTIME)進(jìn)行注釋。

          總結(jié)

          注釋是J2SE5.0提供的一項(xiàng)非常有趣的功能。它不但有趣,而且還非常有用。如即將出臺(tái)的EJB3.0規(guī)范就是借助于注釋實(shí)現(xiàn)的。這樣將使EJB3.0在實(shí)現(xiàn)起來更簡單,更人性化。還有Hibernate3.0除了使用傳統(tǒng)的方法生成hibernate映射外,也可以使用注釋來生成hibernate映射。總之,如果能將注釋靈活應(yīng)用到程序中,將會(huì)使你的程序更加簡潔和強(qiáng)大。






          Android開發(fā)完全講義(第2版)(本書版權(quán)已輸出到臺(tái)灣)

          http://product.dangdang.com/product.aspx?product_id=22741502



          Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


          新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

          posted on 2008-05-09 12:46 銀河使者 閱讀(3101) 評(píng)論(6)  編輯  收藏 所屬分類: java 原創(chuàng)

          評(píng)論

          # re: J2SE5.0中最有趣的新特性:注釋(annotation)[未登錄]  回復(fù)  更多評(píng)論   

          好像是《精通Hibernate》一書中關(guān)于注解的篇章,不知是否是抄襲,感覺抄襲可能性很大
          2008-05-09 13:35 | jones

          # re: J2SE5.0中最有趣的新特性:注釋(annotation)  回復(fù)  更多評(píng)論   

          很實(shí)用,謝謝!
          2008-05-09 14:01 | 宇義

          # re: J2SE5.0中最有趣的新特性:注釋(annotation)  回復(fù)  更多評(píng)論   

          本文是Java 1.5 Tiger: A Developer's Notebook 的學(xué)習(xí)總結(jié),沒看過《精通Hibernate》,中文的?誰寫的?如果有些雷同,估計(jì)這本英文書是最終的源頭了。

          注釋就這點(diǎn)東西,誰講都是這些內(nèi)容。上述這本英文書應(yīng)該是國外最早一批講j2se5.0的書(可是2004年6出的啊),那時(shí)國內(nèi)好象還沒有這種書。
          2008-05-09 14:19 | 銀河使者

          # re: J2SE5.0中最有趣的新特性:注釋(annotation)  回復(fù)  更多評(píng)論   

          支持原創(chuàng)!收藏了!
          2008-05-09 15:43 | BeanSoft

          # re: J2SE5.0中最有趣的新特性:注釋(annotation)  回復(fù)  更多評(píng)論   

          寫的極好,很有幫助。。。謝謝
          2008-07-22 13:59 | txf

          # re: J2SE5.0中最有趣的新特性:注釋(annotation)  回復(fù)  更多評(píng)論   

          明白
          2009-03-03 13:01 | wangs130
          主站蜘蛛池模板: 萨嘎县| 毕节市| 浦县| 琼结县| 永嘉县| 斗六市| 肇庆市| 尤溪县| 凉山| 鄯善县| 墨脱县| 大方县| 寻甸| 桦川县| 饶平县| 瑞丽市| 凤山县| 旺苍县| 盐源县| 南澳县| 汝州市| 汪清县| 育儿| 斗六市| 施甸县| 磐安县| 黄大仙区| 永靖县| 九龙坡区| 四子王旗| 武定县| 长宁区| 若尔盖县| 永修县| 安岳县| 玛沁县| 彩票| 社会| 镇平县| 镇雄县| 都江堰市|