Java註解詳解
阿新 • • 發佈:2018-12-14
一、介紹
1 概念
註解(Annotation),也叫元資料。一種程式碼級別的說明。它是Java 5新增的技術。要區別註釋,註解是程式碼裡的一種特殊標記,可在編譯前、編譯後、執行時等不同的時期被讀取,並作出相應的處理。
2 原則
由於註解的程式碼是附屬資訊,它要遵循一個基本原則:註解不能直接干擾程式碼的執行,無論增加或者刪除註解,程式碼都能正常的執行。
二、如何定義註解
1 建立
定義新的註解使用@interface關鍵字
public @interface MyAnnotation {
}
1.1 成員變數
Annotation只有成員變數。其成員變數以“無形參的方法”形式來宣告。例:
public @interface MyAnnotation {
//String定義了成員型別,name定義了成員名
String name();
//也可以在定義成員變數時指定預設值
int age() default 18;
}
該註解如何使用呢?
class AnnoTest{
//age已經為其指定預設值,那麼在使用的時候就可以不為它指定值,age就會使用這個指定的預設值
@MyAnnotation(name = "張三")
public void test() {
}
}
若是Annotation只有一個成員變數,定義是該成員名必須為value
public @interface MyAnnotation {
String value();
}
該註解使用時也可以省略成員名和“=”號,例:
class AnnoTest{
@MyAnnotation( "張三")
public void test() {
}
}
根據Annotation包含的成員數目可以將其分為兩類:
- 標記註解:沒有成員變數,如@Override
- 元資料註解:包含成員變數的註解,如@SuppressWarnings
1.2 元註解
我們自定義註解時可以使用jdk自帶的4個元註解來修飾定義的註解
- @Retention
- @Target
- @Documented
- @Inherited
1.2.1 @Retention
作用:設定註解保留的截止時間
public @interface Retention {
//保留策略,該變數是個列舉型別
RetentionPolicy value();
}
public enum RetentionPolicy {
//只保留在原始碼中,編譯後丟失
SOURCE,
//保留在原始碼和class檔案中,程式執行執行時丟失
CLASS,
//保留在原始碼、class檔案中,程式執行時jvm依然會保留該註解資訊,可以通過反射回去該註解的資訊
RUNTIME
}
例:
//該註解在程式執行時可以利用反射獲取value值
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "";
}
1.2.2 @Target
作用:指定該註解可以修飾哪些元素
public @interface Target {
//指定註解修飾的元素型別,該成員是個列舉陣列,一次可以指定多個可修飾元素
ElementType[] value();
}
public enum ElementType {
/** 能修飾類、介面或列舉型別 */
TYPE,
/** 能修飾成員變數 */
FIELD,
/** 能修飾方法 */
METHOD,
/** 能修飾引數 */
PARAMETER,
/** 能修飾構造器 */
CONSTRUCTOR,
/** 能修飾區域性變數 */
LOCAL_VARIABLE,
/** 能修飾註解 */
ANNOTATION_TYPE,
/** 能修飾包 */
PACKAGE,
/**
* 可以用在 Type 的宣告式前
* @since 1.8
*/
TYPE_PARAMETER,
/**
*可以用在所有使用 Type 的地方(如:泛型,型別轉換等)
* @since 1.8
*/
TYPE_USE
}
示例1:單個ElementType
@Target(ElementType.FIELD)
public @interface MyAnnotation {
String value() default "";
}
示例2:多個ElementType
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface MyAnnotation {
String value() default "";
}
1.2.3 @Documented
使用了@Documented修飾自定義註解A,在使用javadoc命令生成API文件後,所有使用A註解修飾的程式元素,將會包含註解A的說明。
1.2.4 @Inherited
使用了@Inherited修飾註解可以被繼承。
三、註解的提取
Java註解可以修飾類、類屬性、類方法、類構造器等等,這些屬性被註解修飾後不會自動的生效,需要我們把這些資訊提取出來,加上處理程式碼,下面列舉一些註解的一些常見的使用場景。
1 修飾類
1.1 定義註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Entity{
String name() default "";
String description() default "這裡可以對錶模型進行一些描述";
}
1.2 使用註解
@Entity(name = "user")
class User{
}
1.3 提取註解資訊
public class AnnoTest {
public static void main(String[] args) {
//判斷User類是否被@Entity註解修飾
boolean present = User.class.isAnnotationPresent(Entity.class);
if (present) {
Entity annotation = User.class.getAnnotation(Entity.class);
System.out.println("表名:"+annotation.name()+"\n"+"描述:"+annotation.description());
}
}
}
2 修飾類屬性
2.1 定義註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Field{
String name() default "";
boolean exist() default true;
}
2.2 使用註解
class User{
@Field(name = "username")
private String username;
}
2.3 提取註解資訊
public class AnnoTest {
public static void main(String[] args) throws NoSuchFieldException {
Class<User> c = User.class;
//根據屬性名獲取類屬性
java.lang.reflect.Field field = c.getDeclaredField("username");
Field anno = field.getAnnotation(Field.class);
if (anno != null) {
System.out.println("表字段名:"+anno.name());
System.out.println("是否存在該欄位:"+anno.exist());
}
}
}
3 修飾類方法
3.1 定義註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Log{
String user() default "";
String operation() default "he";
}
3.2 使用註解
class User{
@Log(user = "張三",operation = "刪除文章")
public void deleteArticle() {
}
}
3.3 提取註解資訊
public class AnnoTest {
public static void main(String[] args) throws NoSuchFieldException {
Class<User> c = User.class;
//獲取該類所有方法
Method[] methods = c.getDeclaredMethods();
if (methods.length>0) {
for (Method m:methods) {
if (m.getName().equals("deleteArticle")) {
Log anno = m.getDeclaredAnnotation(Log.class);
if (anno != null) {
StringBuffer sb = new StringBuffer();
LocalDate now = LocalDate.now();
sb.append(anno.user()).append("於").append(now.toString()).append(",呼叫了方法“"+m.getName()+"”,進行了"+anno.operation()+"操作!");
System.out.println(sb.toString());
}
}
}
}
}
}
四、註解的本質
1 新建一個註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log{
@Deprecated
String user() default "";
String operation() default "喝茶";
}
2 編譯註解
開啟dos命令框,在該檔案目錄下輸入命令
javac Log.java
得到Log.class檔案
3 反編譯log.class檔案
輸入命令
javap -verbose -c Log.class > Log.txt
得到Log.txt位元組碼檔案: 總結
- 從反編譯的資訊裡可以知道,註解就是一個繼承java.lang.annotation.Annotation介面的介面。
- 註解的成員變數會被編譯為同名的抽象方法,成員型別就是方法返回值型別,通過呼叫該註解物件的方法就能得到該方法對應的成員變數值。