Java註解的使用

chenweicool發表於2020-11-11

java中註解的使用

1.說再前面

使用註解開發的好處

1.使程式碼更加乾淨易讀,易於維護修改。比如,以前使用spring的開發,都是基於xml檔案實現了統一的配置管理,但是缺點也是顯而易見的,就是隨著專案的越來越大,xml檔案會變得越來越複雜,維護成本也會越來越高。使用註解就可以提供更大的便捷性,易於維護修改。

2 可以實現程式碼的型別檢查,特別是在編譯器的角度實現一些型別檢查,比如預檢查(@Override)和抑制警告(@SuppressWarnings)等。

3 自定義註解,作為額外資訊的載體,儲存有關程式的額外資訊

2 註解的分類以及使用

​ Java註解是附加在程式碼中的一些元資訊,用於編譯和執行時進行解析和使用,起到說明、配置的功能。

註解不會影響程式碼的實際邏輯,僅僅起到輔助性的作用。包含在java.lang.annotation包中。註解的定義類似於介面的定義,使用@interface來定義,定義一個方法即為註解型別定義了一個元素,方法的宣告不允許有引數或throw語句,返回值型別被限定為原始資料型別、字串String、Class、enums、註解型別,或前面這些的陣列,方法可以有預設值。註解並不直接影響程式碼的語義,但是他可以被看做是程式的工具或者類庫。它會反過來對正在執行的程式語義有所影響。註解可以從原始檔、class檔案或者在執行時通過反射機制多種方式被讀取。

一般來說,註解一般分為三種型別: 元註解,標準註解,自定義註解

2.1 元註解

元註解是專職負責註解其他註解,主要是標明該註解的使用範圍,生效範圍。我們是不能改變它的,只能用它來定義我們自定義的註解。

包括 @Retention @Target @Document @Inherited四種。(java.lang.annotation中提供,為註釋型別)。

註解 說明
@Target 定義註解的作用目標,也就是可以定義註解具體作用在類上,方法上,還是變數上
@Retention 定義註解的保留策略。RetentionPolicy.SOURCE:註解僅存在於原始碼中,在class位元組碼檔案中不包含;RetentionPolicy.CLASS:預設的保留策略,註解會在class位元組碼檔案中存在,但執行時無法獲得;RetentionPolicy.RUNTIME:註解會在class位元組碼檔案中存在,在執行時可以通過反射獲取到。
@Document 說明該註解將被包含在javadoc中
@Inherited 說明子類可以繼承父類中的該註解

Target型別主要依賴於ElementType這個型別,具體的型別如下:

Target型別 說明
ElementType.TYPE 介面、類、列舉、註解
ElementType.FIELD 欄位、列舉的常量
ElementType.METHOD 方法
ElementType.PARAMETER 方法引數
ElementType.CONSTRUCTOR 建構函式
ElementType.LOCAL_VARIABLE 區域性變數
ElementType.ANNOTATION_TYPE 註解
ElementType.PACKAGE

2.2 標準註解

Java標準註解提供了三個,定義在java.lang中的註解,我認為這三個註解的作用更多的是一種註釋

  • @Override 表示當前方法覆蓋父類中的方法。

  • @Deprecated 標記一個元素為已過期,避免使用

支援的元素型別為:CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE

  • @SuppressWarnings 不輸出對應的編譯警告

    一個簡單的使用demo:

    @SuppressWarnings(value = {"unused", "rawtypes"})
    public class Children  extends Parent{
        @Override
        public void work() {
            System.out.println("我是一個被重寫的方法");
        }
    
        @Deprecated
        public void play(){
            System.out.println("這個方法不推薦使用了");
        }
    }
    

2.3 自定義註解實現一個sql語句的拼接

需要注意的方面:註解的定義類似於介面的定義,使用@interface來定義,定義一個方法即為註解型別定義了一個元素,方法的宣告不允許有引數或throw語句,返回值型別被限定為原始資料型別、字串String、Class、enums、註解型別,或前面這些的陣列,方法可以有預設值。

自定義註解一般可以分為三步: 定義註解,使用註解,讀取註解

定義註解

@Target(ElementType.TYPE) //註解載入類上
@Retention(RetentionPolicy.RUNTIME) // 執行時讀取註解

public @interface Table {
    String value(); 
}


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interface UserFiled {
    String name();
    String type();
    int length();
}

使用註解

// 將自定義的註解加在使用者上,實現一個表的對映
@Table(value = "user_table")
public class User {

    @UserFiled(name = "user_id",type = "int",length = 8)
    private int userId;

    @UserFiled(name = "user_name",type = "varchar",length = 16)
    private String userName;

    @UserFiled(name = "password",type = "varchar",length = 16)
    private String password;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

讀取註解的內容

/**
 * 讀取註解中的值
 */
public class GetUser {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class userClass = Class.forName("annocation.blog.User");

