1. 程式人生 > >Java註解的自定義和使用

Java註解的自定義和使用

小夥伴們。今天我們來說說註解、標誌@ 。針對java不同版本來說,註解的出現是在jdk1.5 但是在jdk1.5版本使用註解必須繼續類的方法的重寫,不能用於實現的介面中的方法實現,在jdk1.6環境下對於繼續和實現都是用。

jdk1.5版本內建了三種標準的註解:

@Override,表示當前的方法定義將覆蓋超類中的方法。

@Deprecated,使用了註解為它的元素編譯器將發出警告,因為註解@Deprecated是不贊成使用的程式碼,被棄用的程式碼。

@SuppressWarnings,關閉不當編輯器警告資訊。

Java還提供了4中註解,專門負責新註解的建立:

@Target:

表示該註解可以用於什麼地方,可能的ElementType引數有:CONSTRUCTOR

:構造器的宣告FIELD:域宣告(包括enum例項)LOCAL_VARIABLE:區域性變數宣告METHOD:方法宣告PACKAGE:包宣告PARAMETER:引數宣告TYPE:類、介面(包括註解型別)或enum宣告

@Retention

表示需要在什麼級別儲存該註解資訊。可選的RetentionPolicy引數包括:SOURCE:註解將被編譯器丟棄CLASS:註解在class檔案中可用,但會被VM丟棄RUNTIME:VM將在執行期間保留註解,因此可以通過反射機制讀取註解的資訊

@Document

將註解包含在Javadoc中

@Inherited

允許子類繼承父類中的註解

 下面我們自己來新建一個註解。在我們開發中。經常會使用自己設定的註解

package com.java.api;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**定義註解
 * @Target:

表示該註解可以用於什麼地方,可能的ElementType引數有:
CONSTRUCTOR:構造器的宣告
FIELD:域宣告(包括enum例項)
LOCAL_VARIABLE:區域性變數宣告
METHOD:方法宣告
PACKAGE:包宣告
PARAMETER:引數宣告
TYPE:類、介面(包括註解型別)或enum宣告
@Retention

表示需要在什麼級別儲存該註解資訊。可選的RetentionPolicy引數包括:
SOURCE:註解將被編譯器丟棄
CLASS:註解在class檔案中可用,但會被VM丟棄
RUNTIME:VM將在執行期間保留註解,因此可以通過反射機制讀取註解的資訊
 * 
 * */
public class UseCase{
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCases{
    public String id();
    public String description() default "no description";
}
}

然後我那使用註解

package com.java.api;

import com.java.api.UseCase.UseCases;

/**
 * 使用註解:
 * 
 * */
public class PasswordUtils {
    @UseCases(id="47",description="Passwords must contain at least one numeric")
     public boolean validatePassword(String password) {
         return (password.matches("\\w*\\d\\w*"));
     }

     @UseCases(id ="48")
     public String encryptPassword(String password) {
         return new StringBuilder(password).reverse().toString();
     }
}

最後我們來測試我們寫的註解:

package com.java.api;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.java.api.UseCase.UseCases;

/**
 *解析註解:
 * 
 * */
public class UserCaseTest {
public static void main(String[] args) {
     List<Integer> useCases = new ArrayList<Integer>();
     Collections.addAll(useCases, 47, 48, 49, 50);
     trackUseCases(useCases, PasswordUtils.class);
}
public static void trackUseCases(List<Integer> useCases, Class<?> cl) {
    for (Method m : cl.getDeclaredMethods()) {
        //獲得註解的物件
        UseCases uc = m.getAnnotation(UseCases.class);
        if (uc != null) {
            System.out.println("Found Use Case:" + uc.id() + " "
                        + uc.description());
            useCases.remove(new Integer(uc.id()));
        }
    }
    for (int i : useCases) {
        System.out.println("Warning: Missing use case-" + i);
    }
}
}

總結:

java註解是附加在程式碼中的一些元資訊,用於一些工具在編譯、執行時進行解析和使用,起到說明、配置的功能。 註解不會也不能影響程式碼的實際邏輯,僅僅起到輔助性的作用。包含在 java.lang.annotation 包中

java自定義註解和執行時靠反射獲取註解。

