一拜英、什么是注解
???????注解也叫元數(shù)據(jù)静汤,例如我們常見的@Override和@Deprecated,注解是JDK1.5版本開始引入的一個特性,用于對代碼進行說明虫给,可以對包藤抡、類、接口抹估、字段杰捂、方法參數(shù)、局部變量等進行注解
一般常用的注解可以分為三類:
- 一類是Java自帶的標準注解棋蚌,包括@Override(標明重寫某個方法)嫁佳、@Deprecated(標明某個類或方法過時)和@SuppressWarnings(標明要忽略的警告),使用這些注解后編譯器就會進行檢查
- 一類為元注解谷暮,元注解是用于定義注解的注解蒿往,包括@Retention(標明注解被保留的階段)、@Target(標明注解使用的范圍)湿弦、@Inherited(標明注解可繼承)瓤漏、@Documented(標明是否生成javadoc文檔)
- 一類為自定義注解,可以根據(jù)自己的需求定義注解
二颊埃、注解的用途
- 生成文檔蔬充,通過代碼里標識的元數(shù)據(jù)生成javadoc文檔。
- 編譯檢查班利,通過代碼里標識的元數(shù)據(jù)讓編譯器在編譯期間進行檢查驗證饥漫。
- 編譯時動態(tài)處理,編譯時通過代碼里標識的元數(shù)據(jù)動態(tài)處理罗标,例如動態(tài)生成代碼庸队。
- 運行時動態(tài)處理,運行時通過代碼里標識的元數(shù)據(jù)動態(tài)處理闯割,例如使用反射注入實例
三彻消、注解的使用
1、Java自帶的標準注解
常見標準的Annotation:
1.)Override
??????? java.lang.Override是一個標記類型注解宙拉,它被用作標注方法宾尚。它說明了被標注的方法重載了父類的方法,起到了斷言的作用谢澈。如果我們使用了這種注解在一個沒有覆蓋父類方法的方法時煌贴,java編譯器將以一個編譯錯誤來警示。
2.)Deprecated
???????Deprecated也是一種標記類型注解澳化。當一個類型或者類型成員使用@Deprecated修飾的話崔步,編譯器將不鼓勵使用這個被標注的程序元素。所以使用這種修飾具有一定的“延續(xù)性”:如果我們在代碼中通過繼承或者覆蓋的方式使用了這個過時的類型或者成員缎谷,雖然繼承或者覆蓋后的類型或者成員并不是被聲明為@Deprecated,但編譯器仍然要報警。
3.)SuppressWarnings
???????SuppressWarning不是一個標記類型注解胧后。它有一個類型為String[]的成員愚墓,這個成員的值為被禁止的警告名音念。對于javac編譯器來講,被-Xlint選項有效的警告名也同樣對@SuppressWarings有效者甲,同時編譯器忽略掉無法識別的警告名。
@SuppressWarnings("unchecked")
這種注解的使用非常簡單砌创,只需在需要注解的地方標明某個注解即可虏缸,例如在方法上注解:
public class Test {
@Override
public String tostring() {
return "override it";
}
}
例如在類上注解:
@Deprecated
public class Test {
}
2、元注解
java.lang.annotation提供了四種元注解嫩实,專門注解其他的注解(在自定義注解的時候刽辙,需要使用到元注解):
??????????????@Documented
??????????????@Retention
??????????????@Target
??????????????@Inherited
注解 | 作用 |
---|---|
@Target | 表示該注解用于什么地方。如果不明確指出甲献,該注解可以放在任何地方 ElementType.TYPE : 用于描述類宰缤、接口或enum聲明 ElementType.FIELD : 成員變量、對象晃洒、屬性(包括enum實例) ElementType.METHOD : 用于描述方法 Element.PARAMETER : 用于描述參數(shù) Element.CONSTRUCTOR : 用于描述構(gòu)造器 Element.LOCAL_VARIABLE : 用于描述局部變量 ElementType.ANNOTATION_TYPE : 用于描述類慨灭、接口(包括注解類型) 或enum聲明 ElementType.PACKAGE : 用于描述包 |
@Retention | 定義該注解的生命周期 RetentionPolicy.SOURCE : 在編譯階段丟棄。這些注解在編譯結(jié)束之后就不再有任何意義球及,所以它們不會寫入字節(jié)碼(@Override, @SuppressWarnings都屬于這類注解) RetentionPolicy.CLASS : 在類加載的時候丟棄氧骤。在字節(jié)碼文件的處理中有用。注解默認使用這種方式 RetentionPolicy.RUNTIME : 始終不會丟棄吃引,運行期也保留該注解语淘,因此可以使用反射機制讀取該注解的信息。我們自定義的注解通常使用這種方式 |
@Document | 表示是否將注解信息添加在java文檔中 |
@Inherited | 定義該注釋和子類的關(guān)系 |
注:如果一個使用了@Inherited修飾的annotation類型被用于一個class际歼,則這個annotation將被用于該class的子類惶翻。@Inherited annotation類型是被標注過的class的子類所繼承。類并不從它所實現(xiàn)的接口繼承annotation鹅心,方法并不從它所重載的方法繼承annotation
3吕粗、自定義注解
自定義注解類編寫的一些規(guī)則:
- Annotation型定義為@interface, 所有的Annotation會自動繼承java.lang.Annotation這一接口,并且不能再去繼承別的類或是接口.
- 參數(shù)成員只能用public或默認(default)這兩個訪問權(quán)修飾
- 參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型和String、Enum旭愧、Class颅筋、annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組.
- 要獲取類方法和字段的注解信息,必須通過Java的反射技術(shù)來獲取 Annotation對象,因為你除此之外沒有別的獲取注解對象的方法
- 注解也可以沒有定義成員, 不過這樣注解就沒啥用了
PS:自定義注解需要使用到元注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}
???????在注解中一般會有一些元素以表示某些值输枯。注解的元素看起來就像接口的方法议泵,唯一的區(qū)別在于可以為其制定默認值。沒有元素的注解稱為標記注解桃熄,上面的@Test就是一個標記注解先口。
???????注解的可用的類型包括以下幾種:所有基本類型、String、Class碉京、enum厢汹、Annotation、以上類型的數(shù)組形式谐宙。元素不能有不確定的值烫葬,即要么有默認值,要么在使用注解的時候提供元素的值凡蜻。而且元素不能使用null作為默認值搭综。注解在只有一個元素且該元素的名稱是value的情況下,在使用注解的時候可以省略“value=”划栓,直接寫需要的值即可兑巾。
四、反射機制與注解機制
AnnotatedElement 接口是所有程序元素(Class茅姜、Method和Constructor)的父接口闪朱,所以程序通過反射獲取了某個類的AnnotatedElement對象之后,程序就可以調(diào)用該對象的如下四個個方法來訪問Annotation信息:
方法 | 含義 |
---|---|
<T extends Annotation> T getAnnotation(Class<T> annotationClass) | 返回改程序元素上存在的钻洒、指定類型的注解奋姿,如果該類型注解不存在,則返回null |
Annotation[] getAnnotations() | 返回該程序元素上存在的所有注解素标。 |
boolean is AnnotationPresent(Class<?extends Annotation> annotationClass) | 判斷該程序元素上是否包含指定類型的注解称诗,存在則返回true,否則返回false |
Annotation[] getDeclaredAnnotations() | 返回直接存在于此元素上的所有注解头遭。與此接口中的其他方法不同寓免,該方法將忽略繼承的注解。(如果沒有注釋直接存在于此元素上计维,則返回長度為零的一個數(shù)組袜香。)該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會對其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響鲫惶。 |
default <T extends Annotation> T[] getAnnotationsByType(Class <T> annotationClass) | 返回該元素制定類型的注解 |
default <T extends Annotation> T getDeclaredAnnotation( Class <T> annotationClass) | 返回直接存在與該元素上的所有注解 |
default <T extends Annotation>T[] getDeclaredAnntationsByType(Class <T> annotationClass) | 返回直接存在該元素上某類型的注解 |
接下來讓我們在代碼中看看二者的結(jié)合
//定義一個可以注解在Class,interface,enum上的注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetType {
/**
* 定義注解的一個元素 并給定默認值
* @return
*/
String value() default "MyAnTargetType我是定義在類接口枚舉類上的注解元素value的默認值";
}
//定義一個可以注解在PARAMETER上的注解
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetParameter {
/**
* 定義注解的一個元素 并給定默認值
* @return
*/
String value() default "MyAnTargetParameter我是定義在參數(shù)上的注解元素value的默認值";
}
// 定義一個可以注解在METHOD上的注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetMethod {
/**
* 定義注解的一個元素 并給定默認值
*
* @return
*/
String value() default "MyAnTargetMethod我是定義在方法上的注解元素value的默認值";
}
// 定義一個可以注解在FIELD上的注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnTargetField {
/**
* 定義注解的一個元素 并給定默認值
* @return
*/
String value() default "MyAnTargetField我是定義在字段上的注解元素value的默認值";
}
//創(chuàng)建一個使用了以上四種注解的類
@MyAnTargetType
public class AnnotationTest {
@MyAnTargetField
private String field = "我是字段";
@MyAnTargetMethod("測試方法")
public void test(@MyAnTargetParameter String args) {
System.out.println("參數(shù)值 === " + args);
}
}
public class Main{
public static void main(String[] args) {
// 獲取類上的注解MyAnTargetType
MyAnTargetType t = AnnotationTest.class.getAnnotation(MyAnTargetType.class);
System.out.println("類上的注解值 === " + t.value());
MyAnTargetMethod tm = null;
try {
// 根據(jù)反射獲取AnnotationTest類上的test方法
Method method = AnnotationTest.class.getDeclaredMethod("test", String.class);
// 獲取方法上的注解MyAnTargetMethod
tm = method.getAnnotation(MyAnTargetMethod.class);
System.out.println("方法上的注解值 === " + tm.value());
// 獲取方法上的所有參數(shù)注解 循環(huán)所有注解找到MyAnTargetParameter注解
Annotation[][] annotations = method.getParameterAnnotations();
for (Annotation[] tt : annotations) {
for (Annotation t1 : tt) {
if (t1 instanceof MyAnTargetParameter) {
System.out.println("參數(shù)上的注解值 === " + ((MyAnTargetParameter) t1).value());
}
}
}
method.invoke(new AnnotationTest(), "改變默認參數(shù)");
// 獲取AnnotationTest類上字段field的注解MyAnTargetField
MyAnTargetField fieldAn = AnnotationTest.class.getDeclaredField("field").getAnnotation(MyAnTargetField.class);
System.out.println("字段上的注解值 === " + fieldAn.value());
} catch (Exception e) {
e.printStackTrace();
}
運行結(jié)果如下:
類上的注解值 === MyAnTargetType我是定義在類接口枚舉類上的注解元素value的默認值
方法上的注解值 === 測試方法
參數(shù)上的注解值 === MyAnTargetParameter我是定義在參數(shù)上的注解元素value的默認值
參數(shù)值 === 改變默認參數(shù)
字段上的注解值 === MyAnTargetField我是定義在字段上的注解元素value的默認值
最后再寫一個關(guān)于@Inherited例子:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnTargetType {
/**
* 定義注解的一個元素 并給定默認值
* @return
*/
String value() default "MyAnTargetType我是定義在類接口枚舉類上的注解元素value的默認值";
}
public class Main extends AnnotationTest{
public static void main(String[] args) {
MyAnTargetType t2 = Main.class.getAnnotation(MyAnTargetType.class);
System.out.println("類上的注解值 === "+t2.value());
}
}
運行如下:
類上的注解值 === 我是定義在類接口枚舉類上的注解元素value的默認值
說明已經(jīng)獲取到了父類AnnotationTest的注解了
如果MyAnTargetType去掉@Inherited注解運行則報錯如下:
Exception in thread "main" java.lang.NullPointerException
at com.company.Main.main(Main.java:41)
最后借用下別人的Java注解的基礎(chǔ)知識點導圖
關(guān)于Java注解機制的基礎(chǔ)就講完了蜈首,歡迎大家前來diss