1. 元注解
元注解:用在注解上的注解,java1.5后添加的4個元注解:
- @Target
- @Retention
- @Documented
- @Inherited
在java1.8又添加了兩個注解:
- @Native
- @Repeatable
先說明下這幾個注解.
@Target 修飾的對象范圍
取值(ElementType)有:
- 1.CONSTRUCTOR:用于描述構(gòu)造器
- 2.FIELD:用于描述域
- 3.LOCAL_VARIABLE:用于描述局部變量
- 4.METHOD:用于描述方法
- 5.PACKAGE:用于描述包
- 6.PARAMETER:用于描述參數(shù)
- 7.TYPE:用于描述類、接口(包括注解類型) 或enum聲明
當(dāng)注解類型聲明中沒有@Target元注解锻全,則默認為可適用所有的程序元素咳秉。
@Retention 注解保留到哪個時期(生命周期)
取值(RetentionPoicy)有:
- 1.SOURCE:在源文件中有效(即源文件保留)
- 2.CLASS:在class文件中有效(即class保留)
- 3.RUNTIME:在運行時有效(即運行時保留)
默認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)容都被重新定義了.
在類上的注解會被傳遞該子類,方法也可以但重寫了則不會在傳遞給子類
再次捋清楚下:
- 如果父類的注解是定義在類上面噪服,那么子類是可以繼承過來的
- 如果父類的注解定義在方法上面禽拔,那么子類仍然可以繼承過來
- 如果子類重寫了父類中定義了注解的方法索守,那么子類將無法繼承該方法的注解
- 即子類在重寫父類中被@Inherited標(biāo)注的方法時晕窑,會將該方法連帶它上面的注解一并覆蓋掉
2.常見的Java注解
我們平時直接使用java提供幾個注解:@Override @Deprecated @SuppressWarnings @FunctionalInterface,下面逐個看下其定義
-
@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ù)類型杨赤,編譯器會發(fā)出警告,當(dāng)使用該注解后截汪,則不會發(fā)出警告疾牲。@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
可以接受多個參數(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ù)式接口
用戶告知編譯器,檢查這個接口衙解,保證該接口是函數(shù)式接口阳柔,即只能包含一個抽象方法,否則就會編譯出錯蚓峦。@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}
3. 自定義注解
Annotaion不影響程序代碼的執(zhí)行盔沫,無論增加、刪除Annotation枫匾,代碼都始終如一地執(zhí)行架诞。
也就是說:如果我們不去解析注解,它就沒什么作用和影響.
在 java.lang.Class 中末尾有4個涉及的解析注解的方法:
-
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
返回改程序元素上存在的、指定類型的注解干茉,如果該類型注解不存在谴忧,則返回null。
-
Annotation[] getAnnotations()
返回該程序元素上存在的所有注解。
-
boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)
判斷該程序元素上是否包含指定類型的注解沾谓,存在則返回true委造,否則返回false.
-
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注釋。與此接口中的其他方法不同均驶,該方法將忽略繼承的注釋昏兆。(如果沒有注釋直接存在于此元素上,則返回長度為零的一個數(shù)組妇穴。)該方法的調(diào)用者可以隨意修改返回的數(shù)組爬虱;這不會對其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響。
除此之外還有寫注意事項:
- 成員類型受限:原始類型腾它,String,Class,Annotation,Enumeration
- 注解類只有一個成員時跑筝,必須取名為value(),在使用時可以忽略成員名和賦值號(=)
- 注解類可以沒有成員瞒滴,稱之為標(biāo)識注解
4. 實踐自定義注解
首先看下接下來要實現(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("藍色"), 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));
}
}
}
}
測試
public class FruitTest {
public static void main(String[] args) {
FruitAnnotationParser.parse();
}
}
輸出:
蘋果
綠色
providerName:蘋果公司 id:1001 address:美國硅谷
注解理解之后使用起來還是比較簡單的,解析注解的套路就那么4個,解析的過程都差不多.