註解(Annotation)

轉:http://www.cnblogs.com/xdp-gacl/p/3622275.html

 一、認識註解

  註解(Annotation)很重要,未來的開發模式都是基於註解的,JPA是基於註解的,Spring2.5以上都是基於註解的,Hibernate3.x以後也是基於註解的,現在的Struts2有一部分也是基於註解的了,註解是一種趨勢,現在已經有不少的人開始用註解了,註解是JDK1.5之後纔有的新特性

JDK1.5之後內部提供的三個註解

       @Deprecated 意思是“廢棄的,過時的

       @Override 意思是“重寫、覆蓋

       @SuppressWarnings 意思是“壓縮警告

範例:註解的應用:

package cn.gacl.annotation;
/**
 * 此類是用來演示註解(Annotation)的應用的,註解也是JDK1.5新增加的特性之一
 * JDK1.5內部提供的三種註解是:@SuppressWarnings(":deprecation")、@Deprecated、@Override
 * @author 孤傲蒼狼
 *
 */
/**
 * 類名的命名是有講究的,類名、屬性名、變量名一般是名詞,或者是形容詞+名詞,方法一般是動詞,或者是動詞+名詞,
 * 以AnnotationTest作爲類名和以TestAnnotation作爲類名是有區別的,
 * 前者是註解的測試,符合名詞的特徵,後者是測試註解,聽起來就是一個動作名稱,是方法的命名特徵
 */
public class AnnotationTest {
    /**
     * @param args
     */
    @SuppressWarnings(":deprecation")
    //這裏就是註解,稱爲壓縮警告,這是JDK內部自帶的一個註解,一個註解就是一個類,在這裏使用了這個註解就是創建了SuppressWarnings類的一個實例對象
    public static void main(String[] args) {
        System.runFinalizersOnExit(true);
        //The method runFinalizersOnExit(boolean) from the type System is deprecated(過時的,廢棄的)
        //這裏的runFinalizersOnExit()方法畫了一條橫線表示此方法已經過時了,不建議使用了
    }
    @Deprecated //這也是JDK內部自帶的一個註解,意思就是說這個方法已經廢棄了,不建議使用了
    public static void sayHello(){
        System.out.println("hi,孤傲蒼狼");
    }
    @Override //這也是JDK1.5之後內部提供的一個註解,意思就是要重寫(覆蓋)JDK內部的toString()方法
    public String toString(){
        return "孤傲蒼狼";
    }
}

總結:註解(Annotation)相當於一種標記,在程序中加入註解就等於爲程序打上某種標記,沒有加,則等於沒有任何標記,以後,javac編譯器、開發工具和其他程序可以通過反射來了解你的類及各種元素上有無何種標記,看你的程序有什麼標記,就去幹相應的事,標記可以加在包、類,屬性、方法,方法的參數以及局部變量上。

註解就相當於一個你的源程序要調用一個類,在源程序中應用某個註解,得事先準備好這個註解類。就像你要調用某個類,得事先開發好這個類。

二、自定義註解及其應用

  自定義一個最簡單的註解:

public @interface MyAnnotation{}

package cn.gacl.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 這是一個自定義的註解(Annotation)類 在定義註解(Annotation)類時使用了另一個註解類Retention
 * 在註解類上使用另一個註解類,那麼被使用的註解類就稱爲元註解
 * 
 * @author 孤傲蒼狼
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
//Retention註解決定MyAnnotation註解的生命週期
@Target( { ElementType.METHOD, ElementType.TYPE })
//Target註解決定MyAnnotation註解可以加在哪些成分上,如加在類身上,或者屬性身上,或者方法身上等成分
/*
 * @Retention(RetentionPolicy.SOURCE)
 * 這個註解的意思是讓MyAnnotation註解只在java源文件中存在,編譯成.class文件後註解就不存在了
 * @Retention(RetentionPolicy.CLASS)
 * 這個註解的意思是讓MyAnnotation註解在java源文件(.java文件)中存在,編譯成.class文件後註解也還存在,
 * 被MyAnnotation註解類標識的類被類加載器加載到內存中後MyAnnotation註解就不存在了
 */
/*
 * 這裏是在註解類MyAnnotation上使用另一個註解類,這裏的Retention稱爲元註解。
 * Retention註解括號中的"RetentionPolicy.RUNTIME"意思是讓MyAnnotation這個註解的生命週期一直程序運行時都存在
 */
public @interface MyAnnotation {
}

