java註解使用總結

  • 2019 年 10 月 3 日
  • 筆記

2005年,sun公司推出了jdk1.5,同時推出的註解功能吸引了很多人的目光,使用註解編寫程式碼,能夠減輕java程式設計師繁瑣配置的痛苦。

使用註解可以編寫出更加易於維護,bug更少的程式碼。

註解是什麼呢?按照官方的說法,註解就是元標籤,可以添加到你的程式碼,並應用於包聲明、類型聲明、構造函數、方法、欄位、參數和變數。

註解提供了一種非常有用的方法來顯示你編寫的方法是否依賴於其他方法,它們是否完整,編寫出的類是否引用了其他類,等等。

按照Oracle官方的說法,基於註解編寫出的java程式碼會根據源程式碼中的註解生成模板程式碼,從而避免我們在大多數情況下編寫模板程式碼。這導致了一種聲明式編程風格,在這種風格中,程式設計師說要做什麼功能,工具就寫出相應的程式碼來實現它。

簡而言之,註解是一種機制,用於將元標籤與程式元素相關聯,並允許編譯器或虛擬機從這些註解元素中提取程式行為,並在必要時生成相互依賴的程式碼。

現在開始我們的java註解學習之旅。

內置註解
java會內置一些已經實現好的註解,可以直接使用,內置的註解主要用於給java編譯器提供指令。

java內置的註解有五個:

  • @Deprecated
  • @Override
  • @SuppressWarnings
  • @SafeVarargs
  • @FunctionalInterface

@Deprecated註解主要用於給類、方法、變數打上不建議使用的標籤,如果你的程式碼使用了不建議使用的類、方法、變數,編譯器就會給你一個警告。
下面是使用@Deprecated的示例:

public class AnnotationTest {      public static void main(String args[]){          MyAnnotation myAnnotation = new MyAnnotation();          int age = myAnnotation.age;          myAnnotation.eat();      }  }    @Deprecated  class MyAnnotation {      @Deprecated      int age;        @Deprecated      void eat(){        }  }

定義了一個MyAnnotation,在類名,變數名和方法名上都使用的@Deprecated註解。

使用了@Deprecated標識的類、方法或變數時,

@Deprecated還有另外一個用處,就是在javadoc文檔中寫明類、方法或變數為什麼不建議使用,並且給出替代方法:

@Deprecated  /**    @deprecated 已廢棄,請使用MyNewComponent.  */  class MyComponent {    }

@Override註解在方法上使用,標識這個方法重寫父類的方法。如果這個方法和父類方法不一致,編譯器就會顯示錯誤。

強烈建議在重寫父類的方法上使用@Override註解,不用也不會有什麼影響,但是如果不使用@Override註解,當有人修改父類的方法時,你就無法識別出子類的方法是否重寫了父類的方法。而使用了@Override註解,只要父類沒有這個方法,編譯器就會提示父類沒有對應方法的錯誤。
下面是使用@Override註解的示例:

class Anaimal{      public void run(){        }  }    class Cat extends Anaimal{      @Override      public void run(){        }  }

Cat類的run()方法使用了@Override註解,如果將Cat類中的方法改為run1(),編譯器就會顯示The method run1() of type Cat must override or implement a supertype的錯誤。

如果不使用@Override註解,將Cat類中的方法改為run1(),系統則不會報錯。

@SuppressWarnings註解也是在方法上使用,用於抑制警告,在調用deprecated的方法或者進行不安全的類型轉化時,編譯器會發出一些警告,使用@SuppressWarnings就可以忽略那些警告。
使用示例:

@SuppressWarnings  public void methodWithWarning() {      }

@SafeVarargs註解主要用於抑制參數類型安全檢查警告,這個是jdk1.7新增的功能。
使用示例:

@SafeVarargs  static void testSafeVarargs(List<String> ... stringLists) {      Object[] array = stringLists;      List<Integer> tmpList = Arrays.asList(42);      array[0] = tmpList;      String s = stringLists[0].get(0);  }

如果不使用@SafeVarargs註解,編譯器會給出警告資訊:Type safety: Potential heap pollution via varargs parameter stringLists
使用@SafeVarargs有個前提,你必須保證某個使用了可變長度參數的方法,在與泛型類一起使用時不會出現類型安全問題。否則在運行行時會拋出 ClassCastException異常。

@SafeVarargs註解只能在滿足如下條件的方法上使用:

  • 參數長度可變的方法或構造方法。
  • 方法必須聲明為static或final。

@FunctionalInterface註解主要用於編譯級錯誤檢查,加上該註解,當你寫的介面不符合函數式介面定義的時候,編譯器會報錯。
使用示例:

@FunctionalInterface  interface GreetingService  {      void readMessage(String message);  }

創建註解
註解的創建和介面有點類似,都是使用interface關檢字,區別是創建註解時,需要在interface前面加上一個@字元。

下面是創建一個註解的例子:

public @interface MyAnnotation {  }

上面創建註解沒有任何成員變數。

創建帶成員變數的註解:

@interface TestAnnotation{      int age();      String name();  }  

TestAnnotation註解有兩個成員變數,age和name。
註解的成員變數以無參數無方法體的方法形式聲明。

使用default關鍵字指定註解成員變數的默認值:

@interface TestAnnotation{      int age() default  2;      String name() default  「小明」;  }

使用自定義註解時,如果該註解的變數有默認值,可以不為成員變數指定值,直接使用默認值。
使用示例:

@interface TestAnnotation{      int age() default 2;      String name() default "小明";  }      class MyAnnotation {      @TestAnnotation      public void getInfo(){        }  }

如果註解的變數沒有默認值,在使用時必須為每個變數都指定值,程式碼如下:

@interface TestAnnotation{      int age();      String name();  }      class MyAnnotation {      @TestAnnotation(age=2,name="小明")      public void getInfo(){        }  }

元註解
什麼是元註解呢?元註解就是註解的註解,也就是用於定義註解的註解,可以理解為註解的基礎數據類型。這玩意真的很拗口,還是看程式碼比較舒服。

@Target(ElementType.METHOD)  @interface Test_Target {     public String doTestTarget();  }

@Target就是元註解,用於定義Test_Target註解。

java提供五種元註解,分別是:

  • @Retention 指定註解的生命周期,即存活時間。
  • @Documented javadoc命令生成的文檔中體現註解的內容
  • @Target 指定註解可用於哪些元素,例如類、方法、變數等
  • @Inherited 註解的繼承性,
  • @Repeatable 可重複使用的註解

@Retention用於指定註解的生命周期,即存活時間。@Retention提供了如下的三個值,在使用@Retention時,必須使用其中的一個值。

  • RetentionPolicy.SOURCE 註解只在源碼階段保留,在編譯器進行編譯生成class文件時丟棄,無法通過反射獲取註解資訊。
  • RetentionPolicy.CLASS 註解只被保留到編譯進行的時候,它並不會被載入到JVM中,無法通過反射獲取註解資訊,這是默認值。
  • RetentionPolicy.RUNTIME 註解可以保留到程式運行的時候,它會被載入進入到 JVM 中,程式運行時可以通過反射獲取到它們。

下面是使用@Retention定義一個自定義註解的示例:

@Retention(RetentionPolicy.RUNTIME)  @interface Test_Retention{    }    @Test_Retention  class MyAnnotation {      public void getInfo(){        }  }

上面的程式碼創建了一個Test_Retention的註解,並且使用@Retention指定Test_Retention註解可以保留到程式運行的時候(RetentionPolicy.RUNTIME),MyAnnotation使用@Test_Retention修飾,因此在運行時可以通過反射獲取到MyAnnotation的註解資訊。

@Documented如果類A使用了@Documented元註解註解的註解,那麼在使用javadoc生成的類A的文檔會包含有相應的註解資訊。
使用示例:

@Documented  @interface TestDocument{      String doTestDocument();  }    @TestDocument (doTestDocument="保留註解資訊測試")  class MyAnnotation {        public void getInfo(){        }  }

@Target指定了註解所修飾對象的範圍,可用於變數、參數、方法、包資訊等。
@Target元註解提供如下的八個值:

  • ElementType.ANNOTATION_TYPE 用於描述註解類型
  • ElementType.CONSTRUCTOR 用於註解構造方法
  • ElementType.FIELD 用於變數註解
  • ElementType.LOCAL_VARIABLE 用於局部變數註解
  • ElementType.METHOD 用於方法註解
  • ElementType.PACKAGE 用於包註解
  • ElementType.PARAMETER 用於方法內的參數註解
  • ElementType.TYPE 用於類、介面、枚舉註解

程式碼示例:

@Target(ElementType.METHOD)  @interface TestMethodTarget{    }    @Target(ElementType.FIELD)  @interface TestFieldTarget{    }    @Target(ElementType.TYPE)  @interface TestTypeTarget{    }

TestMethodTarget註解只能用於註解類的方法,TestFieldTarget只能用於註解類的成員變數,TestTypeTarget可用於註解類、介面(包括註解類型) 或enum聲明

@Inherited指定了註解可被繼承。某個類使用了被@Inherited修飾的註解,那麼那個註解也會用到該類的子類。
程式碼示例:

@Inherited  @interface TestInherited{    }

@Repeatable同一個註解可多次使用。例如一個人有多種愛好,跑步、畫畫、看電影等。
示例程式碼:

@interface Persons {      Person[]  value();  }      @Repeatable(Persons.class)  @interface Person{      String hobby default "";  }      @Person(hobby="runing")  @Person(hobby="drawing")  @Person(hobby="watching movies")  public class Tom{    }

上面的程式碼,@Repeatable 註解了 Person。而 @Repeatable後面括弧中的類相當於一個容器註解。
什麼是容器註解呢?就是用來存放其它註解的地方。它本身也是一個註解。

上面詳細介紹了註解的知識以及自定義註解的語法。現在我們來看下如何使用註解進行單元測試。

使用註解這要到反射的知識,關於反射的知識請看我的另外一篇文章「Java反射使用總結」。在得到反射對象後,要調用isAnnotationPresent方法這個對象是否包含指定類型的註解。 Annotation

示例程式碼:

public class Marathonrunner {      @RuningTest      public void test5km(){          System.out.println("進行5公里跑步測試");      }        public void test10km(){          System.out.println("進行10公里跑步測試");      }        @RuningTest      public void test21km(){          System.out.println("進行21公里跑步測試");      }        public void test42km(){          System.out.println("進行42公里跑步測試");      }    }    @Retention(RetentionPolicy.RUNTIME)  @Target(ElementType.METHOD)  @interface  RuningTest{    }      public class RuntestTool {      public static void main(String args[]){          Marathonrunner xiaoming = new Marathonrunner();            Class clazz = xiaoming.getClass();            Method[] methods = clazz.getDeclaredMethods();          for (Method method : methods) {              if(method.isAnnotationPresent(RuningTest.class)){                  try {                      method.setAccessible(true);                      method.invoke(xiaoming, null);                    } catch (Exception e) {                    }              }          }      }  }

運行結果:

進行21公里跑步測試  進行5公里跑步測試

上面的程式碼,我們定義了一個RuningTest註解,裡面沒有任何變數。這個註解使用@Retention和@Target元註解修飾,其中@Target元註解的值規定了這個RuningTest註解只能在方法上使用,而@Retention元註解值指定了在運行時可以獲取到註解的資訊。

定義了一個Marathonrunner馬拉松遠動員類,裡面有4個方法。

定義了一個專門用於測試Marathonrunner運動員方法的類。如果我們想測試某個類,只需在那個類上添加@RuningTest註解,不加@RuningTest註解的方法不會進行測試。test5km()和test21km()方法都加了@RuningTest註解,所以被測試到。

註解在spring,mybatis註解中廣發應用。下次專門寫篇文章講下spring中註解的應用。

總結:
本文主要講解了註解的概念,元註解的概念,如何自定義註解,以及如何使用自己定義註解進行單元測試。