Java註釋Override、Deprecated、SuppressWarnings詳解

1、什麼是註釋 

    提及註釋,得先提一提什麼是元數據(metadata)。所謂元數據就是數據的數據。也就是說,元數據是描述數據的。就象數據表中的字段同樣,每一個字段描述了這個字段下的數據的含義。而J2SE5.0中提供的註釋就是java源代碼的元數據,也就是說註釋是描述java源代碼的。在J2SE5.0中能夠自定義註釋。使用時在@後面跟註釋的名字。 
                                                                                    
2、J2SE5.0中預約義的註釋 

    在J2SE5.0的java.lang包中預約義了三個註釋。它們是Override、Deprecated和SuppressWarnings。下面分別解釋它們的含義。 

Override 

    這個註釋的做用是標識某一個方法是否覆蓋了它的父類的方法。那麼爲何要標識呢?讓咱們來看看若是不用Override標識會發生什麼事情。
java

    假設有兩個類Class1和ParentClass1,用Class1中的myMethod1方法覆蓋ParentClass1中的myMethod1方法。 

class ParentClass1 { public void myMethod1() {...} } class Class1 extends ParentClass1 { public void myMethod2() {...} }
創建Class1的實例,而且調用myMethod1方法 
ParentClass1 c1 = new Class1(); c1.myMethod1();
以上的代碼能夠正常編譯經過和運行。可是在寫Class1的代碼時,誤將myMethod1寫成了myMethod2,然而在調用時,myMethod1並未被覆蓋。所以,c1.myMethod1()調用的仍是ParentClass1的myMethod1方法。更不幸的是,程序員並未意識到這一點。所以,這可能會產生bug。 

   若是咱們使用Override來修飾Class1中的myMethod1方法,當myMethod1被誤寫成別的方法時,編譯器就會報錯。所以,就能夠避免這類錯誤。 

class Class1 extends ParentClass1 {  @Override // 編譯器產生一個錯誤 public void myMethod2() {...} }
以上代碼編譯不能經過,被Override註釋的方法必須在父類中存在一樣的方法程序才能編譯經過。也就是說只有下面的代碼才能正確編譯。 

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

Deprecated程序員

    這個註釋是一個標記註釋。所謂標記註釋,就是在源程序中加入這個標記後,並不影響程序的編譯,但有時編譯器會顯示一些警告信息。 
    
    那麼Deprecated註釋是什麼意思呢?若是你常用eclipse等IDE編寫java程序時,可能會常常在屬性或方法提示中看到這個詞。若是某個類成員的提示中出現了個詞,就表示這個並不建議使用這個類成員。由於這個類成員在將來的JDK版本中可能被刪除。之因此在如今還保留,是由於給那些已經使用了這些類成員的程序一個緩衝期。若是如今就去了,那麼這些程序就沒法在新的編譯器中編譯了。 

    說到這,可能你已經猜出來了。Deprecated註釋必定和這些類成員有關。說得對!使用Deprecated標註一個類成員後,這個類成員在顯示上就會有一些變化。在eclipse中很是明顯。讓咱們看看圖1有哪些變化。 


圖1 加上@Deprecated後的類成員在eclipse中的變化安全



    從上圖能夠看出,有三個地方發生的變化。紅色框裏面的是變化的部分。 
    1. 方法定義處 
    2. 方法引用處 
    3. 顯示的成員列表中 

    發生這些變化並不會影響編譯,只是提醒一下程序員,這個方法之後是要被刪除的,最好別用。 

    Deprecated註釋還有一個做用。就是若是一個類從另一個類繼承,而且override被繼承類的Deprecated方法,在編譯時將會出現一個警告。如test.java的內容以下: eclipse

class Class1 { @Deprecated public void myMethod(){} } class Class2 extends Class1 { public void myMethod(){} }
運行javac test.java 出現以下警告: 

    注意:test.java 使用或覆蓋了已過期的 API。 
    注意:要了解詳細信息,請使用 -Xlint:deprecation 從新編譯 
    使用-Xlint:deprecation顯示更詳細的警告信息: 

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

    public void myMethod() 
    ^ 
    1 警告 

    這些警告並不會影響編譯,只是提醒你一下儘可能不要用myMethod方法。 

    SuppressWarnings 