把自定義的註解加到某個類上:

@MyAnnotation 
public class AnnotationUse{
}
用反射測試進行測試AnnotationUse的定義上是否有@MyAnnotation

package cn.gacl.annotation;
@MyAnnotation
//這裏是將新創建好的註解類MyAnnotation標記到AnnotaionTest類上
public class AnnotationUse {
    public static void main(String[] args) {
        // 這裏是檢查Annotation類是否有註解,這裏需要使用反射才能完成對Annotation類的檢查
        if (AnnotationUse.class.isAnnotationPresent(MyAnnotation.class)) {
            /*
             * MyAnnotation是一個類,這個類的實例對象annotation是通過反射得到的,這個實例對象是如何創建的呢?
             * 一旦在某個類上使用了@MyAnnotation,那麼這個MyAnnotation類的實例對象annotation就會被創建出來了
             * 假設很多人考駕照,教練在有些學員身上貼一些綠牌子、黃牌子,貼綠牌子的表示送禮送得比較多的,
             * 貼黃牌子的學員表示送禮送得比較少的,不貼牌子的學員表示沒有送過禮的,通過這個牌子就可以標識出不同的學員
             * 教官在考覈時一看,哦,這個學員是有牌子的,是送過禮給他的,優先讓有牌子的學員過,此時這個牌子就是一個註解
             * 一個牌子就是一個註解的實例對象,實實在在存在的牌子就是一個實實在在的註解對象,把牌子拿下來(去掉註解)註解對象就不存在了
             */
            MyAnnotation annotation = (MyAnnotation) AnnotationUse.class
                    .getAnnotation(MyAnnotation.class);
            System.out.println(annotation);// 打印MyAnnotation對象,這裏輸出的結果爲:@cn.itcast.day2.MyAnnotation()
        }
    }
}

三、@Retention元註解

  根據反射的測試的問題,引出@Retention元註解的講解:其三種取值:RetentionPolicy.SOURCERetentionPolicy.CLASSRetentionPolicy.RUNTIME分別對應:Java源文件(.java文件)---->.class文件---->內存中的字節碼

 四、 Retention註解說明

    當在Java源程序上加了一個註解,這個Java源程序要由javac去編譯,javac把java源文件編譯成.class文件,在編譯成class時可能會把Java源程序上的一些註解給去掉,java編譯器(javac)在處理java源程序時,可能會認爲這個註解沒有用了,於是就把這個註解去掉了,那麼此時在編譯好的class中就找不到註解了, 這是編譯器編譯java源程序時對註解進行處理的第一種可能情況,假設java編譯器在把java源程序編譯成class時,沒有把java源程序中的註解去掉,那麼此時在編譯好的class中就可以找到註解,當程序使用編譯好的class文件時,需要用類加載器把class文件加載到內存中,class文件中的東西不是字節碼,class文件裏面的東西由類加載器加載到內存中去,類加載器在加載class文件時,會對class文件裏面的東西進行處理,如安全檢查,處理完以後得到的最終在內存中的二進制的東西纔是字節碼,類加載器在把class文件加載到內存中時也有轉換,轉換時是否把class文件中的註解保留下來,這也有說法,所以說一個註解的生命週期有三個階段:java源文件是一個階段,class文件是一個階段,內存中的字節碼是一個階段,javac把java源文件編譯成.class文件時,有可能去掉裏面的註解,類加載器把.class文件加載到內存時也有可能去掉裏面的註解,因此在自定義註解時就可以使用Retention註解指明自定義註解的生命週期,自定義註解的生命週期是在RetentionPolicy.SOURCE階段(java源文件階段),還是在RetentionPolicy.CLASS階段(class文件階段),或者是在RetentionPolicy.RUNTIME階段(內存中的字節碼運行時階段),根據JDK提供的API可以知道默認是在RetentionPolicy.CLASS階段 (JDK的API寫到:the retention policy defaults to RetentionPolicy.CLASS.)

  下面看看@Deprecated、@Override、@SuppressWarnings這三個註解的@Retention註解的屬性值分別是什麼吧

4.1、@Deprecated

  Java API中是這樣定義的@Deprecated的

@Documented
@Retention(value=RUNTIME)
public @interface Deprecated

4.2、@Override

  Java API中是這樣定義的@Override的

@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override

