Java 註解詳細用法

註解的定義

註解通過 @interface 關鍵字進行定義。

public @interface MyAnnotation {
}

註解的應用

上面創建了一個註解,那麼註解的的使用方法是什麼呢。

@MyAnnotation 
public class MyTest {
	...
}

元註解

元註解是可以註解到註解上的註解,或者說元註解是一種基本註解,但是它能夠應用到其它的註解上面。

  • @Retention:
    取值:

    • RetentionPolicy.SOURCE 註解只在源碼階段保留,在編譯器進行編譯時它將被丟棄忽視。
    • RetentionPolicy.CLASS 註解只被保留到編譯進行的時候,它並不會被加載到 JVM 中。
    • RetentionPolicy.RUNTIME 註解可以保留到程序運行的時候,它會被加載進入到 JVM 中,所以在程序運行時可以獲取到它們。
      例子:
    @Retention(RetentionPolicy.RUNTIME)
     public @interface MyAnnotation {
     }
    
  • @Documented: 能夠將註解中的元素包含到 Javadoc 中去

  • @Target: 指定了註解運用的地方。
    @Target 有下面的取值

    • ElementType.ANNOTATION_TYPE 可以給一個註解進行註解
    • ElementType.CONSTRUCTOR 可以給構造方法進行註解
    • ElementType.FIELD 可以給屬性進行註解
    • ElementType.LOCAL_VARIABLE 可以給局部變量進行註解
    • ElementType.METHOD 可以給方法進行註解
    • ElementType.PACKAGE 可以給一個包進行註解
    • ElementType.PARAMETER 可以給一個方法內的參數進行註解
    • ElementType.TYPE 可以給一個類型進行註解,比如類、接口、枚舉
  • @Inherited: 如果一個超類被 @Inherited 註解過的註解進行註解的話,那麼如果它的子類沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解
    例:

    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAnnotation {
    }
    @MyAnnotation 
    public class ClassA {
    
    }
    
    public class  ClassB extends ClassA  {
    
    }
    

    註解 MyAnnotation 被 @Inherited 修飾,之後類 ClassA 被 MyAnnotation 註解,類 ClassB 繼承 ClassA ,類 ClassB 也擁有 MyAnnotation 這個註解。

  • @Repeatable: 註解可多次應用,可重複

註解的屬性

  • 註解的屬性也叫做成員變量。
  • 註解只有成員變量,沒有方法。
  • 註解的成員變量在註解的定義中以“無形參的方法”形式來聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    int id();
    String msg();
}

使用的時候可對其賦值:

@TestAnnotation(id=1,msg="hello annotation")
public class Test {

}

:8\color{red}{注: 在註解中定義屬性時它的類型必須是 8 種基本數據類型外加 類、接口、註解及它們的數組}
可以給屬性設置默認值,默認值用 default 關鍵值指定。
例:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    public int id() default -1;
    public String msg() default "Hello";
}
//有了默認值,可以這樣使用
@MyAnnotation ()
public class Test {}
//所以無需要再在 @MyAnnotation 後面的括號裏面進行賦值了

value\color{red}{如果一個註解內僅僅只有一個名字爲 value 的屬性時,應用這個註解時可以直接接屬性值填寫到括號內}
如:

public @interface MyAnnotation {
    public String value();
}
//可這樣使用
@MyAnnotation ("hi")
int a;
//和
@MyAnnotation(value="hi")
int a;
的效果是一樣的

註解沒有任何屬性

public @interface Perform {}
//使用時括號可以省略
@Perform
public void testMethod(){}

Java 預置的註解

  • @Deprecated 用來標記過時的元素
  • @Override 子類要重載父類的方法
  • @SuppressWarnings 阻止警告
  • @SafeVarargs 參數安全類型註解, 提醒開發者不要用參數做一些不安全的操作,它的存在會阻止編譯器產生 unchecked 這樣的警告
  • @FunctionalInterface 函數式接口註解
    (FunctionalInterface),Lambda\color{red}{函數式接口 (Functional Interface) 就是一個具有一個方法的普通接口, 函數式接口可以很容易轉換爲 Lambda 表達式}

註解與反射

獲取Class上註解的方法:

  • isAnnotationPresent() : 判斷類的對象是否應用了某個註解
      public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
    
  • getAnnotation() :方法來獲取 Annotation 對象
    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
    
  • getAnnotations() :返回註解到這個元素上的所有註解 Annotation 對象
    public Annotation[] getAnnotations() {}
    

例子

例子一:
 @MyAnnotation()
 public class Test {
     public static void main(String[] args) {
         boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
         if ( hasAnnotation ) {
             TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
             System.out.println("id:"+testAnnotation.id());
             System.out.println("msg:"+testAnnotation.msg());
         }
     }
 }

例子二,可檢索出屬性方法上的註解:

 @MyAnnotation(msg="hello")
 public class Test {
     @Check(value="hi")
     int a;
 
     @Perform
     public void testMethod(){}
 
     @SuppressWarnings("deprecation")
     public void test1(){
         Hero hero = new Hero();
         hero.say();
         hero.speak();
     }
 
     public static void main(String[] args) {
 
         boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
 
         if ( hasAnnotation ) {
             TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
             //獲取類的註解
             System.out.println("id:"+testAnnotation.id());
             System.out.println("msg:"+testAnnotation.msg());
         }
 
         try {
         	//取得類的指定屬性
             Field a = Test.class.getDeclaredField("a");
             a.setAccessible(true);
             //獲取一個成員變量上的註解
             Check check = a.getAnnotation(Check.class);
 
             if ( check != null ) {
                 System.out.println("check value:"+check.value());
             }
 
             Method testMethod = Test.class.getDeclaredMethod("testMethod");
 
             if ( testMethod != null ) {
                 // 獲取方法中的註解
                 Annotation[] ans = testMethod.getAnnotations();
                 for( int i = 0;i < ans.length;i++) {
                     System.out.println("method testMethod annotation:"+ans[i].annotationType().getSimpleName());
                 }
             }
         } catch (NoSuchFieldException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
             System.out.println(e.getMessage());
         } catch (SecurityException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
             System.out.println(e.getMessage());
         } catch (NoSuchMethodException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
             System.out.println(e.getMessage());
         }
 	    }
 	}

使用場景

一個測試代碼BUG的例子
Jiecha.java

@Retention(RetentionPolicy.RUNTIME)
public @interface Jiecha {

}

被測試的類
NoBug.java

public class NoBug {

    @Jiecha
    public void suanShu(){
        System.out.println("1234567890");
    }
    @Jiecha
    public void jiafa(){
        System.out.println("1+1="+1+1);
    }
    @Jiecha
    public void jiefa(){
        System.out.println("1-1="+(1-1));
    }
    @Jiecha
    public void chengfa(){
        System.out.println("3 x 5="+ 3*5);
    }
    @Jiecha
    public void chufa(){
        System.out.println("6 / 0="+ 6 / 0);
    }

    public void ziwojieshao(){
        System.out.println("我寫的程序沒有 bug!");
    }

}

測試類
TestTool.java

public class TestTool {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        NoBug testobj = new NoBug();

        Class clazz = testobj.getClass();

        Method[] method = clazz.getDeclaredMethods();
        //用來記錄測試產生的 log 信息
        StringBuilder log = new StringBuilder();
        // 記錄異常的次數
        int errornum = 0;

        for ( Method m: method ) {
            // 只有被 @Jiecha 標註過的方法才進行測試
            if ( m.isAnnotationPresent( Jiecha.class )) {
                try {
                    m.setAccessible(true);
                    m.invoke(testobj, null);

                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    //e.printStackTrace();
                    errornum++;
                    log.append(m.getName());
                    log.append(" ");
                    log.append("has error:");
                    log.append("\n\r  caused by ");
                    //記錄測試過程中,發生的異常的名稱
                    log.append(e.getCause().getClass().getSimpleName());
                    log.append("\n\r");
                    //記錄測試過程中,發生的異常的具體信息
                    log.append(e.getCause().getMessage());
                    log.append("\n\r");
                } 
            }
        }
        log.append(clazz.getSimpleName());
        log.append(" has  ");
        log.append(errornum);
        log.append(" error.");

        // 生成測試報告
        System.out.println(log.toString());

    }
}

測試的結果:

1234567890
1+1=11
1-1=0
3 x 5=15
chufa has error:

  caused by ArithmeticException

/ by zero

NoBug has  1 error.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章