java註解(不是注釋)

來源:互聯網
上載者:User

本文將向你介紹J2SE5.0中的新特性之一:註解。本文將從什麼是註解;J2SE5.0中預定義的註解;如何自訂註解;如何對註解進行註解以及如何在程式中讀取註解5個方面進行討論。

 

 

一、什麼是註解

    說起註解,得先提一提什麼是中繼資料(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
 {
     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的內容如下:

 

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

 

 

三、如何自訂註解

註解的強大之處是它不僅可以使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()
{
}

 


這一節討論了如何自訂註解。那麼定義註解有什麼用呢。有什麼方法對註解進行限制呢。我們能從程式中得到註解嗎。這些疑問都可以從下面的內容找到答案。

 

 

四、如何對註解進行註解

這一節的題目讀起來雖然有些繞口,但它所蘊涵的知識卻對設計更強大的java程式有很大協助。

在上一節討論了自訂註解,由此我們可知註解在J2SE5.0中也和類、介面一樣。是程式中的一個基本的組成部分。既然可以對類、介面進行註解,那麼當然也可以對註解進行註解。

使用普通註解對註解進行註解的方法和對類、介面進行註解的方法一樣。所不同的是,J2SE5.0為註解單獨提供了4種註解,即元註解。 元註解理解: 註解可以用於註解類(annotate Classes) 可以用於註解介面(annotate Interfaces) 可以用於註解枚舉類型(annotate Enums) 因此註解同樣也可以用於註解註解(annotate Annotations)

它們是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 Class1 extends java.lang.Object

 

而如果這樣定義MyAnnotation將會出現另一個結果。

@Documented

@interface MyAnnotation {}

產生的文檔:

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

class Class1 extends java.lang.Object

 

Inherited

繼承是java主要的特性之一。在類中的protected和public成員都將會被子類繼承,但是父類的註解會不會被子類繼承呢。很遺憾的告訴大家,在預設的情況下,父類的註解並不會被子類繼承。如果要繼承,就必須加上Inherited註解。


@Inherited
@interface MyAnnotation { }

@MyAnnotation
public 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的註解。

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

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

註:要想使用反射得到註解資訊,這個註解必須使用

@Retention(RetentionPolicy.RUNTIME)進行註解。

 

六、註解的本質:註解是一種類型 JDK5.0中的類型:1、類(class)2、介面(interface)3、枚舉(enum)4、註解(Annotation) 因此,註解與其他3種類型一樣,都可以定義、使用,以及包含有自己的屬性、方法  

七、註解的分類: (1)標記注釋:註解的內部沒有屬性,稱作標記註解 使用方法:@註解名 使用例子:@MarkAnnotation   (2)單值註解:註解的內部只有一個屬性,稱作單值註解 使用方法:@註解名(屬性名稱=屬性值) 使用例子:@SingleAnnotation(value="abc")  //也可以寫成@SingleAnnotation("abc") *(屬性名稱=屬性值)可以簡化為(屬性值),但是需要滿足以下兩個條件: 1、該註解必須為單值註解 2、該註解的屬性名稱必須為value   (3)多值註解:註解的內部有多個屬性,稱作多值註解 使用方法:@註解名(屬性名稱1=屬性值1, 屬性名稱2=屬性值2……) 使用例子:@MultipliedAnnotation(value1 = "abc", value2 = 30……)  

八、自動化的測試機的寫法: 自動化的測試機的原理:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.