@Override是給javac(java編譯器)看的,編譯完以後就@Override註解就沒有價值了,@Override註解在源代碼中有用,編譯成.class文件後@Override註解就沒有用了,因此@Override的Retention的屬性值是RetentionPolicy.SOURCE

4.3、@SuppressWarnings

  Java API中是這樣定義的@SuppressWarnings的

@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings

@SuppressWarnings是給javac(java編譯器)看的,編譯器編譯完java文件後,@SuppressWarnings註解就沒有用了,所以@SuppressWarnings的Retention的屬性值是RetentionPolicy.SOURCE

五、@Target元註解

  @Target元註解決定了一個註解可以標識到哪些成分上,如標識在在類身上,或者屬性身上,或者方法身上等成分,@Target默認值爲任何元素(成分)

例如:

@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarning

六、爲註解增加屬性

  註解可以看成是一種特殊的類,既然是類,那自然可以爲類添加屬性

6.1.添加屬性

 語法:類型 屬性名();

package cn.gacl.annotation;

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

@Retention(RetentionPolicy.RUNTIME)
//Retention註解決定MyAnnotation註解的生命週期
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyAnnotation {
    /**
     * 定義基本屬性
     * @return
     */
    String color();
}

 其實從代碼的寫法上來看,註解更像是一種特殊的接口,註解的屬性定義方式就和接口中定義方法的方式一樣,而應用了註解的類可以認爲是實現了這個特殊的接口

6.2.應用屬性

package cn.gacl.annotation;

@MyAnnotation(color="red")//應用MyAnnotation註解的color屬性
public class MyAnnotationTest {
    public static void main(String[] args) {
        /**
         * 用反射方式獲得註解對應的實例對象後,在通過該對象調用屬性對應的方法
         */
        MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
        System.out.println(annotation.color());//輸出red
    }
}

6.3.爲屬性指定缺省值(默認值)

  語法:類型 屬性名() default 默認值;

package cn.gacl.annotation;

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

@Retention(RetentionPolicy.RUNTIME)
//Retention註解決定MyAnnotation註解的生命週期
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyAnnotation {
    String color() default "blue";//爲屬性指定缺省值
}
package cn.gacl.annotation;

@MyAnnotation
public class MyAnnotationTest {
    public static void main(String[] args) {
        /**
         * 用反射方式獲得註解對應的實例對象後,在通過該對象調用屬性對應的方法
         */
        MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
        System.out.println(annotation.color());//輸出color屬性的默認值:blue
        
    }
}

6.4.value屬性

  如果一個註解中有一個名稱爲value的屬性,且你只想設置value屬性(即其他屬性都採用默認值或者你只有一個value屬性),那麼可以省略掉“value=”部分。

  例如:@SuppressWarnings("deprecation")

package cn.gacl.annotation;

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

@Retention(RetentionPolicy.RUNTIME)
//Retention註解決定MyAnnotation註解的生命週期
@Target( { ElementType.METHOD, ElementType.TYPE })
public @interface MyAnnotation {
    String color() default "blue";//爲屬性指定缺省值
    String value();//定義一個名稱爲value的屬性
}

package cn.gacl.annotation;

@MyAnnotation("孤傲蒼狼")//等價於@MyAnnotation(value="孤傲蒼狼")
public class MyAnnotationTest {
    public static void main(String[] args) {
        /**
         * 用反射方式獲得註解對應的實例對象後,在通過該對象調用屬性對應的方法
         */
        MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
        System.out.println(annotation.color());//輸出color屬性的默認值:blue
        System.out.println(annotation.value());
        
    }
}

七、爲註解增加高級屬性

7.1、數組類型的屬性

  • 增加數組類型的屬性:int[] arrayAttr() default {1,2,4};
  • 應用數組類型的屬性:@MyAnnotation(arrayAttr={2,4,5})
  • 如果數組屬性只有一個值,這時候屬性值部分可以省略大括號,如:@MyAnnotation(arrayAttr=2),這就表示數組屬性只有一個值,值爲2

7.2.、枚舉類型的屬性

  • 增加枚舉類型的屬性:EumTrafficLamp lamp() default EumTrafficLamp.RED;
  • 應用枚舉類型的屬性:@MyAnnotation(lamp=EumTrafficLamp.GREEN)

7.3、註解類型的屬性

/**
 * MetaAnnotation註解類爲元註解
 * @author 孤傲蒼狼
 *
 */
public @interface MetaAnnotation {
    String value();//元註解MetaAnnotation設置有一個唯一的屬性value
}