    這個世界的事物老是成對出現。即然有使編譯器產生警告信息的,那麼就有抑制編譯器產生警告信息的。 
    SuppressWarnings註釋就是爲了這樣一個目的而存在的。讓咱們先看一看以下的代碼。 

public void myMethod() { List wordList = new ArrayList(); wordList.add("foo"); }
這是一個類中的方法。編譯它,將會獲得以下的警告。 

    注意:Testannotation.java 使用了未經檢查或不安全的操做。 
    注意:要了解詳細信息,請使用 -Xlint:unchecked 從新編譯。 

    這兩行警告信息表示List類必須使用範型纔是安全的,才能夠進行類型檢查。若是想不顯示這個警告信息有兩種方法。一個是將這個方法進行以下改寫: 

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和前兩個註釋不同。這個註釋有一個屬性。固然,還能夠抑制其它警告,如:
@SuppressWarnings (value={"unchecked", "fallthrough"})

ide

3、如何自定義註釋 

    註釋的強大之處是它不只可使java程序變成自描述的,並且容許程序員自定義註釋。註釋的定義和接口差很少,只是在interface前面多了一個「@」。 函數

public @interface MyAnnotation { }
上面的代碼是一個最簡單的註釋。這個註釋沒有屬性。也能夠理解爲是一個標記註釋。就象Serializable接口同樣是一個標記接口,裏面未定義任何方法。 

    固然,也能夠定義有屬性的註釋。 
public @interface MyAnnotation {   String value(); }
能夠按以下格式使用MyAnnotation 
@MyAnnotation("abc") public void myMethod() { }
看了上面的代碼,你們可能有一個疑問。怎麼沒有使用value,而直接就寫」abc」了。那麼」abc」到底傳給誰了。其實這裏有一個約定。若是沒有寫屬性名的值,而這個註釋又有value屬性,就將這個值賦給value屬性,若是沒有,就出現編譯錯誤。 

    除了能夠省略屬性名,還能夠省略屬性值。這就是默認值。 
public @interface MyAnnotation {   public String myMethod(){} default 「xyz」; }
能夠直接使用MyAnnotation 
@MyAnnotation // 使用默認值xyz public void myMethod() { }
也能夠這樣使用 
@MyAnnotation(myMethod=」abc」) public void myMethod() { }

    若是要使用多個屬性的話。能夠參考以下代碼。 
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() { }
這一節討論瞭如何自定義註釋。那麼定義註釋有什麼用呢?有什麼方法對註釋進行限制呢?咱們能從程序中獲得註釋嗎?這些疑問均可以從下面的內容找到答案。




4、如何對註釋進行註釋
 

    這一節的題目讀起來雖然有些繞口,但它所蘊涵的知識卻對設計更強大的java程序有很大幫助。 
在上一節討論了自定義註釋,由此咱們可知註釋在J2SE5.0中也和類、接口同樣。是程序中的一個基本的組成部分。既然能夠對類、接口進行註釋,那麼固然也能夠對註釋進行註釋。 
    使用普通註釋對註釋進行註釋的方法和對類、接口進行註釋的方法同樣。所不一樣的是,J2SE5.0爲註釋單獨提供了4種註釋。它們是Target、Retention、Documented和Inherited。下面就分別介紹這4種註釋。 

   Target 
   這個註釋理解起來很是簡單。因爲target的中文意思是「目標」,所以,咱們可能已經猜到這個註釋和某一些目標相關。那麼這些目標是指什麼呢?你們能夠先看看下面的代碼。 

@Target( ElementType.METHOD) @interface MyAnnotation {} @MyAnnotation // 錯誤的使用 public class Class1 { @MyAnnotation // 正確的使用 public void myMethod1() {} }
    以上代碼定義了一個註釋MyAnnotation和一個類Class1,而且使用MyAnnotation分別對Class1和myMethod1進行註釋。若是編譯這段代碼是沒法經過的。也許有些人感到驚訝,沒錯啊!但問題就出在@Target(ElementType.METHOD)上,因爲Target使用了一個枚舉類型屬性,它的值是ElementType.METHOD。這就代表MyAnnotation只能爲方法註釋。而不能爲其它的任何語言元素進行註釋。所以,MyAnnotation天然也不能爲Class1進行註釋了。 
   
說到這,你們可能已經基本明白了。原來target所指的目標就是java的語言元素。如類、接口、方法等。固然,Target還能夠對其它的語言元素進行限制,如構造函數、字段、參數等。如只容許對方法和構造函數進行註釋能夠寫成: 

@Target( {ElementType.METHOD, ElementType.CONSTRUCTOR}) @interface MyAnnotation {}
Retention 
     既然能夠自定義註釋,固然也能夠讀取程序中的註釋(如何讀取註釋將在下一節中討論)。可是註釋只有被保存在class文件中才能夠被讀出來。而Retention就是爲設置註釋是否保存在class文件中而存在的。下面的代碼是Retention的詳細用法。 

@Retention(RetentionPolicy.SOURCE) @interface MyAnnotation1 { } @Retention(RetentionPolicy.CLASS) @interface MyAnnotation2 {} @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation3 {}
    其中第一段代碼的做用是不將註釋保存在class文件中,也就是說象「//」同樣在編譯時被過濾掉了。第二段代碼的做用是隻將註釋保存在class文件中,而使用反射讀取註釋時忽略這些註釋。第三段代碼的做用是即將註釋保存在class文件中,也能夠經過反射讀取註釋。 

Documented 

    這個註釋和它的名子同樣和文檔有關。在默認的狀況下在使用javadoc自動生成文檔時,註釋將被忽略掉。若是想在文檔中也包含註釋,必須使用Documented爲文檔註釋。 
@interface MyAnnotation { } @MyAnnotation class Class1 { public void myMethod() { } }
使用javadoc爲這段代碼生成文檔時並不將@MyAnnotation包含進去。生成的文檔對Class1的描述以下: 
class Class1extends java.lang.Object 而若是這樣定義MyAnnotation將會出現另外一個結果。 @Documented @interface MyAnnotation {} 生成的文檔: @MyAnnotation // 這行是在加上@Documented後被加上的 class Class1extends java.lang.Object
Inherited

     繼承是java主要的特性之一。在類中的protected和public成員都將會被子類繼承,可是父類的註釋會不會被子類繼承呢?很遺憾的告訴你們,在默認的狀況下,父類的註釋並不會被子類繼承。若是要繼承,就必須加上Inherited註釋。 
@Inherited @interface MyAnnotation { } @MyAnnotation public class ParentClass {} public class ChildClass extends ParentClass { } 在以上代碼中ChildClass和ParentClass同樣都已被MyAnnotation註釋了。

5、如何使用反射讀取註釋 

    前面討論瞭如何自定義註釋。可是自定義了註釋又有什麼用呢?這個問題纔是J2SE5.0提供註釋的關鍵。自定義註釋固然是要用的。那麼如何用呢?解決這個問題就須要使用java最使人興奮的功能之一:反射(reflect)。 
在之前的JDK版本中,咱們可使用反射獲得類的方法、方法的參數以及其它的類成員等信息。那麼在J2SE5.0中一樣也能夠象方法同樣獲得註釋的各類信息。 

    在使用反射以前必須使用import java.lang.reflect.* 來導入和反射相關的類。 
    若是要獲得某一個類或接口的註釋信息,可使用以下代碼: 

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

若是要獲得所有的註釋信息可以使用以下語句:
Annotation[] annotations = TestAnnotation.class.getAnnotations();

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

getDeclaredAnnotations與getAnnotations相似,但它們不一樣的是getDeclaredAnnotations獲得的是當前成員全部的註釋,不包括繼承的。而getAnnotations獲得的是包括繼承的全部註釋。 

    若是要獲得其它成員的註釋,可先獲得這個成員,而後再獲得相應的註釋。如獲得myMethod的註釋。
spa

Method method = TestAnnotation.class.getMethod("myMethod", null); Annotation annotation = method.getAnnotation(MyAnnotation.class); 注:要想使用反射獲得註釋信息,這個註釋必須使用 @Retention(RetentionPolicy.RUNTIME)進行註釋。

總結 
    註釋是J2SE5.0提供的一項很是有趣的功能。它不但有趣,並且還很是有用。EJB3規範就是藉助於註釋實現的。這樣將使EJB3在實現起來更簡單,更人性化。還有Hibernate3除了使用傳統的方法生成hibernate映射外,也可使用註釋來生成hibernate映射。總之,若是能將註釋靈活應用到程序中,將會使你的程序更加簡潔和強大。hibernate

相關文章
相關標籤/搜索