Java注解 Annotation


Java注解 Annotation

 

Annotation的由來

  從JDK5.0發布以來,5.0平台提供了一個正式的annotation功能:允許開發者定義、使用自己的annotation類型。

  此功能由一個定義annotation類型的語法和一個描述annotation聲明的語法,讀取annotation的API,一個使用annotation修飾的class文件,一個annotation處理工具(apt)組成。

 

Annotation工作方式

  Annotation並不直接影響代碼語義,但是它能夠被看作類似程序的工具或者類庫,它會反過來對正在運行的程序語義有所影響。

  Annotation可以從源文件、class文件、或者以在運行時反射的多種方式被讀取。

 

JDK中內置的注解

  Java注解Annotation:

  Override注解(@Override)表示子類要重寫父類的對應方法。

  Deprecated注解(@Deprecated)表示方法是不建議被使用的。

  SuppressWarnings注解(如:@SuppressWarnings("unchecked"))表示抑制警告。

  括號中的參數(一個字符串數組)表示要壓制的警告類型。單個字符串的時候可以用也可以不用花括號,但是多個字符串的時候需要用花括號包起來表示,

  比如:    

  @SuppressWarnings({"unchecked","deprecation"})

  如果注解一個類去壓制一種警告,再注解類中的方法取壓制另一種警告,則方法中會同時壓制這兩種警告。

 

Annotation的定義方式

  自己定義注解:

  新建->Annotation.

  可以看到注解定義的形式與interface類似,不同的是在interface關鍵字前面加了一個@符號。

  自定義注解:當注解中的屬性名為value時,在對其賦值時可以不指定屬性的名稱而直接寫上屬性值即可;除了value以外的其他值都需要使用name=value這種顯式賦值方式,即明確指定給誰賦值。

  程序示例1  

注解程序示例1——自定義注解
public @interface AnnotationTest
{

    String value() default "hello";
    //不加()就出錯了
    //屬性名字叫作value時可以不加名字直接賦值
    //屬性名字不叫作value時給屬性賦值必須顯式指定名字
    //可以通過default關鍵字設置默認值
}

 

注解程序示例1——使用自定義注解
//使用自定義的注解:
@AnnotationTest(value = "hello")
// 自定義注解中加了String value屬性之后需要對屬性進行賦值,當屬性有默認值之后可以不賦值
public class AnnotationUsage
{
    // 使用自定義的注解:
    @AnnotationTest("world")
    // 不加value=也是可以的
    public void method()
    {
        System.out.println("usage of annotation!");
    }

    public static void main(String[] args)
    {
        AnnotationUsage usage = new AnnotationUsage();
        usage.method();
    }

}

 

  可以使用關鍵字default給屬性設定默認值,如:String value() default "hello";

  程序示例2  

注解程序示例2
public @interface AnnotationTest
{

    String[] value1() default "hello";
    // 不加()就出錯了
    // 屬性名字叫作value時可以不加名字直接賦值
    // 屬性名字不叫作value時給屬性賦值必須顯式指定名字
    // 可以通過default關鍵字設置默認值
    
    EnumTest value2();
}

enum EnumTest
{
    Hello, World, Welcome;
}
//使用自定義的注解:
@AnnotationTest(value2 = EnumTest.Hello)
// 自定義注解中加了屬性之后需要對屬性進行賦值,當屬性有默認值之后可以不賦值
public class AnnotationUsage
{
    // 使用自定義的注解:
    @AnnotationTest(value1={"Hello","world"}, value2 = EnumTest.World)
    //value1現在是數組,所以可以賦多個值
    public void method()
    {
        System.out.println("usage of annotation!");
    }

    public static void main(String[] args)
    {
        AnnotationUsage usage = new AnnotationUsage();
        usage.method();
    }

}

 

  使用@interface自行定義注解時,實際上是自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動為您完成其他產生的細節。

  在定義Annotation型態時,不能繼承其他的Annotation型態或是接口。

  要想定義注解只能通過@interface關鍵字來定義,手動繼承Annotation接口並不能定義一個注解類型,即如果定義一個接口繼承了Annotation,那么該接口仍然只是一個接口而不是注解。注意Annotation接口本身並不是注解。

  定義Annotation型態時也可以使用包來管理類別,方式類同於類的導入功能。

 

告知編譯程序如何處理@Retention

  java.lang.annotation.Retention型態可以在您定義Annotation型態時,指示編譯程序該如何對待您自定義的Annotation型態。

  預設上編譯程序會將Annotation信息留在.class檔案中,但不被虛擬機讀取,而僅用於編譯程序或工具程序運行時提供信息。

  在使用Retention型態時,需要提供

  java.lang.annotation.RetentionPolicy的枚舉型態:

  

package java.lang.annotation;

public enum RetentionPolicy
{
     SOURCE,   //編譯程序處理完Annotation信息后就完成任務,注解只會存在於源文件當中,編譯器將其丟棄,不會把注解編譯到class文件當中,

