1. 程式人生 > >Java註釋Override、Deprecated、SuppressWarnings詳解

Java註釋Override、Deprecated、SuppressWarnings詳解

一、什麼是註釋

    說起註釋,得先提一提什麼是元資料(metadata)。所謂元資料就是資料的資料。也就是說,元資料是描述資料的。就象資料表中的欄位一樣,每個欄位描述了這個欄位下的資料的含義。而J2SE5.0中提供的註釋就是java原始碼的元資料,也就是說註釋是描述java原始碼的。在J2SE5.0中可以自定義註釋。使用時在@後面跟註釋的名字。
                                                                                    
二、J2SE5.0中預定義的註釋


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

Override


    這個註釋的作用是標識某一個方法是否覆蓋了它的父類的方法。那麼為什麼要標識呢?讓我們來看看如果不用Override標識會發生什麼事情。     假設有兩個類Class1和ParentClass1,用Class1中的myMethod1方法覆蓋ParentClass1中的myMethod1方法。

class ParentClass1 { publicvoid myMethod1() {...} } class Class1 extends ParentClass1
{ publicvoid 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 // 編譯器產生一個錯誤 publicvoid myMethod2() {...} } 以上程式碼編譯不能通過,被Override註釋的方法必須在父類中存在同樣的方法程式才能編譯通過。也就是說只有下面的程式碼才能正確編譯。

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

Deprecated

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

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


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



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

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

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

class Class1 { @Deprecated publicvoid myMethod(){} } class Class2 extends Class1 { publicvoid myMethod(){} } 執行javac test.java 出現如下警告:

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

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

    public void myMethod()
    ^
    1 警告

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

    SuppressWarnings


    這個世界的事物總是成對出現。即然有使編譯器產生警告資訊的,那麼就有抑制編譯器產生警告資訊的。
    SuppressWarnings註釋就是為了這樣一個目的而存在的。讓我們先看一看如下的程式碼。

publicvoid 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"})

三、如何自定義註釋

    註釋的強大之處是它不僅可以使java程式變成自描述的,而且允許程式設計師自定義註釋。註釋的定義和介面差不多,只是在interface前面多了一個“@”。

public @interface MyAnnotation { } 上面的程式碼是一個最簡單的註釋。這個註釋沒有屬性。也可以理解為是一個標記註釋。就象Serializable介面一樣是一個標記介面,裡面未定義任何方法。

    當然,也可以定義有屬性的註釋。
public @interface MyAnnotation {   String value(); } 可以按如下格式使用MyAnnotation
@MyAnnotation("abc") publicvoid myMethod() { } 看了上面的程式碼,大家可能有一個疑問。怎麼沒有使用value,而直接就寫”abc”了。那麼”abc”到底傳給誰了。其實這裡有一個約定。如果沒有寫屬性名的值,而這個註釋又有value屬性,就將這個值賦給value屬性,如果沒有,就出現編譯錯誤。

    除了可以省略屬性名,還可以省略屬性值。這就是預設值。
public @interface MyAnnotation {   public String myMethod(){}default “xyz”; } 可以直接使用MyAnnotation
@MyAnnotation // 使用預設值xyz publicvoid myMethod() { } 也可以這樣使用
@MyAnnotation(myMethod=”abc”) publicvoid myMethod() { }
    如果要使用多個屬性的話。可以參考如下程式碼。
public @interface MyAnnotation { publicenum MyEnum{A, B, C} public MyEnum.value1() {} public String value2() {} } @MyAnnotation(value1=MyAnnotation.MyEnum.A, value2 = “xyz”) publicvoid myMethod() { } 這一節討論瞭如何自定義註釋。那麼定義註釋有什麼用呢?有什麼方法對註釋進行限制呢?我們能從程式中得到註釋嗎?這些疑問都可以從下面的內容找到答案。


四、如何對註釋進行註釋


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

   Target
   這個註釋理解起來非常簡單。由於target的中文意思是“目標”,因此,我們可能已經猜到這個註釋和某一些目標相關。那麼這些目標是指什麼呢?大家可以先看看下面的程式碼。

@Target(ElementType.METHOD) @interface MyAnnotation {} @MyAnnotation // 錯誤的使用 publicclass Class1 { @MyAnnotation // 正確的使用 publicvoid 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 { publicvoid 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 publicclass ParentClass {} publicclass ChildClass extends ParentClass { } 在以上程式碼中ChildClass和ParentClass一樣都已被MyAnnotation註釋了。

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

    前面討論瞭如何自定義註釋。但是自定義了註釋又有什麼用呢?這個問題才是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的註釋。

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

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

轉自【來自網路,原始出處已不可考】