1. 程式人生 > >java註解使用總結

java註解使用總結

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中註解的應用。

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