      CLASS,      //編譯程序將Annotation存儲於class檔中,缺省,即默認情況是這種行為

      RUNTIME   //編譯程序將Annotation存儲於class檔中,可由VM讀入,可以通過反射的方式讀取到

}

 

  RetentionPolicy為SOURCE的例子是@SuppressWarnings。

  僅在編譯時期告知編譯程序來抑制警告,所以不必將這個信息存儲於.class檔案。

 

  RetentionPolicy為RUNTIME的時機,可以像是您使用Java設計一個程序代碼分析工具,您必須讓VM能讀出Annotation信息,以便在分析程序時使用。搭配反射(Reflection)機制,就可以達到這個目的。

  java.lang.reflect.AnnotatedElement接口。其中包含了這樣幾個方法:

    <T extends Annotation> T getAnnotation(Class<T> annotationClass)

    Annotation[] getAnnotations()

    Annotation[] getDeclaredAnnotations()

    boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

  Class, Constructor, Method, Field, Package等類別,都實現了AnnotatedElement接口

  程序示例3——通過反射獲取注解信息

  首先自定義注解類型: 

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation
{
    String hello() default "shengqishi";
    String world();

}

  之后在類中使用自定義的注解:

@MyAnnotation(hello="beijing",world="tianjin")
public class MyTest
{

    @MyAnnotation(hello="shanghai",world="guangzhou")
    @Deprecated
    @SuppressWarnings("unchecked")
    public void output()
    {
        System.out.println("Output something.");
    }
}

  通過反射獲取注解信息:  

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

//通過反射獲取注解信息
public class MyReflection
{

    public static void main(String[] args) throws Exception
    {
        MyTest myTest = new MyTest();

        // 獲取Class對象
        Class<MyTest> c = MyTest.class;

        // 獲取Method對象
        Method method = c.getMethod("output", new Class[] {});

        // 判斷是否存在指定類型的注解
        if (method.isAnnotationPresent(MyAnnotation.class))
        {
            // 如果存在該類型注解,這執行這個方法
            method.invoke(myTest, new Object[] {});

          // 如果MyAnnotation前面是@Retention(RetentionPolicy.RUNTIME),則執行
            // 如果MyAnnotation前面是@Retention(RetentionPolicy.CLASS),或SOURCE,則括號內語句不執行
            // 因為只有為RUNTIME時,注解信息會被讀取過來,其他兩種情況注解不能被反射讀取過來

            // 返回注解
            MyAnnotation myAnnotation = method
                    .getAnnotation(MyAnnotation.class);

            // 獲取並打印注解中的信息
            String hello = myAnnotation.hello();
            String world = myAnnotation.world();
            System.out.println(hello + ", " + world);
        }

        // 獲取方法的全部的Annotation
        Annotation[] annotations = method.getAnnotations();
        for (Annotation annotation : annotations)
        {
            System.out.println(annotation.annotationType().getName());
        }
        // 運行結果為:
        // com.learnjava.annotation.MyAnnotation
        // java.lang.Deprecated
        // 因為只能獲取到RetentionPolicy為RUNTIME的注解

    }
}

 

 

限定annotation使用對象@Target

  使用java.lang.annotation.Target可以定義其使用之時機。即表示注解可以修飾什么(修飾類、方法、注解等)。

  在定義時要指定java.lang.annotation.ElementType的枚舉值之一。

  

package java.lang.annotation;

public enum ElementType
{
      TYPE,   //適用class,interface,enum

      FIELD, //適用field

      METHOD,   //適用method

      PARAMETER,   //適用method上之parameter

      CONSTRUCTOR,    //適用constructor

      LOCAL_VARIABLE,     //適用局部變量

      ANNOTATION_TYPE,   //適用annotation型態

      PACKAGE,   //使用package
}

 

  程序示例4——設定注解目標  

注解Target設定示例
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;


@Target(ElementType.METHOD)
public @interface MyTarget
{
    String value();

}

public class MyTargetTest
{
    @MyTarget("hello")
    // 此注解只能被用於修飾方法,放在別的地方會出錯
    public void doSomething()
    {
        System.out.println("Do something!");
    }

}

  

要求為API文件@Documented

  想要在使用者制作JavaDoc文件的同時,也一並將Annotation的訊息加入API文件中,使用

  java.lang.annotation.Documented

  程序示例5——注解加入文檔 

加入文檔的注解
import java.lang.annotation.Documented;

@Documented
// 表示注解DocumentedAnnotation將會生成到文檔里面去
public @interface DocumentedAnnotation
{
    String hello();

}



public class DocumentedTest
{
    @DocumentedAnnotation(hello="welcome")
    public void method()
    {
        System.out.println("Hello World!");        
    }

}

  

子類是否繼承父類@Inherited

  缺省狀態下,父類中的annotation並不會被子類繼承。

  可以在定義Annotation型態時加上java.lang.annotation.Inherited型態的Annotation。

  比較簡單,不再舉例。

 

參考資料

  張龍老師Java SE視頻教程。

  另:官方文檔是最好的參考資料。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM