Java基礎教程:註解

Java基礎教程:註解

本篇文章參考的相關資料連接:php

  • 維基百科:https://zh.wikipedia.org/wiki/Java%E6%B3%A8%E8%A7%A3
  • 註解基礎與高級應用:http://linbinghe.com/2017/ac8515d0.html
  • 秒懂註解:https://blog.csdn.net/briblue/article/details/73824058 

概述

  這篇文章參考了不少其餘文章的寫做思路和篇章內容,主要用來幫助咱們更好的理解Java中註解的使用,解開註解的神祕面紗。html

  維基百科上對註解的解釋是這樣的:java

  Java註解又稱Java標註,是Java語言5.0版本開始支持加入源代碼的特殊語法元數據[1]。Java語言中的類、方法、變量、參數和包等均可以被標註。和Javadoc不一樣,Java標註能夠經過反射獲取標註內容。在編譯器生成類文件時,標註能夠被嵌入到字節碼Java虛擬機能夠保留標註內容,在運行時能夠獲取到標註內容[2]。 固然它也支持自定義Java標註[3]app

  咱們能夠這樣理解,就是咱們在類、方法、變量、參數等元素上面貼一個標籤,而且咱們可以在運行時動態的獲取到這些標籤框架

 

  咱們在方法上貼了一個名爲RoleCheck的標籤,它裏面有一個標籤的描述信息爲level。在運行該方法時,咱們同時能夠獲取到這個標籤及裏面的描述信息。具體的語法和更多的內容咱們將會在下面的文章中分享到。ide

內置註解

Java 定義了一套註解,共有 7 個,3 個在 java.lang 中,剩下 4 個在 java.lang.annotation 中
函數

做用在代碼的註解是工具

  • @Override - 檢查該方法是不是重載方法。若是發現其父類,或者是引用的接口中並無該方法時,會報編譯錯誤。
  • @Deprecated - 標記過期方法。若是使用該方法,會報編譯警告。
  • @SuppressWarnings - 指示編譯器去忽略註解中聲明的警告

做用在其餘註解的註解(或者說 元註解)是:測試

  • @Retention - 標識這個註解怎麼保存,是隻在代碼中,仍是編入class文件中,或者是在運行時能夠經過反射訪問
    • SOURCE:註解將被編譯器丟棄
    • CLASS:註解在class文件中可用,但會被JVM丟棄
    • RUNTIME:JVM將在運行期間保留註解,所以能夠經過反射機制讀取註解的信息。
  • @Documented - 標記這些註解是否包含在用戶文檔中。
  • @Target - 標記這個註解應該是哪一種 Java 成員
  • @Inherited - 標記這個註解是繼承於哪一個註解類(默認 註解並無繼承於任何子類)

關於Target:spa

你能夠這樣理解,當一個註解被 @Target 註解時,這個註解就被限定了運用的場景

類比到標籤,本來標籤是你想張貼到哪一個地方就到哪一個地方,可是由於 @Target 的存在,它張貼的地方就很是具體了,好比只能張貼到方法上、類上、方法參數上等等。@Target 有下面的取值

  • ElementType.ANNOTATION_TYPE 能夠給一個註解進行註解
  • ElementType.CONSTRUCTOR 能夠給構造方法進行註解
  • ElementType.FIELD 能夠給屬性進行註解
  • ElementType.LOCAL_VARIABLE 能夠給局部變量進行註解
  • ElementType.METHOD 能夠給方法進行註解
  • ElementType.PACKAGE 能夠給一個包進行註解
  • ElementType.PARAMETER 能夠給一個方法內的參數進行註解
  • ElementType.TYPE 能夠給一個類型進行註解,好比類、接口、枚舉

從 Java 7 開始,額外添加了 3 個註解:

  • @SafeVarargs - Java 7 開始支持,忽略任何使用參數爲泛型變量的方法或構造函數調用產生的警告。
  • @FunctionalInterface - Java 8 開始支持,標識一個匿名函數或函數式接口。
  • @Repeatable - Java 8 開始支持,標識某註解能夠在同一個聲明上使用屢次。

自定義註解

  內置註解咱們能夠直接用到就只有lang中的幾個,annotation中幾個註解的都是做用在其餘註解上的,咱們稱之爲元註解其餘註解就是咱們的自定義註解

定義註解

  使用@interface自定義註解,會自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其餘細節。

  在定義註解時,不能繼承其餘的註解或接口

  @interface用來聲明一個註解,其中的每個方法其實是聲明瞭一個配置參方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。能夠經過default來聲明參數的默認值。

public @interface RoleCheck {
    int[] level() default 0;
}

  好比上面這個代碼中,咱們定義一個名叫RoleCheck的註解(標籤),和一個名爲level配置參數(一個標籤描述信息,是數值型的),而且默認值爲0

