目錄
java註解
理解java註解
註解是java提供的一種對元重新中元素關聯信息和元數據的途徑和方法。
Annatation(註解)是一個接口,重新可以通過反射來獲取指定程序中元素的Annatation對象,然後通過該對象來獲取註解中的元數據信息
基本語法
聲明註解與元註解
示例:
// 聲明 Test註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test{
}
我們使用了@interface聲明瞭Test註解,並使用@Target註解傳入ElementType.METHOD參數來標明。@Test只能用於方法上,@Retention(RetentionPolicy.RUNTIME)則用來表示該註解的生存期是運行時。
從代碼上看註解的定義很想接口的定義,畢竟在編譯後也會生成Test.class文件。對於@Targer和@Retention是由java提供的元註解(標記其他註解的註解)。
-
@Target用來約束註解可以應用的地方(如方法、類或字段),其中ElementType是枚舉類型其定義如下:表示可能的取值範圍
public enum ElementType{ // 標明該註解可以用於類、接口(包括註解類型)或enum聲明 TYPE, // 標明該註解可以用於字段(域)聲明,包括enum實例 FIELD, // 標明該註解可以用於方法聲明 METHOD, // 標明該註解可以用於參數聲明 PARAMETER, // 標明註解可以用於構造函數聲明 CONSTRUCTOR, // 標明註解可以用於局部變量聲明 LOCAL_VARIABLE, // 標明註解可以用於註解聲明(應用於另一個註解上) ANNOTATION_TYPE, // 標明註解可以用於包聲明 PACKAGE, //標明註解可以用於類型參數聲明(1.8新加入) TYPE_PARAMETER, // 類型使用聲明(1.8新加入) TYPE_USE }
當註解爲指定Target值時,此註解可以用於任何元素之上,多個值使用{}包含並用逗號隔開。
-
@Retention用來約束註解的生命週期
- SOURCE:源碼級別,註解將會被編譯器丟棄,不會保留在編譯好的class文件中
- CLASS:類文件級別,註解在class文件中可用,但是會被JVM丟棄,在執行的時候不會加載到虛擬機中。當註解未定義Retention值時,默認值是CLASS,如Java內置註解,@Override、@Deprecated、@SuppressWarnning等
- RUNTIME:運行時級別,將在運行期(JVM)也保留,因此可用通過反射機制去讀取註解的內容。如SpringMvc中的@Controller、@Autowired、@RequestMapping等。
註解元素以及其數據類型
- 標記註解:註解內部沒有定義其他元素,唯一作用就是標記聲明
在自定義註解中,一般會包含一些元素以表示某些值,方便處理器使用。
定義一個DBTable的註解,主要用於數據庫表與Bean類的映射,在其中聲明一個String類型的name元素,其默認值是空字符,但是必須注意到對應對應元素的證明用採用方法的聲明方式,同時可選擇使用default提供默認值
@Target(Element.TYPE)// 只能應用於類上
@Retention(RetentionPolicy.RUNTIME)// 保存到運行時
public @interface DBTable{
String name() default "";
}
@DBTable使用方式
//在類上使用該註解
@DBTable(name = "MEMBER")
public class Member{
}
支持的數據類型:
- 所有基本類型
- String
- Class
- enum
- Annotation
- 上述類型的數組
注意:聲明註解元素時可以使用基本類型,但是不允許使用任何包裝類型,註解也是可以作爲元素的類型,也就是嵌套註解
編譯器對默認值的限制
- 元素不能有不確定的值:要麼有默認值,要麼在使用註解時提供元素的值
- 對於非基本類型的元素,無論是在源代碼中聲明,還是在註解接口中定義默認值,都不能以null作爲值
- 所以說只能定義一些特殊的值來表示某個元素不存在
註解不支持繼承
註解不支持繼承,因此不能使用關鍵字extends來繼承某個@interface,但是註解在編譯後會自動繼承java.lang.annotation.Annotation接口。
快捷方式
註解中定義了名爲value的元素,並且在使用該註解時,如果該元素時唯一需要賦值的一個元素,那麼此時無需使用key=value的語法,只需要在括號內給出所需的值。
可以應用於人格合法類型的元素,但是限制了元素名必須爲value。
- 示例:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface IntegerValue{
int value() default 0;
String name() default "";
}
public class test {
// 只想給value賦值
@IntegerValue(20)
private int age;
// 都需要賦值是隻能使用key=value
@IntegerValue(value=1000,name="zhonghu")
public int money;
}
java內置註解與其他元註解
java提供的內置註解,主要有三種
-
@Override:用於標明此方法覆蓋了父類的方法
@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 { }
-
@SuppressWarnnings:用於有選擇的關閉編譯器對類、方法、成員變量、變量初始化的警告
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
其內部有一個String數組主要接收值如下
- deprecation:使用了不贊成使用的類或方法時的警告;
- unchecked:執行了未檢查的轉換時的警告,例如當使用集合時沒有用泛型 (Generics) 來指定集合保存的類型;
- fallthrough:當 Switch 程序塊直接通往下一種情況而沒有 Break 時的警告;
- path:在類路徑、源文件路徑等中有不存在的路徑時的警告;
- serial:當在可序列化的類上缺少 serialVersionUID 定義時的警告;
- finally:任何 finally 子句不能正常完成時的警告;
- all:關於以上所有情況的警告。
元註解
- @Target
- @Retention
- @Documented:被修飾的註解會生成到javadoc中
- @Inherited:可以讓註解被“繼承”,只是通過使用其讓子類Class對象使用getAnnotations()獲取父類被@Inherited修飾的註解
註解與反射機制
java使用Annotation接口代表註解元素,該接口是所有註解類型的父接口。
同時爲了運行時能準確獲取到註解的相關信息,java在java.lang.reflect反射包下新增了AnnotatedElement接口,用於表示目前正在JVM運行的程序中已使用註解的元素,通過此接口提供的方法可以利用反射技術,讀取註解的信息。
註解處理器
使用註解的過程中很重要的一部分就是創建於使用註解處理器
-
示例:
-
定義註解:
import java.lang.annotation.*; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FruitProvider{ public int id() default -1; public String name() default ""; public String address() default ""; }
-
註解使用:
public class Apple { @FruitProvider(id=1,name="zhonghu",address = "china") private String appleProvider; public String getAppleProvider() { return appleProvider; } public void setAppleProvider(String appleProvider) { this.appleProvider = appleProvider; } }
-
註解處理器:
import java.lang.reflect.Field; public class FruitInfoUtil { public static void getFruitInfo(Class<?> clazz){ String strFruitProvicer = "供應商信息:"; Field[] fields = clazz.getDeclaredFields();//通過反射獲取處理註解 for (Field field : fields) { 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 { public static void main(String[] args) { FruitInfoUtil.getFruitInfo(Apple.class); } }
-
-
結果:
java8中註解增強
元註解Repeatable
表示在同一位置重複相同的註解。
新增的兩種ElementType
- TYPE_PARAMETER
- 用於標註類型參數
- TYPE_USE
- 可以用於標註除class以外的任意類型