從JDK5開始,Java增長了對元數據的支持,也就是註解(Annotation),註解就是在代碼裏的特殊標記,這些標記能夠在編譯、類加載、運行時被讀取,並執行相應的處理。經過註解,程序開發人員能夠在不改變原有邏輯的狀況下,在源代碼中嵌入一些補充信息。java
上面@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 表示這個註解能放在什麼位置上,是隻能放在類上?仍是既能夠放在方法上,又能夠放在屬性上。
從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註解只能用於方法、類或接口上。
表示註解存在的生命週期。仍是看其源碼:
@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
表示該註解具備繼承性。下面自定義註解@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用於指定被該元Annotation修飾的Annotation類將被javadoc工具提取成文檔,若是定義Annotation類時使用@Document
修飾,則全部使用該Annotation修飾的程序元素的API文檔中將會包含該Annotation說明。
我以爲這個不經常使用,就再也不說明,之後遇到在補充。
在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(); }