1. 元注解
元注解:用在注解上的注解,java1.5后添加的4個(gè)元注解:
- @Target
- @Retention
- @Documented
- @Inherited
在java1.8又添加了兩個(gè)注解:
- @Native
- @Repeatable
先說明下這幾個(gè)注解.
@Target 修飾的對(duì)象范圍
取值(ElementType)有:
- 1.CONSTRUCTOR:用于描述構(gòu)造器
- 2.FIELD:用于描述域
- 3.LOCAL_VARIABLE:用于描述局部變量
- 4.METHOD:用于描述方法
- 5.PACKAGE:用于描述包
- 6.PARAMETER:用于描述參數(shù)
- 7.TYPE:用于描述類、接口(包括注解類型) 或enum聲明
當(dāng)注解類型聲明中沒有@Target元注解,則默認(rèn)為可適用所有的程序元素。
@Retention 注解保留到哪個(gè)時(shí)期(生命周期)
取值(RetentionPoicy)有:
- 1.SOURCE:在源文件中有效(即源文件保留)
- 2.CLASS:在class文件中有效(即class保留)
- 3.RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)
默認(rèn)CLASS,可以看下源碼中提解釋:
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
}
@Documented 標(biāo)志可文檔化
被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化
@Inherited 將注解內(nèi)容傳遞到子類
若是方法被重寫則得不到父類方法中的注解內(nèi)容,關(guān)于該方法所有的內(nèi)容都被重新定義了.
在類上的注解會(huì)被傳遞該子類,方法也可以但重寫了則不會(huì)在傳遞給子類
再次捋清楚下:
- 如果父類的注解是定義在類上面,那么子類是可以繼承過來的
- 如果父類的注解定義在方法上面,那么子類仍然可以繼承過來
- 如果子類重寫了父類中定義了注解的方法,那么子類將無法繼承該方法的注解
- 即子類在重寫父類中被@Inherited標(biāo)注的方法時(shí),會(huì)將該方法連帶它上面的注解一并覆蓋掉
2.常見的Java注解
我們平時(shí)直接使用java提供幾個(gè)注解:@Override @Deprecated @SuppressWarnings @FunctionalInterface,下面逐個(gè)看下其定義
-
@Override :告知服務(wù)器,我們要覆蓋父類中的當(dāng)前方法.
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
-
@Deprecated :告知編譯器,某一程序元素不建議使用了
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
-
@SuppressWarnings 忽略特定的警告
用于告知編譯器忽略特定的警告信息,例在泛型中使用原生數(shù)據(jù)類型,編譯器會(huì)發(fā)出警告,當(dāng)使用該注解后,則不會(huì)發(fā)出警告。@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
可以接受多個(gè)參數(shù)例如:
@SupressWarning(value={"uncheck","deprecation"})接受的類型:
- all: to suppress all warnings
- boxing: to suppress warnings relative to boxing/unboxing operations
- cast: to suppress warnings relative to cast operations
- dep-ann: to suppress warnings relative to deprecated annotation
- deprecation: to suppress warnings relative to deprecation
- fallthrough: to suppress warnings relative to missing breaks in switch statements
- finally: to suppress warnings relative to finally block that don’t return
- hiding: to suppress warnings relative to locals that hide variable
- incomplete-switch: to suppress warnings relative to missing entries in a switch statement (enum case)
- nls: to suppress warnings relative to non-nls string literals
- null: to suppress warnings relative to null analysis
- rawtypes: to suppress warnings relative to un-specific types when using generics on class params
- restriction: to suppress warnings relative to usage of discouraged or forbidden references
- serial: to suppress warnings relative to missing serialVersionUID field for a serializable class
- static-access: to suppress warnings relative to incorrect static access
- synthetic-access: to suppress warnings relative to unoptimized access from inner classes
- unchecked: to suppress warnings relative to unchecked operations
- unqualified-field-access: to suppress warnings relative to field access unqualified
- unused: to suppress warnings relative to unused code
-
@FunctionalInterface 保證該接口是函數(shù)式接口
用戶告知編譯器,檢查這個(gè)接口,保證該接口是函數(shù)式接口,即只能包含一個(gè)抽象方法,否則就會(huì)編譯出錯(cuò)。@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
3. 自定義注解
Annotaion不影響程序代碼的執(zhí)行,無論增加、刪除Annotation,代碼都始終如一地執(zhí)行。
也就是說:如果我們不去解析注解,它就沒什么作用和影響.
在 java.lang.Class 中末尾有4個(gè)涉及的解析注解的方法:
-
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
返回改程序元素上存在的、指定類型的注解,如果該類型注解不存在,則返回null。
-
Annotation[] getAnnotations()
返回該程序元素上存在的所有注解。
-
boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)
判斷該程序元素上是否包含指定類型的注解,存在則返回true,否則返回false.
-
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上,則返回長度為零的一個(gè)數(shù)組。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響。
除此之外還有寫注意事項(xiàng):
- 成員類型受限:原始類型,String,Class,Annotation,Enumeration
- 注解類只有一個(gè)成員時(shí),必須取名為value(),在使用時(shí)可以忽略成員名和賦值號(hào)(=)
- 注解類可以沒有成員,稱之為標(biāo)識(shí)注解
4. 實(shí)踐自定義注解
首先看下接下來要實(shí)現(xiàn)的相關(guān)類:
FruitName 水果名稱
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FruitName {
String value() default "";
}
FruitColor 水果顏色
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FruitColor {
public enum Color {
BLUE("藍(lán)色"), RED("紅色"), GREEN("綠色");
private String name;
public String getName() {
return name;
}
Color(String name) {
this.name = name;
}
}
Color value() default Color.GREEN;
}
FruitProvider 供應(yīng)商信息
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FruitProvider {
int id() default 1;
String address() default "";
String providerName() default "";
}
接下來到使用類了
Apple 使用注解
public class Apple {
@FruitName("蘋果")
public String name;
@FruitColor(FruitColor.Color.GREEN)
public String color;
@FruitProvider(id=1001,address = "美國硅谷",providerName = "蘋果公司")
public String provider;
}
FruitAnnotationParser 注解解析類
public class FruitAnnotationParser {
public static void parse() {
Field[] fields = Apple.class.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(FruitName.class)) {
FruitName fruitName = field.getAnnotation(FruitName.class);
String value = fruitName.value();
System.out.println(value);
} else if (field.isAnnotationPresent(FruitColor.class)) {
FruitColor fruitColor = field.getAnnotation(FruitColor.class);
FruitColor.Color color = fruitColor.value();
System.out.println(color.getName());
} else if (field.isAnnotationPresent(FruitProvider.class)) {
FruitProvider provider = field.getAnnotation(FruitProvider.class);
String address = provider.address();
int id = provider.id();
String providerName = provider.providerName();
System.out.println(String.format("providerName:%s id:%d address:%s", providerName, id, address));
}
}
}
}
測(cè)試
public class FruitTest {
public static void main(String[] args) {
FruitAnnotationParser.parse();
}
}
輸出:
蘋果
綠色
providerName:蘋果公司 id:1001 address:美國硅谷
注解理解之后使用起來還是比較簡單的,解析注解的套路就那么4個(gè),解析的過程都差不多.