Java(九)註解(Annotation)

一、什麼是註解

注意!是注“解”,不是註釋,註釋是在寫代碼時加上一些對代碼的解釋,它們兩都起到了解釋代碼的作用

1.概念

從JDK5開始,Java增加對元數據的支持,也就是註解,註解與註釋是有一定區別的,可以把註解理解爲代碼裏的特殊標記,這些標記可以在編譯,類加載,運行時被讀取,並執行相應的處理。通過註解開發人員可以在不改變原有代碼和邏輯的情況下在源代碼中嵌入補充信息。
——百度百科

在Java中的註解是以@開頭的,它會在編譯,類加載,運行時被讀取,然後由註解處理器來進行處理,註解僅僅是存放元數據,不會修改修飾對象的代碼產生直接影響
註解和類、接口、數組、枚舉等關鍵字平起平坐,同樣重要

2.來源

在註解還沒有出現之前,一般使用XML文件來存儲元數據的,配置文件的信息還有SQL語句都可以算是元數據,這就使元數據和代碼耦合度十分的低,如果項目十分大的話,就會造成維護困難,於是就誕生了註解,讓元數據和代碼緊密結合,極大的降低了編程的複雜度,在後來,註解功能就不僅僅侷限與綁定元數據了
註解是真的好用,誰用誰知道

3.註解分類

  1. JDK自帶的註解
  2. 來自第三方框架的註解
  3. 自定義註解

二、自定義註解

在上面三類註解中,我們主要關注的是自定義註解

1.元註解

元註解在自定義註解中需要用到,總共有4個,分別是@Target、@Retention、@Inherited、@Documented

@Target

這個註解是表示自定義註解的作用域,也就是說這個註解表明了定義的註解可以用來修飾什麼類型的類、屬性、方法

下面是 @Target的取值表 ,也不需要記,用的時候查看一下就好了

取值 註解使用範圍
METHOD 可用於方法上
TYPE 可用於類或者接口上
ANNOTATION_TYPE 可用於註解類型上(被@interface修飾的類型)
CONSTRUCTOR 可用於構造方法上
FIELD 可用於域上,就是類的屬性/字段
LOCAL_VARIABLE 可用於局部變量上,就是方法內部的屬性
PACKAGE 用於記錄java文件的package信息
PARAMETER 可用於參數上

@Resource中的@Target的value

@Target({TYPE, FIELD, METHOD})

@Retention

這個註解表示的是自定義註解的生命週期,也就是說這個註解能夠決定自定義註解能夠在那個階段被處理,在那些階段可以存在

下面是**@Retention的取值表**,同上

取值 生命週期
RetentionPolicy.SOURCE 只能存活在.java文件上,不會存活在.class文件,在經過編譯器編譯之後就不存在了
RetentionPolicy.CLASS 能夠存活在.class文件,不會存活在運行時期,也就是在內存中,但是在經過類加載器之後就不存在了
RetentionPolicy.RUNTIME 能夠存活在內存中,無論經過編譯器還是類加載器都會存在,也是我們自定義註解時常用的

@Resource中的@Retention的value

@Retention(RUNTIME)

@Inherited

這個註解能夠讓子類繼承父類上的註解,打個比方說,A繼承了B,B上有這個註解,也有其他註解,那麼A類就會繼承B類上的其他註解。但是子接口的繼承和實現都不會父接口的註解
這個註解沒有value,直接使用就好了

@Documented

這個註解表示,在生成javadoc時,帶了該註解的類是否要包含該自定義註解的信息,我們用不上。生成javadoc文檔的方式有很多,用IDEA、javadoc.exe命令行

2.自定義註解語法

  1. 使用@interface關鍵字定義,跟使用class、interface、enum等關鍵字一樣
  2. 定義註解類型元素(annotation type element)
    定義註解類型元素纔是我們的重點,第一,元素的訪問修飾符必須爲public;第二,元素的類型只能爲基本數據類型(String、int、Class等)、還有枚舉類型;第三、如果只有一個元素,則把元素名稱取爲value;第四,在元素名稱後面加**();第五,在()後面可以加default,後面接默認值**

拉個@Retention註解來作例子

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

3.自定義註解

場景:類似於數據庫的表格
Table

/**
 * @author xxj
 * 表註解
 */
@Target({TYPE})
@Retention(RUNTIME)
public @interface Table {
    String value();
}

Column

/**
 * @author xxj
 * 行註解
 */
@Target(LOCAL_VARIABLE)
@Retention(RUNTIME)
public @interface Column {
    String value();
}

4.使用自定義註解

定義了註解,當然也要使用註解啦,這裏涉及但反射機制,如果還不瞭解的可以去看我的【java】反射機制

首先,先定義一個User類

/**
 * @author xxj
 * User表
 */
@Table("User")
public class User implements Serializable {
    @Column("name")
    private String name;
    @Column("age")
    private Integer age;
    public User() {
    }
    //get、set、toString就不放上來了
}

然後,寫一個Dome測試類
最重要的就是explainAnnotation()方法了,瞭解過反射的可以看一下

/**
 * @author xxj
 * 註解解析測試
 */
public class Demo {
    public static void main(String[] args) {
        User user=new User();
        user.setName("xiaoming");
        user.setAge(18);
        User user1=new User();
        user1.setName("xiaohong");
        //調用方法解析註解
        explainAnnotation(user);
        explainAnnotation(user1);
    }
    private static void explainAnnotation(Object object){
        //定義一個String來存放信息
        String str="";
        Class obClass=object.getClass();
        //判斷是否是Table註解
        if (!obClass.isAnnotationPresent(Table.class)){
            return;
        }
        //獲取到table註解信息
        Table table= (Table) obClass.getAnnotation(Table.class);
        //獲取table註解的value
        String tableName=table.value();
        str+="tableName="+tableName;
        //獲取object類的屬性/字段
        Field[] fields=obClass.getDeclaredFields();
        //循環解析屬性
        for (Field field:fields) {
            //判斷是否是Column註解
            if (!field.isAnnotationPresent(Column.class)){
                continue;
            }
            //獲取column註解信息
            Column column= field.getAnnotation(Column.class);
            //獲取column註解的value
            String columnName=column.value();
            //拼接對應屬性的getXxx()方法名稱
            String getMethodName="get"+columnName.substring(0,1).toUpperCase()
                    +columnName.substring(1);
            //定義一個object對象來存放值
            Object fieldValue = null;
            try {
                //獲取對應屬性的getXxx()方法
                Method method=obClass.getMethod(getMethodName);
                //調用方法
                fieldValue=method.invoke(object);
            }catch (Exception e){}
            //拼接字符串
            str+=","+columnName+":"+fieldValue;
        }
        System.out.println(str);
    }
}

可以看到,註解的內容可以直接通過class類獲得,但是對象的屬性值只能通過對象獲得,所以還是要使用method.invoke(object),將對象傳遞過去才能獲得對象的屬性值

三、總結

從上面的自定義註解和解析自定義註解中,我們可以看出創建了一個自定義的註解不是直接使用它就有效果的,而是要另外寫一個方法來解析這個註解,然後根據解析出來的註解內容在做相應的操作。
像MyBatis中提供的@mapper,就是用來判斷這個類是否是有@mapper註解,而寫在@select上的value,就可以直接拿來拼接SQL語句了。

——————————————————————————————
如果本文章內容有問題,請直接評論或者私信我。如果覺得寫的還不錯的話,點個贊也是對我的支持哦
未經允許,不得轉載!

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