1、Annotation的工作原理:
JDK5.0中提供了註解的功能,允許開發者定義和使用自己的註解型別。該功能由一個定義註解型別的語法和描述一個註解宣告的語法,讀取註解的API,一個使用註解修飾的class檔案和一個註解處理工具組成。

Annotation並不直接影響程式碼的語義,但是他可以被看做是程式的工具或者類庫。它會反過來對正在執行的程式語義有所影響。

Annotation可以衝原始檔、class檔案或者在執行時通過反射機制多種方式被讀取。

2、@Override註解:
java.lang
註釋型別 Override
@Target(value=METHOD)
@Retention(value=SOURCE)
public @interface Override
表示一個方法宣告打算重寫超類中的另一個方法宣告。如果方法利用此註釋型別進行註解但沒有重寫超類方法,則編譯器會生成一條錯誤訊息。

@Override註解表示子類要重寫父類的對應方法。

Override是一個Marker annotation,用於標識的Annotation,Annotation名稱本身表示了要給工具程式的資訊。

下面是一個使用@Override註解的例子:

class A {
    private String id;
    A(String id){
        this.id = id;
    }
    @Override
    public String toString() {
        return id;
    }
}
3、@Deprecated註解:
java.lang
註釋型別 Deprecated
@Documented
@Retention(value=RUNTIME)
public @interface Deprecated
用 @Deprecated 註釋的程式元素,不鼓勵程式設計師使用這樣的元素,通常是因為它很危險或存在更好的選擇。在使用不被贊成的程式元素或在不被贊成的程式碼中執行重寫時,編譯器會發出警告。

@Deprecated註解表示方法是不被建議使用的。

Deprecated是一個Marker annotation。

下面是一個使用@Deprecated註解的例子:

class A {
    private String id;
    A(String id){
        this.id = id;
    }
    @Deprecated
    public void execute(){
        System.out.println(id);
    }
    public static void main(String[] args) {
        A a = new A("a123");
        a.execute();
    }
}
4、@SuppressWarnings註解:
java.lang
註釋型別 SuppressWarnings
@Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE})
@Retention(value=SOURCE)
public @interface SuppressWarnings
指示應該在註釋元素(以及包含在該註釋元素中的所有程式元素)中取消顯示指定的編譯器警告。注意,在給定元素中取消顯示的警告集是所有包含元素中取消顯示的警告的超集。例如,如果註釋一個類來取消顯示某個警告,同時註釋一個方法來取消顯示另一個警告,那麼將在此方法中同時取消顯示這兩個警告。

根據風格不同,程式設計師應該始終在最裡層的巢狀元素上使用此註釋,在那裡使用才有效。如果要在特定的方法中取消顯示某個警告,則應該註釋該方法而不是註釋它的類。

@SuppressWarnings註解表示抑制警告。

下面是一個使用@SuppressWarnings註解的例子:

@SuppressWarnings("unchecked")
public static void main(String[] args) {
    List list = new ArrayList();
    list.add("abc");
}
5、自定義註解:
使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation介面,由編譯程式自動完成其他細節。在定義註解時,不能繼承其他的註解或介面。

自定義最簡單的註解:

public @interface MyAnnotation {

}
使用自定義註解:

public class AnnotationTest2 {

    @MyAnnotation
    public void execute(){
        System.out.println("method");
    }
}
5.1、新增變數:
public @interface MyAnnotation {

    String value1();
}
使用自定義註解:

public class AnnotationTest2 {

    @MyAnnotation(value1="abc")
    public void execute(){
        System.out.println("method");
    }
}
當註解中使用的屬性名為value時,對其賦值時可以不指定屬性的名稱而直接寫上屬性值介面;除了value意外的變數名都需要使用name=value的方式賦值。

5.2、新增預設值:
public @interface MyAnnotation {

    String value1() default "abc";
}
5.3、多變數使用列舉:
public @interface MyAnnotation {

    String value1() default "abc";
    MyEnum value2() default MyEnum.Sunny;
}
enum MyEnum{
    Sunny,Rainy
}
使用自定義註解:

public class AnnotationTest2 {

