註解

從JDK5開始,Java增長了對元數據的支持,也就是註解(Annotation),註解就是在代碼裏的特殊標記,這些標記能夠在編譯、類加載、運行時被讀取,並執行相應的處理。經過註解,程序開發人員能夠在不改變原有邏輯的狀況下,在源代碼中嵌入一些補充信息。java

5個基本註解

  • @Override
  • @Depricated:該註解的方法說明已過期,不建議在使用。
  • @SafeVarargs:
  • @FunctionalInterface:Java1.8新增的註解,代表該接口是函數式接口,能夠用Lambda表達式。
  • @SuppressWarnings:

上面@SafeVarargs註解是最難理解的了,用的也少,下次遇到,再作詳細說明。mysql

自定義註解

簡要說明

註解也是一種特殊的Java類,其定義方式以下:sql

public @interface A{
    String name();
}

即以接口的形式定義註解,以抽象方法定義註解的屬性,方法返回值的類型就註解是屬性值的類型。數據庫

而後咱們就能夠這樣使用本身定義的註解:編程

@A(name="sqmax")
class B{

    @A(name="sqmax")
    String a;

    @A(name="sqmax")
    void doSth() {

    }
}

仿照註解源碼,些本身的註解

下面是咱們常用的@Override和註解的源代碼:數組

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

咱們也能夠本身定義註解。
好比咱們能夠自定義一個數據庫配置類註解。jsp

