1. 程式人生 > >Java註解詳解

Java註解詳解

一、介紹

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包含的成員數目可以將其分為兩類:

  1. 標記註解:沒有成員變數,如@Override
  2. 元資料註解:包含成員變數的註解,如@SuppressWarnings

1.2 元註解

我們自定義註解時可以使用jdk自帶的4個元註解來修飾定義的註解

  1. @Retention
  2. @Target
  3. @Documented
  4. @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位元組碼檔案: @Log位元組碼資訊說明 總結

  1. 從反編譯的資訊裡可以知道,註解就是一個繼承java.lang.annotation.Annotation介面的介面。
  2. 註解的成員變數會被編譯為同名的抽象方法,成員型別就是方法返回值型別,通過呼叫該註解物件的方法就能得到該方法對應的成員變數值。