Java復習總結——註解
註解
概念及作用
- 概念
- 註解即元數據,就是源代碼的元數據
- 註解在代碼中添加信息提供了一種形式化的方法,可以在後續中更方便的 使用這些數據
- Annotation是一種應用於類、方法、參數、變量、構造器及包聲明中的特殊修飾符。它是一種由JSR-175標準選擇用來描述元數據的一種工具。
- 作用
- 生成文檔
- 跟蹤代碼依賴性,實現替代配置文件功能,減少配置。如Spring中的一些註解
- 在編譯時進行格式檢查,如@Override等
- 每當你創建描述符性質的類或者接口時,一旦其中包含重復性的工作,就可以考慮使用註解來簡化與自動化該過程。
什麽是java註解?
在java語法中,使用@
符號作為開頭,並在@後面緊跟註解名。被運用於類,接口,方法和字段之上,例如:
@Override
void myMethod() {
......
}
這其中@Override就是註解。這個註解的作用也就是告訴編譯器,myMethod()方法覆寫了父類中的myMethod()方法。
java中內置的註解
java中有三個內置的註解:
@Override:表示當前的方法定義將覆蓋超類中的方法,如果出現錯誤,編譯器就會報錯。
@Deprecated:如果使用此註解,編譯器會出現警告信息。
@SuppressWarnings:忽略編譯器的警告信息。
本文不在闡述三種內置註解的使用情節和方法,感興趣的請看這裏
元註解
自定義註解的時候用到的,也就是自定義註解的註解;(這句話我自己說的,不知道對不對)
元註解的作用就是負責註解其他註解。Java5.0
定義了4個標準的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。
Java5.0
定義的4個元註解:
- @Target
- @Retention
- @Documented
- @Inherited
java8加了兩個新註解,後續我會講到。
這些類型和它們所支持的類在java.lang.annotation包中可以找到。
@Target
@Target說明了Annotation所修飾的對象範圍:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。
作用:用於描述註解的使用範圍(即:被描述的註解可以用在什麽地方)
取值(ElementType)有:
類型 | 用途 |
---|---|
CONSTRUCTOR | 用於描述構造器 |
FIELD | 用於描述域 |
LOCAL_VARIABLE | 用於描述局部變量 |
METHOD | 用於描述方法 |
PACKAGE | 用於描述包 |
PARAMETER | 用於描述參數 |
TYPE | 用於描述類、接口(包括註解類型) 或enum聲明 |
比如說這個註解表示只能在方法中使用:
@Target({ElementType.METHOD})
public @interface MyCustomAnnotation {
}
//使用
public class MyClass {
@MyCustomAnnotation
public void myMethod()
{
......
}
}
@Retention
@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請註意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。
作用:表示需要在什麽級別保存該註釋信息,用於描述註解的生命周期(即:被描述的註解在什麽範圍內有效)
取值(RetentionPoicy)有:
類型 | 用途 | 說明 |
---|---|---|
SOURCE | 在源文件中有效(即源文件保留) | 僅出現在源代碼中,而被編譯器丟棄 |
CLASS | 在class文件中有效(即class保留) | 被編譯在class文件中 |
RUNTIME | 在運行時有效(即運行時保留) | 編譯在class文件中 |
使用示例:
/***
* 字段註解接口
*/
@Target(value = {ElementType.FIELD})//註解可以被添加在屬性上
@Retention(value = RetentionPolicy.RUNTIME)//註解保存在JVM運行時刻,能夠在運行時刻通過反射API來獲取到註解的信息
public @interface Column {
String name();//註解的name屬性
}
@Documented
@Documented用於描述其它類型的annotation應該被作為被標註的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記註解,沒有成員。
作用:將註解包含在javadoc中
示例:
java.lang.annotation.Documented
@Documented
public @interface MyCustomAnnotation { //Annotation body}
@Inherited
是一個標記註解
闡述了某個被標註的類型是被繼承的
使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類
@Inherited annotation類型是被標註過的class的子類所繼承。類並不從實現的接口繼承annotation,方法不從它所重載的方法繼承annotation當@Inherited annotation類型標註的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被發現,或者到達類繼承結構的頂層。
作用:允許子類繼承父類中的註解
示例,這裏的MyParentClass 使用的註解標註了@Inherited,所以子類可以繼承這個註解信息:
java.lang.annotation.Inherited
@Inherited
public @interface MyCustomAnnotation {
}
@MyCustomAnnotation
public class MyParentClass {
...
}
public class MyChildClass extends MyParentClass {
...
}
自定義註解
格式
public @interface 註解名{
定義體
}
註解參數的可支持數據類型:
- 所有基本數據類型(int,float,double,boolean,byte,char,long,short)
- String 類型
- Class類型
- enum類型
- Annotation類型
- 以上所有類型的數組
規則
- 修飾符只能是public 或默認(default)
- 參數成員只能用基本類型byte,short,int,long,float,double,boolean八種基本類型和String,Enum,Class,annotations及這些類型的數組
- 如果只有一個參數成員,最好將名稱設為”value”
- 註解元素必須有確定的值,可以在註解中定義默認值,也可以使用註解時指定,非基本類型的值不可為null,常使用空字符串或0作默認值
- 在表現一個元素存在或缺失的狀態時,定義一下特殊值來表示,如空字符串或負值
示例:
/**
* test註解
* @author ddk
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestAnnotation {
/**
* id
* @return
*/
public int id() default -1;
/**
* name
* @return
*/
public String name() default "";
}
註解處理器類庫
java.lang.reflect.AnnotatedElement
Java使用Annotation接口來代表程序元素前面的註解,該接口是所有Annotation類型的父接口。除此之外,Java在java.lang.reflect 包下新增了AnnotatedElement接口,該接口代表程序中可以接受註解的程序元素,該接口主要有如下幾個實現類:
- Class:類定義
- Constructor:構造器定義
- Field:累的成員變量定義
- Method:類的方法定義
- Package:類的包定義
java.lang.reflect 包下主要包含一些實現反射功能的工具類,實際上,java.lang.reflect 包所有提供的反射API擴充了讀取運行時Annotation信息的能力。當一個Annotation類型被定義為運行時的Annotation後,該註解才能是運行時可見,當class文件被裝載時被保存在class文件中的Annotation才會被虛擬機讀取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通過反射獲取了某個類的AnnotatedElement對象之後,程序就可以調用該對象的如下四個個方法來訪問Annotation信息:
- 方法1: T getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定類型的註解,如果該類型註解不存在,則返回null。
- 方法2:Annotation[] getAnnotations():返回該程序元素上存在的所有註解。
- 方法3:boolean is AnnotationPresent(Class annotationClass):判斷該程序元素上是否包含指定類型的註解,存在則返回true,否則返回false.
- 方法4:Annotation[] getDeclaredAnnotations():返回直接存在於此元素上的所有註釋。與此接口中的其他方法不同,該方法將忽略繼承的註釋。(如果沒有註釋直接存在於此元素上,則返回長度為零的一個數組。)該方法的調用者可以隨意修改返回的數組;這不會對其他調用者返回的數組產生任何影響。
註解處理器示例:
/***********註解聲明***************/
/**
* 水果名稱註解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
/**
* 水果顏色註解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
/**
* 顏色枚舉
* @author peida
*
*/
public enum Color{ BULE,RED,GREEN};
/**
* 顏色屬性
* @return
*/
Color fruitColor() default Color.GREEN;
}
/**
* 水果供應者註解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供應商編號
* @return
*/
public int id() default -1;
/**
* 供應商名稱
* @return
*/
public String name() default "";
/**
* 供應商地址
* @return
*/
public String address() default "";
}
/***********註解使用***************/
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
@FruitProvider(id=1,name="陜西紅富士集團",address="陜西省西安市延安路89號紅富士大廈")
private String appleProvider;
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
}
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
public String getAppleProvider() {
return appleProvider;
}
public void displayName(){
System.out.println("水果的名字是:蘋果");
}
}
/***********註解處理器***************/
//其實是用的反射
public class FruitInfoUtil {
public static void getFruitInfo(Class<?> clazz){
String strFruitName=" 水果名稱:";
String strFruitColor=" 水果顏色:";
String strFruitProvicer="供應商信息:";
Field[] fields = clazz.getDeclaredFields();
for(Field field :fields){
if(field.isAnnotationPresent(FruitName.class)){
FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
strFruitName=strFruitName+fruitName.value();
System.out.println(strFruitName);
}
else if(field.isAnnotationPresent(FruitColor.class)){
FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
}
else if(field.isAnnotationPresent(FruitProvider.class)){
FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
strFruitProvicer=" 供應商編號:"+fruitProvider.id()+" 供應商名稱:"+fruitProvider.name()+" 供應商地址:"+fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
}
/***********輸出結果***************/
public class FruitRun {
/**
* @param args
*/
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
}
}
====================================
水果名稱:Apple
水果顏色:RED
供應商編號:1 供應商名稱:陜西紅富士集團 供應商地址:陜西省西安市延安路89號紅富士大廈
Java 8 中註解新特性
@Repeatable 元註解,表示被修飾的註解可以用在同一個聲明式或者類型加上多個相同的註解(包含不同的屬性值)
@Native 元註解,本地方法
java8 中Annotation 可以被用在任何使用 Type 的地方
//初始化對象時
String myString = new @NotNull String();
//對象類型轉化時
myString = (@NonNull String) str;
//使用 implements 表達式時
class MyList<T> implements @ReadOnly List<@ReadOnly T>{
...
}
//使用 throws 表達式時
public void validateValues() throws @Critical ValidationFailedException{
...
}
思維導圖
擴展閱讀
http://www.cnblogs.com/ITtangtang/p/3974531.html
www.jianshu.com/p/948549b92e0a
https://www.jianshu.com/p/89c07ce0c99c
Java復習總結——註解