        // 讀取類上的註解
        Table table = (Table) userClass.getAnnotation(Table.class);
        System.out.println(table.value());

        // 讀取屬性上註解
        Field userId = userClass.getDeclaredField("userId");
        UserFiled userFiledId = userId.getAnnotation(UserFiled.class);
        System.out.println(userFiledId.length() + "----" + userFiledId.type() + "-----" + userFiledId.name());

        Field userName = userClass.getDeclaredField("userName");
        UserFiled userFiledName = userName.getAnnotation(UserFiled.class);
        System.out.println(userFiledName.length()+"----"+userFiledName.type()+"----"+userFiledName.name());

        Field password = userClass.getDeclaredField("password");
        UserFiled userFiledPassword = password.getAnnotation(UserFiled.class);
        System.out.println(userFiledPassword.name() + "-----" + userFiledPassword.type() + "-----" + userFiledPassword.length());

        // 拼接一個sql語句
        String name = "chenwei";
        String sql ="select * from" + table.value()+"where"+userFiledName.name()+"="+name;
    }
}

結果:

user_table
user_id----int-----8
user_name----varchar----16
password-----varchar-----16

自定義註解的實現過程:

1,定義註解

2,使用註解,根據自己定義的註解來達到一些目的,本例中,就是使用註解來完成資料庫表和實體類的對映關係

3 讀取註解的內容,也是比較重要的一部分,核心還是利用了反射的思想,得到使用註解的這個類,根據類中的getAnnotion的方法得到定義的註解,獲得註解上的值。

3 註解的實現的原理

註解的實現的原理很大的一部分是基於反射實現。

​ 反射可以獲取到Class物件,進而獲取到Constructor、Field、Method等例項,點開原始碼結構發現Class、Constructor、Field、Method等均實現了AnnotatedElement介面,AnnotatedElement介面的方法如下

// 判斷該元素是否包含指定註解,包含則返回true
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
    
// 返回該元素上對應的註解,如果沒有返回null
<T extends Annotation> T getAnnotation(Class<T> annotationClass);

// 返回該元素上的所有註解,如果沒有任何註解則返回一個空陣列
Annotation[] getAnnotations();

// 返回指定型別的註解,如果沒有返回空陣列
T[] getAnnotationsByType(Class<T> annotationClass)
    
// 返回指定型別的註解,如果沒有返回空陣列,只包含直接標註的註解,不包含inherited的註解
T getDeclaredAnnotation(Class<T> annotationClass)
    
// 返回指定型別的註解,如果沒有返回空陣列,只包含直接標註的註解,不包含inherited的註解
T[] getDeclaredAnnotationsByType
    
// 返回該元素上的所有註解,如果沒有任何註解則返回一個空陣列,只包含直接標註的註解,不包含inherited的註解
Annotation[] getDeclaredAnnotations();

通過一個例項再次說明一下註解的使用過程:

定義註解

@Documented
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotaion {
    String getValue() default "this is myAnntaion";
    int order() default 0;
}

使用註解

@MyAnnotaion(getValue = "annotation on class")
public class Demo {

    @MyAnnotaion(getValue = "annotation on filed")
    public String name;

    @MyAnnotaion(getValue = "annotation on method")
    public void hello(){
    }

    @MyAnnotaion
    public void defaultMethod(){

    }
}

利用反射讀取註解中的值。

public class TestDemo {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
        /**
         * 獲取類上的註解
         */
        Class<Demo> demoClass = Demo.class;
        Annotation[] annotaion = demoClass.getAnnotations();
        printAnnotation(annotaion);

        /**
         * 讀取成員變數上的註解
         */
        Field name = demoClass.getField("name");
        Annotation[] getOnFiled = name.getAnnotations();
        printAnnotation(getOnFiled);

        /**
         * 讀取方法上的註解
         */
        Method hello = demoClass.getMethod("hello", null);
        MyAnnotaion onMethod = hello.getAnnotation(MyAnnotaion.class);
        System.out.println(onMethod.getValue());

        /**
         * 獲取預設方法上的註解
         */
        Method defaultMethod = demoClass.getMethod("defaultMethod", null);
        MyAnnotaion onDefaultMethod = defaultMethod.getAnnotation(MyAnnotaion.class);
        System.out.println(onDefaultMethod.getValue());

    }

    public static void printAnnotation(Annotation... annotations) {
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
    }
}

執行結果

@annocation.MyAnnotaion(getValue=annotation on class, order=0)
@annocation.MyAnnotaion(getValue=annotation on filed, order=0)
annotation on method
this is myAnntaion

參考資料:

http://blog.kimzing.com/java/Java註解入門到精通-學這一篇就夠了/

《java程式設計思想》

相關文章