一、元註解
元註解的作用就是負責註解其他註解。
1.@Target
@Target用來指明註解所修飾的目標,包括packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。
取值(ElementType)有:
CONSTRUCTOR:用於描述構造器
FIELD:用於描述域
LOCAL_VARIABLE:用於描述局部變量
METHOD:用於描述方法
PACKAGE:用於描述包
PARAMETER:用於描述參數
TYPE:用於描述類、接口(包括註解類型) 或enum聲明
例如:
Target(ElementType.TYPE)
public @interface AnnotationTest1 {
public String tableName() default "className";
}
- 1
- 2
- 3
- 4
表示AnnotationTest1這個註解用來註解類、接口(包括註解類型) 或enum聲明。
@Target(ElementType.FIELD)
public @interface AnnotationTest2 {
}
- 1
- 2
- 3
- 4
表示註解AnnotationTest2用於註解類的成員變量。
2.@Retention
@Retention定義了該Annotation的生命週期,某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取。
取值(RetentionPoicy)有:
SOURCE:在源文件中有效(即源文件保留)
CLASS:在class文件中有效(即class保留)
RUNTIME:在運行時有效(即運行時保留)
例如:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
表示Column這個註解用來註解類的成員變量,並且註解一直保留到運行時。
3.@Documented
@Documented的作用是在生成javadoc文檔的時候將該Annotation也寫入到文檔中。
例如:
@Target(ElementType.METHOD)
@Documented
public @interface DocumentTest {
String hello();
}
- 1
- 2
- 3
- 4
- 5
該註解用來修飾成員方法,下面簡單的使用;
public class DocumentClass {
/**
* this is method of doSomething
*/
@DocumentTest(hello = "yahaitt")
public void doSomething() {
System.out.println("do something");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
生成的doc文件中如下:
@DocumentTest(hello="yahaitt")
public void doSomething()
this is method of doSomething
- 1
- 2
- 3
4.@Inherited
我們自定義註解(Annotation)時,把自定義的註解標註在父類上,但是它不會被子類所繼承,我們可以在定義註解時給我們自定義的註解標註一個@Inherited註解來實現註解繼承。
例如:
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedTest {
String value();
}
- 1
- 2
- 3
- 4
- 5
@InheritedTest("Jadyer")
public class Parent {
@InheritedTest("JavaEE")
public void doSomething() {
System.out.println("Parent do something");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
public class Child extends Parent {
}
- 1
- 2
- 3
public class Test {
public static void main(String[] args) throws SecurityException, NoSuchMethodException {
classTest();
methodTest();
}
/**
* 通過反射方式測試子類是否會繼承父類中定義在類上面的註解
*/
public static void classTest(){
Class<Child> c = Child.class;
if (c.isAnnotationPresent(InheritedTest.class)) {
InheritedTest inheritedTest = c.getAnnotation(InheritedTest.class);
String value = inheritedTest.value();
System.out.println(value);
}
}
/**
* 通過反射方式測試子類是否會繼承父類中定義在方法上面的註解
*/
public static void methodTest() throws SecurityException, NoSuchMethodException{
Class<Child> c = Child.class;
Method method = c.getMethod("doSomething", new Class[]{});
if (method.isAnnotationPresent(InheritedTest.class)) {
InheritedTest inheritedTest = method.getAnnotation(InheritedTest.class);
String value = inheritedTest.value();
System.out.println(value);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
如果父類的註解是定義在類上面,那麼子類是可以繼承過來的
如果父類的註解定義在方法上面,那麼子類仍然可以繼承過來
如果子類重寫了父類中定義了註解的方法,那麼子類將無法繼承該方法的註解,即子類在重寫父類中被@Inherited標註的方法時,會將該方法連帶它上面的註解一併覆蓋掉
二、Java內建註解
Java提供了三種內建註解
@Override——當我們想要複寫父類中的方法時,我們需要使用該註解去告知編譯器我們想要複寫這個方法。這樣一來當父類中的方法移除或者發生更改時編譯器將提示錯誤信息。
@Deprecated——當我們希望編譯器知道某一方法不建議使用時,我們應該使用這個註解。Java在javadoc 中推薦使用該註解,我們應該提供爲什麼該方法不推薦使用以及替代的方法。
@SuppressWarnings——這個僅僅是告訴編譯器忽略特定的警告信息,例如在泛型中使用原生數據類型。它的保留策略是SOURCE並且被編譯器丟棄。
三、自定義註解
使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。
定義註解格式:
public @interface 註解名 {定義體}
註解參數的可支持數據類型:
1.所有基本數據類型(int,float,boolean,byte,double,char,long,short)
2.String類型
3.Class類型
4.enum類型
5.Annotation類型
6.以上所有類型的數組
Annotation類型裏面的參數該怎麼設定:
第一,只能用public或默認(default)這兩個訪問權修飾.例如,String value();這裏把方法設爲defaul默認類型;
第二,參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這裏的參數成員就爲String;
第三,如果只有一個參數成員,最好把參數名稱設爲”value”,後加小括號
第四,可以在使用default爲每個參數設置一個默認值。註解元素必須有確定的值,要麼在定義註解的默認值中指定,要麼在使用註解時指定,非基本類型的註解元素的值不可爲null。因此, 使用空字符串或0作爲默認值是一種常用的做法。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
- 1
- 2
- 3
- 4
- 5
- 6
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
public enum Color{ BULE,RED,GREEN};
Color fruitColor() default Color.GREEN;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
public int id() default -1;
public String name() default "";
public String address() default "";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
四、註解的使用
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
參數名只有一個,並且設置爲value,在對註解使用賦值的時候可以不用加參數名,直接賦值即可。其他情況需要指定參數名和參數值。
五、註解處理
Java在java.lang.reflect 包下新增了AnnotatedElement接口,該接口主要有如下幾個實現類:
Class:類定義
Constructor:構造器定義
Field:累的成員變量定義
Method:類的方法定義
Package:類的包定義
所以,AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,程序通過反射獲取了某個類的AnnotatedElement對象之後,程序就可以調用該對象的如下四個個方法來訪問Annotation信息:
//返回改程序元素上存在的、指定類型的註解,如果該類型註解不存在,則返回null。
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
//返回該程序元素上存在的所有註解。
Annotation[] getAnnotations()
//判斷該程序元素上是否包含指定類型的註解,存在則返回true,否則返回false.
boolean is AnnotationPresent(Class<?extends Annotation> annotationClass)
//返回直接存在於此元素上的所有註釋。與此接口中的其他方法不同,該方法將忽略繼承的註釋。(如果沒有註釋直接存在於此元素上,則返回長度爲零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。
Annotation[] getDeclaredAnnotations()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
在上面的@Inherited的講解中也舉個相應的例子,我們只需要反射得到Class、Method和Constructor,因爲AnnotatedElement是Class、Method和Constructor的父接口,它們都實現了AnnotatedElement,所以,在Class、Method和Constructor中就可以使用AnnotatedElement中聲明的方法來得到註解和註解內容。