爲註解添加一個註解類型的屬性,並指定註解屬性的缺省值:MetaAnnotation annotationAttr() default @MetaAnnotation("xdp");

八、註解綜合測試

EumTrafficLamp.java

package cn.gacl.annotation;
/**
 * 交通信號燈顏色枚舉
 * @author 孤傲蒼狼
 *
 */
public enum EumTrafficLamp {
    RED,//紅
    YELLOW,//黃
    GREEN//綠
}

MetaAnnotation.java

/**
 * MetaAnnotation註解類爲元註解
 * @author 孤傲蒼狼
 *
 */
public @interface MetaAnnotation {
    String value();//元註解MetaAnnotation設置有一個唯一的屬性value
}
MyAnnotation.java

package cn.gacl.annotation;

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

@Retention(RetentionPolicy.RUNTIME)
//Retention註解決定MyAnnotation註解的生命週期
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation {
    String color() default "blue";//爲屬性指定缺省值
    /**
     * 爲註解添加value屬性,這個value屬性很特殊,如果一個註解中只有一個value屬性要設置,
     * 那麼在設置註解的屬性值時,可以省略屬性名和等號不寫, 直接寫屬性值,如@SuppressWarnings("deprecation"),
     * 這裏的MyAnnotation註解設置了兩個String類型的屬性,color和value,
     * 因爲color屬性指定有缺省值,value屬性又是屬於特殊的屬性,因此使用MyAnnotation註解時
     * 可以這樣使用MyAnnotation註解:"@MyAnnotation(color="red",value="xdp")"
     * 也可以這樣使用:"@MyAnnotation("孤傲蒼狼")",這樣寫就表示MyAnnotation註解只有一個value屬性要設置,color屬性採用缺省值
     * 當一個註解只有一個value屬性要設置時,是可以省略"value="的
     */
    String value();//定義一個名稱爲value的屬性
    //添加一個int類型數組的屬性
    int[] arrayAttr() default {1,2,4};
    //添加一個枚舉類型的屬性,並指定枚舉屬性的缺省值,缺省值只能從枚舉類EumTrafficLamp中定義的枚舉對象中取出任意一個作爲缺省值
    EumTrafficLamp lamp() default EumTrafficLamp.RED;
    //爲註解添加一個註解類型的屬性,並指定註解屬性的缺省值
    MetaAnnotation annotationAttr() default @MetaAnnotation("xdp");

}

MyAnnotationTest.java

package cn.gacl.annotation;
/**
 * 這裏是將新創建好的註解類MyAnnotation標記到AnnotaionTest類上,
 * 並應用了註解類MyAnnotation中定義各種不同類型的的屬性
 */
@MyAnnotation(
        color="red",
        value="孤傲蒼狼",
        arrayAttr={3,5,6},
        lamp=EumTrafficLamp.GREEN,
        annotationAttr=@MetaAnnotation("gacl")
        )
public class MyAnnotationTest {
    @MyAnnotation("將MyAnnotation註解標註到main方法上")
    public static void main(String[] args) {
        /**
         * 這裏是檢查Annotation類是否有註解,這裏需要使用反射才能完成對Annotation類的檢查
         */
        if(MyAnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) {
            /**
             * 用反射方式獲得註解對應的實例對象後,在通過該對象調用屬性對應的方法
             * MyAnnotation是一個類,這個類的實例對象annotation是通過反射得到的,這個實例對象是如何創建的呢?
             * 一旦在某個類上使用了@MyAnnotation,那麼這個MyAnnotation類的實例對象annotation就會被創建出來了
             */
            MyAnnotation annotation = (MyAnnotation) MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
            System.out.println(annotation.color());//輸出color屬性的默認值:red
            System.out.println(annotation.value());//輸出value屬性的默認值:孤傲蒼狼
            System.out.println(annotation.arrayAttr().length);//這裏輸出的數組屬性的長度的結果爲:3,數組屬性有三個元素,因此數組的長度爲3
            System.out.println(annotation.lamp());//這裏輸出的枚舉屬性值爲:GREEN
            System.out.println(annotation.annotationAttr().value());//這裏輸出的註解屬性值:gacl
            
            MetaAnnotation ma = annotation.annotationAttr();//annotation是MyAnnotation類的一個實例對象
            System.out.println(ma.value());//輸出的結果爲:gacl

            
        }
    }
}







發佈了0 篇原創文章 · 獲贊 126 · 訪問量 92萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章