Java注释Override、Deprecated、SuppressWarnings详解
http://blog.csdn.net/softwave/article/details/6991913
一、什么是注釋?
??? 說起注釋,得先提一提什么是元數據(metadata)。所謂元數據就是數據的數據。也就是說,元數據是描述數據的。就象數據表中的字段一樣,每個字段描述了這個字段下的數據的含義。而J2SE5.0中提供的注釋就是java源代碼的元數據,也就是說注釋是描述java源代碼的。在J2SE5.0中可以自定義注釋。使用時在@后面跟注釋的名字。?
????????????????????????????????????????????????????????????????????????????????????
二、J2SE5.0中預定義的注釋?
??? 在J2SE5.0的java.lang包中預定義了三個注釋。它們是Override、Deprecated和SuppressWarnings。下面分別解釋它們的含義。?
Override?
??? 這個注釋的作用是標識某一個方法是否覆蓋了它的父類的方法。那么為什么要標識呢?讓我們來看看如果不用Override標識會發生什么事情。
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{@Overridepublic void myMethod1() {...}}
Deprecated
????這個注釋是一個標記注釋。所謂標記注釋,就是在源程序中加入這個標記后,并不影響程序的編譯,但有時編譯器會顯示一些警告信息。?
????
??? 那么Deprecated注釋是什么意思呢?如果你經常使用eclipse等IDE編寫java程序時,可能會經常在屬性或方法提示中看到這個詞。如果某個類成員的提示中出現了個詞,就表示這個并不建議使用這個類成員。因為這個類成員在未來的JDK版本中可能被刪除。之所以在現在還保留,是因為給那些已經使用了這些類成員的程序一個緩沖期。如果現在就去了,那么這些程序就無法在新的編譯器中編譯了。?
??? 說到這,可能你已經猜出來了。Deprecated注釋一定和這些類成員有關。說得對!使用Deprecated標注一個類成員后,這個類成員在顯示上就會有一些變化。在eclipse中非常明顯。讓我們看看圖1有哪些變化。?
圖1 加上@Deprecated后的類成員在eclipse中的變化
??? 從上圖可以看出,有三個地方發生的變化。紅色框里面的是變化的部分。?
??? 1. 方法定義處?
??? 2. 方法引用處?
??? 3. 顯示的成員列表中?
??? 發生這些變化并不會影響編譯,只是提醒一下程序員,這個方法以后是要被刪除的,最好別用。?
??? Deprecated注釋還有一個作用。就是如果一個類從另外一個類繼承,并且override被繼承類的Deprecated方法,在編譯時將會出現一個警告。如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"})
三、如何自定義注釋?
??? 注釋的強大之處是它不僅可以使java程序變成自描述的,而且允許程序員自定義注釋。注釋的定義和接口差不多,只是在interface前面多了一個“@”。?
??? 當然,也可以定義有屬性的注釋。?
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 // 使用默認值xyzpublic void myMethod(){} 也可以這樣使用?
@MyAnnotation(myMethod=”abc”)public void myMethod(){}
??? 如果要使用多個屬性的話??梢詤⒖既缦麓a。?
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(){} 這一節討論了如何自定義注釋。那么定義注釋有什么用呢?有什么方法對注釋進行限制呢?我們能從程序中得到注釋嗎?這些疑問都可以從下面的內容找到答案。
四、如何對注釋進行注釋?
??? 這一節的題目讀起來雖然有些繞口,但它所蘊涵的知識卻對設計更強大的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{ }@MyAnnotationclass 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 { }@MyAnnotationpublic class ParentClass {}public class 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的注釋。
總結?
??? 注釋是J2SE5.0提供的一項非常有趣的功能。它不但有趣,而且還非常有用。EJB3規范就是借助于注釋實現的。這樣將使EJB3在實現起來更簡單,更人性化。還有Hibernate3除了使用傳統的方法生成hibernate映射外,也可以使用注釋來生成hibernate映射??傊?#xff0c;如果能將注釋靈活應用到程序中,將會使你的程序更加簡潔和強大。
Annotation在java的世界正鋪天蓋地展開,有空寫這一篇簡單的annotations的文章,算是關于Annotation入門的文章吧,希望能各位們能拋磚,共同學習......?
?? 不講廢話了,實踐才是硬道理.?
???第一部分:了解一下java1.5起默認的三個annotation類型:?
?? 一個是@Override:只能用在方法之上的,用來告訴別人這一個方法是改寫父類的。?
?? 一個是@Deprecated:建議別人不要使用舊的API的時候用的,編譯的時候會用產生警告信息,可以設定在程序里的所有的元素上.?
?? 一個是@SuppressWarnings:這一個類型可以來暫時把一些警告信息消息關閉.?
?? 如果不清楚上面三個類型的具體用法,各位可以baidu或google一下的,很簡單的。?
???第二部分:講一下annotation的概念先,再來講一下怎樣設計自己的annotation.?
?? 首先在jdk自帶的java.lang.annotation包里,打開如下幾個源文件:?
???
?? 1、源文件Target.java?
???
?? 其中的@interface是一個關鍵字,在設計annotations的時候必須把一個類型定義為@interface,而不能用class或interface關鍵字(會不會覺得sun有點吝嗇,偏偏搞得與interface這么像).?
???
?? 2、源文件Retention.java?
Java代碼??
?? 看到這里,大家可能都模糊了,都不知道在說什么,別急,往下看一下.?
?? 在上面的文件都用到了RetentionPolicy,ElementType這兩個字段,你可能就會猜到這是兩個java文件.的確,這兩個文件的源代碼如下:?
???
?? 3、源文件RetentionPolicy.java?
Java代碼??
?? 這是一個enum類型,共有三個值,分別是SOURCE,CLASS 和 RUNTIME.?
?? SOURCE代表的是這個Annotation類型的信息只會保留在程序源碼里,源碼如果經過了編譯之后,Annotation的數據就會消失,并不會保留在編譯好的.class文件里面。?
?? ClASS的意思是這個Annotation類型的信息保留在程序源碼里,同時也會保留在編譯好的.class文件里面,在執行的時候,并不會把這一些信息加載到虛擬機(JVM)中去.注意一下,當你沒有設定一個Annotation類型的Retention值時,系統默認值是CLASS.?
?? 第三個,是RUNTIME,表示在源碼、編譯好的.class文件中保留信息,在執行的時候會把這一些信息加載到JVM中去的.?
舉一個例子,如@Override里面的Retention設為SOURCE,編譯成功了就不要這一些檢查的信息;相反,@Deprecated里面的Retention設為RUNTIME,表示除了在編譯時會警告我們使用了哪個被Deprecated的方法,在執行的時候也可以查出該方法是否被Deprecated.?
?? 4、源文件ElementType.java?
Java代碼??
?? @Target里面的ElementType是用來指定Annotation類型可以用在哪一些元素上的.說明一下:TYPE(類型), FIELD(屬性), METHOD(方法), PARAMETER(參數), CONSTRUCTOR(構造函數),LOCAL_VARIABLE(局部變量), ANNOTATION_TYPE,PACKAGE(包),其中的TYPE(類型)是指可以用在Class,Interface,Enum和Annotation類型上.?
?? 另外,從1的源代碼可以看出,@Target自己也用了自己來聲明自己,只能用在ANNOTATION_TYPE之上.?
?? 如果一個Annotation類型沒有指明@Target使用在哪些元素上,那么它可以使用在任何元素之上,這里的元素指的是上面的八種類型.?
?? 舉幾個正確的例子:?
?? @Target(ElementType.METHOD)?
?? @Target(value=ElementType.METHOD)?
?? @Target(ElementType.METHOD,ElementType.CONSTRUCTOR)???
?? 具體參考一下javadoc文檔?
???
?? 上面一下1和2的源文件,它們都使用了@Documented,@Documented的目的就是讓這一個Annotation類型的信息能夠顯示在javaAPI說明文檔上;沒有添加的話,使用javadoc生成API文檔的時候就會找不到這一個類型生成的信息.?
?? 另外一點,如果需要把Annotation的數據繼承給子類,那么就會用到@Inherited這一個Annotation類型.?
???
???第三部分:下面講的設計一個最簡單的Annotation例子,這一例子共用四個文件;?
?? 1、Description.java?
Java代碼??
???
?? 說明:所有的Annotation會自動繼承java.lang.annotation這一個接口,所以不能再去繼承別的類或是接口.?
?? 最重要的一點,Annotation類型里面的參數該怎么設定:?
?? 第一,只能用public或默認(default)這兩個訪問權修飾.例如,String value();這里把方法設為defaul默認類型.?
?? 第二,參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這里的參數成員就為String.?
?? 第三,如果只有一個參數成員,最好把參數名稱設為"value",后加小括號.例:上面的例子就只有一個參數成員.?
?? 2、Name.java?
Java代碼??
?? 3、JavaEyer.java?
Java代碼??
?? 4、最后,寫一個可以運行提取JavaEyer信息的類TestAnnotation?
Java代碼??
??? 5、運行結果:?
???? 描述:javaeye,做最棒的軟件開發交流社區?
???? -----------------?
??? 創始人:robbin?
??? 創建的社區:javaEye?
??? 創始人:江南白衣?
???? 創建的社區:springside?
總結
以上是生活随笔為你收集整理的Java注释Override、Deprecated、SuppressWarnings详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 字符串 数组互转
- 下一篇: 通配符在泛型中的应用