/**
 * Created by SqMax on 2018/5/15.
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface JDBCConfig {
    String ip();
    int port() default 3306;
    String database();
    String encoding();
    String userName();
    String passWord();
}

而後咱們可使用這個註解,以註解的方式獲取數據庫鏈接信息:ide

@JDBCConfig(ip="127.0.0.1",database = "dbgirl",encoding = "utf-8", userName = "root",passWord = "123456")
public class DBUtil {
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException{
        JDBCConfig config=DBUtil.class.getAnnotation(JDBCConfig.class);
        String ip=config.ip();
        int port=config.port();
        String database=config.database();
        String encoding=config.encoding();
        String userName=config.userName();
        String passWord=config.passWord();

        String url=String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s",ip,port,database,encoding);
        System.out.println(url);
        return DriverManager.getConnection(url,userName,passWord);
    }

    public static void main(String[] args) throws SQLException{
        Connection connection=getConnection();
        System.out.println(connection.isClosed());
    }
}

運行結果以下:函數

jdbc:mysql://127.0.0.1:3306/dbgirl?characterEncoding=utf-8
false

能夠看到已成功鏈接到數據庫。工具

元註解

從新看一下上面咱們自定義的@JDBCConfig註解:

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface JDBCConfig {
    String ip();
    int port() default 3306;
    String database();
    String encoding();
    String userName();
    String passWord();
}

它的上面還有一些註解:@Target,@Retention,@Documented。
這些就是元註解。元註解就是能夠用來註解自定義註解的註解。
主要有這麼幾種:

  • @Target
  • @Retention
  • @Inherited
  • @Document
  • @Repeatable(java1.8新增)

下面一一講解:

@Target

@Target 表示這個註解能放在什麼位置上,是隻能放在類上?仍是既能夠放在方法上,又能夠放在屬性上。
從Target的源代碼能夠看到,java在定義Target註解時用又用@Target註解了。
源碼以下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

該註解有一個value屬性,值是一個ElementType數組。其中ElementType是一個枚舉類,源碼以下:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE
}

這些枚舉實例分別表示@Target註解的註解能夠用在哪些地方,註釋已經寫的很清楚。
再看咱們自定義的@JDBCConfig註解,能夠看到該註解上的@Target,參數爲METHOD、TYPE,代表@JDBCConfig註解只能用於方法、類或接口上。

@Retention

表示註解存在的生命週期。仍是看其源碼:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

能夠看到該註解有一個value參數,參數的值是RetentionPolicy,它也是一個枚舉類,源碼以下:

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

value的值爲SOURCE,說明該註解只保留在源代碼上,編譯後該註解就會被去除。
爲CLASS代表註解在java文件編程成.class文件後,依然存在,可是運行起來後就沒了。
爲RUNTIME代表註解在運行起來以後依然存在,程序能夠經過反射獲取這些信息,自定義註解@JDBCConfig就是這樣。

@Inherited

@Inherited表示該註解具備繼承性。下面自定義註解@Inheritable被元註解@Inherited修飾,當用該註解修飾Base類時,那麼其子類InheritableTest也會獲得這個註解,下面輸出結果爲true。

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Inheritable {

}
@Inheritable
class Base{

}
public class InheritableTest extends Base{
    public static void main(String[] args) {
        //輸出true
        System.out.println(InheritableTest.class
                .isAnnotationPresent(Inheritable.class));
    }
}

上面輸出結果爲true。雖然InheritableTest上面沒有註解,但由於他繼承自Base,Base又被一個可被繼承的註解@Inheritable註釋,因此InheritableTest也能獲取到這個註解。

@Document

@Document用於指定被該元Annotation修飾的Annotation類將被javadoc工具提取成文檔,若是定義Annotation類時使用@Document修飾,則全部使用該Annotation修飾的程序元素的API文檔中將會包含該Annotation說明。
我以爲這個不經常使用,就再也不說明,之後遇到在補充。

@Repeatable(java1.8新增)

在java8以前,同一個程序元素前最多隻能使用一個相同類型的Annotation;若是須要在同一個元素前使用多個相同類型的Annotation,則必須使用Annotation「容器」。例如Structs2開發中,有時須要在Action類上使用@Result註解。在java8之前只能寫成以下形式。

@Results({@Result(name="failure",location="fail.jsp"),@Result(name="success",location="success.jsp")}
public Action FooAction{..... }

而不能寫成以下形式:

@Result(name="failure",location="fail.jsp")
@Result(name="success",location="success.jsp")
public Action FooAction{..... }

如今咱們本身寫一個可重複的註解。
首先自定義的註解要用@Repeatable修飾@MultiAnot,該註解的值是一個「容器」註解@MultiAnots,這個容器註解的值就是自定義的可重複註解。以下:

@Repeatable(value = MultiAnots.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MultiAnot{
    String name() default "SunQ";
    int age();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MultiAnots {
    MultiAnot[] value();
}
//@MultiAnots({@MultiAnot(age=24),@MultiAnot(name = "wang",age=22)})
@MultiAnot(age=24)
@MultiAnot(name = "zhang",age=23)
public class MultiAnotTest {

    public static void main(String[] args) {
        Class<MultiAnotTest> clas=MultiAnotTest.class;

        MultiAnot[] anots=clas.getAnnotationsByType(MultiAnot.class);
        for (MultiAnot multiAnot:anots){
            System.out.println(multiAnot.name()+"--->"+multiAnot.age());
        }

        MultiAnot anot=clas.getDeclaredAnnotation(MultiAnot.class);
        System.out.println(anot);//輸出null

        MultiAnots container = clas.getDeclaredAnnotation(MultiAnots.class);
        System.out.println(container);
    }
}

上面程序輸出爲:

SunQ--->24
zhang--->23
null
@top.sqmax.chapter14.MultiAnots(value=[@top.sqmax.chapter14.MultiAnot(name=SunQ, age=24), @top.sqmax.chapter14.MultiAnot(name=zhang, age=23)])

從上面輸出能夠看到getAnnotationsByType能夠獲取重複註解,而getDeclaredAnnotation不能;還有雖然上面沒有用到@MultiAnots註解,getDeclaredAnnotation仍然能夠獲取到@MultiAnots註解,它會把兩個@MultiAnot註解看成一個數組做爲@MultiAnots註解的值。

省略屬性名

當註解方法名爲value時,使用註解時,value屬性名能夠省略。
上面的不少註解的使用都省略了value這個屬性名,注意屬性名爲其餘時,不能省略。
如:

@Repeatable(value = MultiAnots.class)
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
@interface MultiAnot{
    String name() default "SunQ";
    int age();
}

能夠寫成:

@Repeatable(MultiAnots.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MultiAnot{
    String name() default "SunQ";
    int age();
}
相關文章
相關標籤/搜索