1.1 注解聲明
Java注解Annotation,有聲明注解和元注解
- 元注解:Java提供的元注解,所謂元注解就是標(biāo)記其他注解的注解(@Target,@Retention),即@Target(ElementType.ANNOTATION_TYPE)
- 聲明注解:用@interface聲明的注解
@Target
@Target 用來約束注解可以應(yīng)用的地方(如類,方法,字段)
/*
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 9.7.4 Where Annotations May Appear
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
public enum ElementType {
/** 標(biāo)明該注解可以用于類、接口(包括注解類型)或enum聲明 */
TYPE,
/** 標(biāo)明該注解可以用于字段(域)聲明,包括enum實(shí)例 */
FIELD,
/** 標(biāo)明該注解可以用于方法聲明 */
METHOD,
/** 標(biāo)明該注解可以用于參數(shù)聲明 */
PARAMETER,
/** 標(biāo)明注解可以用于構(gòu)造函數(shù)聲明 */
CONSTRUCTOR,
/** 標(biāo)明注解可以用于局部變量聲明 */
LOCAL_VARIABLE,
/** 標(biāo)明注解可以用于注解聲明(應(yīng)用于另一個(gè)注解上)*/
ANNOTATION_TYPE,
/** 標(biāo)明注解可以用于包聲明 */
PACKAGE,
/**
* 標(biāo)明注解可以用于類型參數(shù)聲明(1.8新加入)
* @since 1.8
*/
TYPE_PARAMETER,
/**
* 類型使用聲明(1.8新加入)
* @since 1.8
*/
TYPE_USE
}
當(dāng)聲明的注解未指定Target值時(shí),則此注解可以用于任何元素之上,多個(gè)值使用{}包含并用逗號(hào)隔開,如下:
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
@Retention
用來約束注解的保留的地方,有三個(gè)值,源碼級(jí)別(source),類文件級(jí)別(class)或者運(yùn)行時(shí)級(jí)別(runtime)
/*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.3.2 @Retention
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
1.2 注解元素
如何聲明一個(gè)注解元素:
@Target(ElementType.TYPE)//只能應(yīng)用于類上
@Retention(RetentionPolicy.RUNTIME)//保存到運(yùn)行時(shí)
public @interface DBTable {
String name() default "";
}
上面這個(gè)例子說明,DBTable這個(gè)注解聲明了一個(gè)String類型的name的元素。
default關(guān)鍵字 表示默認(rèn)值
name() 方法不能傳參,否則編譯出錯(cuò):
@interface members may not have parameter
在使用注解元素的時(shí)候,使用鍵值對(duì)的方式,以方法名作為key,value表示元素的返回值
//在類上使用該注解
@DBTable(name = "MEMBER")
public class Member {
//.......
}
注解支持的元素?cái)?shù)據(jù)類型除了String,還支持如下數(shù)據(jù)類型:
- 所有基本類型(使用基本類型但不允許使用任何包裝類型)
- String
- Class
- enum
- Annotation
- 上面類型的數(shù)組
示例:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Reference{
boolean next() default false;
}
public @interface AnnotationElementDemo {
//枚舉類型
enum Status {FIXED,NORMAL};
//聲明枚舉
Status status() default Status.FIXED;
//布爾類型
boolean showSupport() default false;
//String類型
String name()default "";
//class類型
Class<?> testCase() default Void.class;
//注解嵌套
Reference reference() default @Reference(next=true);
//數(shù)組類型
long[] value();
}
編譯器對(duì)元素的默認(rèn)值有些過分挑剔。
- 元素必須要有值:要么定義元素的時(shí)候設(shè)置默認(rèn)值,要么在使用注解的時(shí)候提供元素的值
- 元素的值不能為null。
1.3 注解不支持繼承
注解是不支持繼承的,因此不能使用關(guān)鍵字extends來繼承某個(gè)@interface,但注解在編譯后,編譯器會(huì)自動(dòng)繼承java.lang.annotation.Annotation接口,這里我們反編譯前面定義的DBTable注解:
import java.lang.annotation.Annotation;
//反編譯后的代碼
public interface DBTable extends Annotation
{
public abstract String name();
}
雖然反編譯后發(fā)現(xiàn)DBTable注解繼承了Annotation接口,請(qǐng)記住,即使Java的接口可以實(shí)現(xiàn)多繼承,但定義注解時(shí)依然無法使用extends關(guān)鍵字繼承@interface。
1.4 注解快捷方式
快捷方式就是注解中定義了名為value的元素,并且在使用該注解時(shí),如果該元素是唯一需要賦值的一個(gè)元素,那么此時(shí)無需使用key=value的語(yǔ)法,而只需在括號(hào)內(nèi)給出value元素所需的值即可。這可以應(yīng)用于任何合法類型的元素,記住,這限制了元素名必須為value,簡(jiǎn)單案例如下:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntegerVaule{
int value() default 0;
String name() default "";
}
//使用注解
public class QuicklyWay {
//當(dāng)只想給value賦值時(shí),可以使用以下快捷方式
@IntegerVaule(20)
public int age;
//當(dāng)name也需要賦值時(shí)必須采用key=value的方式賦值
@IntegerVaule(value = 10000,name = "MONEY")
public int money;
}
2.1 Java內(nèi)置注解與其它元注解
Java常用內(nèi)置注解三個(gè):
- @Override:用于標(biāo)明此方法覆蓋了父類的方法
- @Deprecated:用于標(biāo)明已經(jīng)過時(shí)的方法或類
- @SuppressWarnnings:用于有選擇的關(guān)閉編譯器對(duì)類、方法、成員變量、變量初始化的警告.其內(nèi)部有一個(gè)String數(shù)組,主要接收值如下:
- deprecation:使用了不贊成使用的類或方法時(shí)的警告;
- unchecked:執(zhí)行了未檢查的轉(zhuǎn)換時(shí)的警告,例如當(dāng)使用集合時(shí)沒有用泛型 (Generics) 來指定集合保存的類型;
- fallthrough:當(dāng) Switch 程序塊直接通往下一種情況而沒有 Break 時(shí)的警告;
- path:在類路徑、源文件路徑等中有不存在的路徑時(shí)的警告;
- serial:當(dāng)在可序列化的類上缺少 serialVersionUID 定義時(shí)的警告;
- finally:任何 finally 子句不能正常完成時(shí)的警告;
- all:關(guān)于以上所有情況的警告。
Java常用內(nèi)置元注解:
- @Target
- @Retention
- @Documented 被修飾的注解會(huì)生成到j(luò)avadoc中
- @Inherited 可以讓注解被繼承,但這并不是真的繼承,只是通過使用@Inherited,可以讓子類Class對(duì)象使用getAnnotations()獲取父類被@Inherited修飾的注解
3. 注解與反射機(jī)制
經(jīng)過反編譯后, 知道Java所有注解都繼承了Annotation接口。也就是說 Java使用Annotation接口代表注解元素。
為了運(yùn)行時(shí)能準(zhǔn)確獲取到注解的相關(guān)信息,Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 VM 中運(yùn)行的程序中已使用注解的元素,通過該接口提供的方法可以利用反射技術(shù)地讀取注解的信息,如反射包的Constructor類、Field類、Method類、Package類和Class類都實(shí)現(xiàn)了AnnotatedElement接口
示例:
注解類:DocumentA
import java.lang.annotation.*;
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DocumentA {
}
被注解修飾類A:
@DocumentA
public class A {
}
測(cè)試類DocumentDemo:
public class DocumentDemo extends A{
public static void main(String[] args) {
Class<?> clazz = DocumentDemo.class;
//根據(jù)指定注解類型獲取該注解
DocumentA documentA = clazz.getAnnotation(DocumentA.class);
System.out.println("A:"+documentA);
//獲取該元素上的所有注解,包含從父類繼承
Annotation[] annotations = clazz.getAnnotations();
System.out.println("an:"+ Arrays.toString(annotations));
//獲取該元素上的所有注解,但不包含繼承!
Annotation[] an2 = clazz.getDeclaredAnnotations();
System.out.println("an2:"+ Arrays.toString(an2));
//判斷注解DocumentA是否在該元素上
boolean b=clazz.isAnnotationPresent(DocumentA.class);
System.out.println("b:"+b);
}
}
-----------------
輸入結(jié)果:
A:@com.littlezan.test.testannotation.DocumentA()
an:[@com.littlezan.test.testannotation.DocumentA()]
an2:[]
b:true
4. 運(yùn)行時(shí)注解處理器
通過注解Api和反射包中與注解相關(guān)的Api,解析注解內(nèi)容來組裝需要的數(shù)據(jù)
5. Java 8中注解增強(qiáng)
5.1 元注解@Repeatable
元注解@Repeatable是JDK1.8新加入的,它表示在同一個(gè)位置重復(fù)相同的注解。在沒有該注解前,一般是無法在同一個(gè)類型上使用相同的注解的
//Java8前無法這樣使用
@FilterPath("/web/update")
@FilterPath("/web/add")
public class A {}
Java8前如果是想實(shí)現(xiàn)類似的功能,我們需要在定義@FilterPath注解時(shí)定義一個(gè)數(shù)組元素接收多個(gè)值如下
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface FilterPath {
String [] value();
}
//使用
@FilterPath({"/update","/add"})
public class A { }
但在Java8新增了@Repeatable注解后就可以采用如下的方式定義并使用了
//使用Java8新增@Repeatable原注解
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FilterPaths.class)//參數(shù)指明接收的注解class
public @interface FilterPath {
String value();
}