    @MyAnnotation(value1="a", value2=MyEnum.Sunny)
    public void execute(){
        System.out.println("method");
    }
}
5.4、陣列變數:
public @interface MyAnnotation {

    String[] value1() default "abc";
}
使用自定義註解:

public class AnnotationTest2 {

    @MyAnnotation(value1={"a","b"})
    public void execute(){
        System.out.println("method");
    }
}
6、設定註解的作用範圍:
@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Retention
指示註釋型別的註釋要保留多久。如果註釋型別宣告中不存在 Retention 註釋,則保留策略預設為 RetentionPolicy.CLASS。

只有元註釋型別直接用於註釋時,Target 元註釋才有效。如果元註釋型別用作另一種註釋型別的成員,則無效。

public enum RetentionPolicy
extends Enum<RetentionPolicy>
註釋保留策略。此列舉型別的常量描述保留註釋的不同策略。它們與 Retention 元註釋型別一起使用,以指定保留多長的註釋。

CLASS
編譯器將把註釋記錄在類檔案中,但在執行時 VM 不需要保留註釋。
RUNTIME
編譯器將把註釋記錄在類檔案中,在執行時 VM 將保留註釋,因此可以反射性地讀取。
SOURCE
編譯器要丟棄的註釋。
@Retention註解可以在定義註解時為編譯程式提供註解的保留策略。

屬於CLASS保留策略的註解有@SuppressWarnings,該註解資訊不會儲存於.class檔案。

6.1、在自定義註解中的使用例子:
@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation {

    String[] value1() default "abc";
}
7、使用反射讀取RUNTIME保留策略的Annotation資訊的例子:
java.lang.reflect
        介面 AnnotatedElement
所有已知實現類:
        AccessibleObject, Class, Constructor, Field, Method, Package
表示目前正在此 VM 中執行的程式的一個已註釋元素。該介面允許反射性地讀取註釋。由此介面中的方法返回的所有註釋都是不可變並且可序列化的。呼叫者可以修改已賦值陣列列舉成員的訪問器返回的陣列;這不會對其他呼叫者返回的陣列產生任何影響。

如果此介面中的方法返回的註釋(直接或間接地)包含一個已賦值的 Class 成員,該成員引用了一個在此 VM 中不可訪問的類,則試圖通過在返回的註釋上呼叫相關的類返回的方法來讀取該類,將導致一個 TypeNotPresentException。

isAnnotationPresent
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
如果指定型別的註釋存在於此元素上,則返回 true,否則返回 false。此方法主要是為了便於訪問標記註釋而設計的。

引數:

annotationClass - 對應於註釋型別的 Class 物件

返回:

如果指定註釋型別的註釋存在於此物件上,則返回 true,否則返回 false

丟擲:

NullPointerException - 如果給定的註釋類為 null

從以下版本開始:

1.5

getAnnotation
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
如果存在該元素的指定型別的註釋,則返回這些註釋,否則返回 null。

引數:

annotationClass - 對應於註釋型別的 Class 物件

返回:

如果該元素的指定註釋型別的註釋存在於此物件上,則返回這些註釋,否則返回 null

丟擲:

NullPointerException - 如果給定的註釋類為 null

從以下版本開始:

1.5

getAnnotations
Annotation[] getAnnotations()
返回此元素上存在的所有註釋。(如果此元素沒有註釋,則返回長度為零的陣列。)該方法的呼叫者可以隨意修改返回的陣列;這不會對其他呼叫者返回的陣列產生任何影響。

返回:

此元素上存在的所有註釋

從以下版本開始:

1.5

getDeclaredAnnotations
Annotation[] getDeclaredAnnotations()
返回直接存在於此元素上的所有註釋。與此介面中的其他方法不同,該方法將忽略繼承的註釋。(如果沒有註釋直接存在於此元素上,則返回長度為零的一個數組。)該方法的呼叫者可以隨意修改返回的陣列;這不會對其他呼叫者返回的陣列產生任何影響。

返回:

直接存在於此元素上的所有註釋

從以下版本開始:

1.5


下面是使用反射讀取RUNTIME保留策略的Annotation資訊的例子:

自定義註解:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    String[] value1() default "abc";
}
使用自定義註解:

public class AnnotationTest2 {

    @MyAnnotation(value1={"a","b"})
    @Deprecated
    public void execute(){
        System.out.println("method");
    }
}
讀取註解中的資訊:

public static void main(String[] args) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
    AnnotationTest2 annotationTest2 = new AnnotationTest2();
    //獲取AnnotationTest2的Class例項
    Class<AnnotationTest2> c = AnnotationTest2.class;
    //獲取需要處理的方法Method例項
    Method method = c.getMethod("execute", new Class[]{});
    //判斷該方法是否包含MyAnnotation註解
    if(method.isAnnotationPresent(MyAnnotation.class)){
        //獲取該方法的MyAnnotation註解例項
        MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
        //執行該方法
        method.invoke(annotationTest2, new Object[]{});
        //獲取myAnnotation
        String[] value1 = myAnnotation.value1();
        System.out.println(value1[0]);
    }
    //獲取方法上的所有註解
    Annotation[] annotations = method.getAnnotations();
    for(Annotation annotation : annotations){
        System.out.println(annotation);
    }
}
8、限定註解的使用:
限定註解使用@Target。

@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Target
指示註釋型別所適用的程式元素的種類。如果註釋型別宣告中不存在 Target 元註釋,則宣告的型別可以用在任一程式元素上。如果存在這樣的元註釋,則編譯器強制實施指定的使用限制。 例如,此元註釋指示該宣告型別是其自身,即元註釋型別。它只能用在註釋型別宣告上:

@Target(ElementType.ANNOTATION_TYPE)
    public @interface MetaAnnotationType {
        ...
    }
此元註釋指示該宣告型別只可作為複雜註釋型別宣告中的成員型別使用。它不能直接用於註釋:

@Target({}) 
    public @interface MemberType {
        ...
    }
這是一個編譯時錯誤,它表明一個 ElementType 常量在 Target 註釋中出現了不只一次。例如,以下元註釋是非法的:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
    public @interface Bogus {
        ...
    }
public enum ElementType
extends Enum<ElementType>
程式元素型別。此列舉型別的常量提供了 Java 程式中宣告的元素的簡單分類。

這些常量與 Target 元註釋型別一起使用,以指定在什麼情況下使用註釋型別是合法的。

ANNOTATION_TYPE
註釋型別宣告
CONSTRUCTOR
構造方法宣告
FIELD
欄位宣告(包括列舉常量)
LOCAL_VARIABLE
區域性變數宣告
METHOD
方法宣告
PACKAGE
包宣告
PARAMETER
引數宣告
TYPE
類、介面(包括註釋型別)或列舉宣告

註解的使用限定的例子:

@Target(ElementType.METHOD)
public @interface MyAnnotation {

    String[] value1() default "abc";
}
9、在幫助文件中加入註解:
要想在製作JavaDoc檔案的同時將註解資訊加入到API檔案中,可以使用java.lang.annotation.Documented。

在自定義註解中宣告構建註解文件:

@Documented
public @interface MyAnnotation {

    String[] value1() default "abc";
}
使用自定義註解:

public class AnnotationTest2 {

    @MyAnnotation(value1={"a","b"})
    public void execute(){
        System.out.println("method");
    }
}
10、在註解中使用繼承:
預設情況下註解並不會被繼承到子類中,可以在自定義註解時加上java.lang.annotation.Inherited註解宣告使用繼承。

@Documented
@Retention(value=RUNTIME)
@Target(value=ANNOTATION_TYPE)
public @interface Inherited
指示註釋型別被自動繼承。如果在註釋型別宣告中存在 Inherited 元註釋,並且使用者在某一類宣告中查詢該註釋型別,同時該類宣告中沒有此型別的註釋,則將在該類的超類中自動查詢該註釋型別。此過程會重複進行,直到找到此型別的註釋或到達了該類層次結構的頂層 (Object) 為止。如果沒有超類具有該型別的註釋,則查詢將指示當前類沒有這樣的註釋。

注意,如果使用註釋型別註釋類以外的任何事物,此元註釋型別都是無效的。還要注意,此元註釋僅促成從超類繼承註釋;對已實現介面的註釋無效。