1. 程式人生 > >java註解詳解和自定義註解

java註解詳解和自定義註解

本文首先介紹了註解的基本概念和JDK內建的標準註解,然後介紹瞭如何自定義註解,最後給出了自定義註解的例子。

一、註解的基本概念

Java 註解就像修飾符一樣,可以用於從java程式碼中抽取文件、跟蹤程式碼中的依賴性或者在編譯時做檢查。註解可以被應用在包、類、方法、成員變數、引數和本地變數的宣告中。我們大多數人最先接觸的註解就是@Override。

註解的工作原理就是,先使用註解修飾java程式碼,然後另一塊叫做註解處理器的程式碼會解析這段註解和被修飾的程式碼並做相應的處理。

二、JDK內建的標準註解

JavaSE中內建了三個標準註解,都是定義在java.lang中,它們是:

  1. @Override:用於修飾子類的方法覆蓋了父類中的方法;
  2. @Deprecated:用於修飾已經過時了的方法,不推薦使用的方法;
  3. @SuppressWarnnings:告訴java編譯器禁止編譯警告。

2.1 @Override

@Override很簡單,只是一個標記,用於標註一個方法。它表示,被它標註的方法覆蓋了父類的方法。如果一不小心,子類的方法名寫錯了,有了@Override之後,編譯時會報錯。也就是說被@Override標註的方法如果沒有覆蓋父類的方法,編譯時報錯。

2.2 @Deprecated

@Deprecated也是一個標記註解,用於修飾一個方法。它表示此方法不推薦使用。無論是繼承、覆蓋或直接使用此方法,編譯器都會給出警告。

2.3 @SuppressWarnings

字面翻譯就是抑制警告,它用於告訴編譯器,對被標註的這句程式碼不要給出特定的警告。

@SuppressWarnings有一些引數用於表示特定的警告:

  1. deprecation:不要給出“不贊成使用的類或方法的警告”;
  2. unchecked:不要給出“型別轉換時警告”;
  3. fallthrough:不要給出”switch語句塊沒有break的警告”;
  4. path:不要給出“不存在的路徑”的警告;
  5. serial:不要給出“可序列化類缺少serialVersionUID”的警告;
  6. finally:不要給出“finally語句塊不能正常完成”的警告;
  7. all:不要給出以上所有情況的警告。

三、 自定義註解

java允許我們自己定義註解,它提供了元註解用於自定義註解。

3.1 元註解

java提供元註解的目的就是讓開發者自定義註解,元註解負責註解自定義註解。

  1. @Target;
  2. @Retention;
  3. @Documented;
  4. @Inherited。

Java5.0定義了4個元註解。接下來,分別分析這四個元註解:

3.1.1 @Target

@Target用來說明自定義註解可以用在什麼地方,其ElementType取值有:
1. CONSTRUCTOR:用於描述構造器
2. FIELD:用於描述域
3. LOCAL_VARIABLE:用於描述區域性變數
4. METHOD:用於描述方法
5. PACKAGE:用於描述包
6. PARAMETER:用於描述引數
7. TYPE:用於描述類、介面(包括註解型別) 或enum宣告

使用示例:@Target(ElementType.FIELD)

3.1.2 @Retention

@Retention用來描述自定義註解的生命週期,其RetentionPoicy取值有:
1. SOURCE:在原始檔中有效
2. CLASS:在class檔案中有效
3. RUNTIME:在執行時有效

使用示例:@Retention(RetentionPolicy.RUNTIME)

3.1.3 @Documented

@Documented用於表示自定義註解可以被javadoc之類的工具文件化,沒有成員。

使用示例:@Documented

3.1.4 @Inherited

@Inherited 元註解是一個標記註解,@Inherited闡述了某個被標註的型別是被繼承的。如果一個使用了@Inherited修飾的annotation型別被用於一個class,則這個annotation將被用於該class的子類。

@Inherited annotation型別是被標註過的class的子類所繼承。類並不從它所實現的介面繼承annotation,方法並不從它所過載的方法繼承annotation。

當@Inherited annotation型別標註的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation型別的annotation時,反射程式碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation型別被發現,或者到達類繼承結構的頂層。

使用示例:@Inherited

3.2 開始自定義註解

定義註解格式:

  public @interface 註解名 {定義體}

註解引數的可支援資料型別:

  1. 所有基本資料型別(int,float,boolean,byte,double,char,long,short)
  2. String型別
  3. Class型別
  4. enum型別
  5. Annotation型別
  6. 以上所有型別的陣列

使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation介面,由編譯程式自動完成其他細節。在定義註解時,不能繼承其他的註解或介面。

@interface用來宣告一個註解,其中的每一個方法實際上是聲明瞭一個配置引數。方法的名稱就是引數的名稱,返回值型別就是引數的型別(返回值型別只能是基本型別、Class、String、enum)。可以通過default來宣告引數的預設值。

注意:

  • 只能用public或預設(default)這兩個訪問權修飾.例如,String value();這裡把方法設為defaul預設型別; 

  • 引數成員只能用基本型別byte,short,char,int,long,float,double,boolean八種基本資料型別和 String,Enum,Class,annotations等資料型別,以及這一些型別的陣列.例如,String value();這裡的引數成員就為String;  

  • 如果只有一個引數成員,最好把引數名稱設為”value”,後加小括號.

3.3 例子

定義一個註解@Persion:

package com.zcx.annotation;

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Persion {
    String name() default "zcx";
    int age() default 18;

    String[] hobby() default {"basketball", "football"};
}

再定義一個註解@StudentGender:

package com.zcx.annotation;

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

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface StudentGender {

    public enum Gender{BOY, GIRL};
    Gender gender() default Gender.BOY;

}

定義一個類,使用剛才定義的兩個註解:

package com.zcx.annotation;

import com.zcx.annotation.StudentGender.Gender;

@Persion(name="haha", age=97, hobby={"test1", "test2"})
public class Student {


    @StudentGender(gender=Gender.BOY)
    private String stuGender;


    public String getStuGender() {
        return stuGender;
    }
    public void setStuGender(String stuGender) {
        this.stuGender = stuGender;
    }
}

定義一個註解處理器,用於處理註解內容:

package com.zcx.annotation;

import java.lang.reflect.Field;


public class AnnotationProcessor {
    public static void getStudentInfo(Class<?> clazz){
        if(clazz.isAnnotationPresent(Persion.class)){
            Persion annotation = (Persion)clazz.getAnnotation(Persion.class);
            System.out.println(annotation);
            System.out.println(annotation.age());
            System.out.println(annotation.name());
            String[] hobby = annotation.hobby();
            for(String str : hobby){
                System.out.println(str);
            }
        }
        Field[] fields = clazz.getDeclaredFields();

        for(Field field :fields){
            System.out.println("fieldName=" + field.toString());
            if(field.isAnnotationPresent(StudentGender.class)){
                StudentGender annotation = (StudentGender)field.getAnnotation(StudentGender.class);
                System.out.println(annotation);
                System.out.println(annotation.gender());
            }
        }
    }
}

最後,定義測試類:

package com.zcx.annotation;

public class Test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        AnnotationProcessor.getStudentInfo(Student.class);
    }
}