描述註解

  可是如今這個註解並不能直接使用,由於咱們尚未用元註解去描述這個註解。

  1.這個註解在哪裏保存呢?

    @Retention,固然是RUNTIME:JVM將在運行期間保留註解,所以能夠經過反射機制讀取註解的信息。

  2.這個註解的運用場景是哪裏?也就是說它被寫在哪裏?

    @Target,咱們選擇 ElementType.METHOD ,它能夠給方法進行註解。

  3.用不用@Document?

    @Documented 表示含有該註解類型的元素(帶有註釋的)會經過javadoc或相似工具進行文檔化,Documented是一個標記註解(相似@Override 這種只須要一個簡單的聲明便可的註解即爲標記註解),沒有成員。咱們這裏用或不用均可以,由於咱們不涉及文檔化處理,可是仍是寫上了。

  最終註解將變成以下的樣子:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RoleCheck {
    int[] level() default 0;
}

 

說明:

  若是隻有一個參數成員,最好把參數名稱設爲」value」,後加小括號。註解在只有一個元素且該元素的名稱是value的狀況下,在使用註解的時候能夠省略「value=」,直接寫須要的值便可。也就是下面這樣。

public @interface RoleCheck {
    int value() default 0;
}
//用的時候,不用寫參數名稱了,直接寫值
@RoleCheck(1)
public void doSomeThing()
{
  .....  
}

使用註解

//註解沒有屬性
@RoleCheck
public void do(){.....}

//註解只有一個參數爲value的屬性
@RoleCheck(1)
public void do(){.....}

//註解有一到多個屬性
@RoleCheck(level=1,name="MS")
public void do(){.....}

註解處理器

  看完以上的內容,咱們其實只是作了一件事,就是給不一樣的物件貼上標籤,可是隻貼上標籤,若是咱們不會根據標籤進行處理的話,就毫無心義了,和註釋毫無區別

  註解處理器就是咱們的處理環節,註解處理器能夠在運行時經過反射機制讀取到標籤的信息從而進行處理

一、首先能夠經過 Class 對象的 isAnnotationPresent() 方法判斷它是否應用了某個註解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

二、而後經過 getAnnotation() 方法來獲取 Annotation 對象。

public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法。

public Annotation[] getAnnotations() {}

前一種方法返回指定類型的註解,後一種方法返回註解到這個元素上的全部註解

一個實例

  咱們作的事情是什麼,經過class對象的 isAnnotationPresent()判斷我Test類是否是有@RoleCheck註解,若是有的話就把這個註解的描述信息打印出來,這裏打印出的是默認值。

@RoleCheck
public class Test {

    public static void main(String[] args) {

        boolean hasAnnotation = Test.class.isAnnotationPresent(RoleCheck.class);

        if ( hasAnnotation ) {
            RoleCheck testAnnotation = Test.class.getAnnotation(RoleCheck.class);
            System.out.println("id:"+roleCheck.level());
        }
    }

}

  上面的例子中,只是檢閱出了註解在類上的註解,其實屬性、方法上的註解照樣是能夠的。一樣仍是基於反射。

     //返回HelloController類中全部定義的方法
        Method[] methods = HelloController.class.getDeclaredMethods();
        //遍歷每個方法
        for(Method method:methods)
        {
            //對於標有註解,且名稱爲doSomething的方法進行處理
            RoleCheck roleCheck =method.getAnnotation(RoleCheck.class);
            if(roleCheck!=null&&method.getName().equals("doSomething"))
            {
                //..........
            }
        }
 **********************************************************       
        //返回HelloController類中全部定義的字段
        Field[] fields = HelloController.class.getDeclaredFields();
        //遍歷每個字段
        for(Field field :fields)
        {
            RoleCheck roleCheck = field.getAnnotation(RoleCheck.class);
            if(roleCheck!=null&&field.getName().equals("level"))
            {
                //.......
            }
        }

註解的使用場景

  當開發者使用了Annotation 修飾了類、方法、Field 等成員以後,這些 Annotation 不會本身生效,必須由開發者提供相應的代碼來提取並處理 Annotation 信息。這些處理提取和處理 Annotation 的代碼統稱爲 APT(Annotation Processing Tool)。咱們能夠給本身答案了,註解有什麼用?給誰用?給 編譯器或者 APT 用

  好比舉一個註解使用實例,JUNIT測試框架:

public class ExampleUnitTest {
    @Test
    public void addition_isCorrect() throws Exception {
        assertEquals(4, 2 + 2);
    }
}

  @Test 標記了要進行測試的方法 addition_isCorrect().

  再舉一個註解使用實例,SpringMVC

@RequestMapping(value = "/add.do")
public String addOrder(Order order) {
        try {
            orderService.add(order);
            return "操做成功";
        } catch (Exception e) {
            e.printStackTrace();
            return "操做失敗,請重試";
        }
}

  @RequestMapping代表了要進行地址映射的方法。

相關文章
相關標籤/搜索