註解(Annotation)至關於一種標記,在程序中加入註解就等於爲程序打上某種標記,沒有加,則等於沒有任何標記,之後,javac編譯器、開發工具和其餘程序能夠經過反射來了解你的類及各類元素上有無何種標記,看你的程序有什麼標記,就去幹相應的事,標記能夠加在包、類,屬性、方法,方法的參數以及局部變量上。java
註解就至關於一個你的源程序要調用一個類,在源程序中應用某個註解,得事先準備好這個註解類。就像你要調用某個類,得事先開發好這個類。程序員
註解根據它的生命週期能夠分爲,源碼註解、編譯時註解、運行時註解數組
源碼註解:只存在於源碼中,當源碼進行編譯之後,註解就不存在了ide
編譯時註解:存在於源碼和字節碼文件中,當程序開始運行註解就不存在了工具
運行時註解:不只存在於源碼和字節碼文件中,在程序運行時依然存在,而且能夠影響程序的運行
開發工具
按照運行機制分spa
源碼註解:註解只在源碼中存在,編譯成.class文件就不存在了;code
編譯時註解:註解在源碼和.class文件中都存在;對象
運行時註解:在運行階段還起做用,甚至會影響運行邏輯的註解;blog
按照來源分
JDK註解
第三方註解
自定義註解
註解(Annotation)很重要,將來的開發模式都是基於註解的,JPA是基於註解的,Spring2.5以上都是基於註解的,Hibernate3.x之後也是基於註解的,如今的Struts2有一部分也是基於註解的了,註解是一種趨勢,如今已經有很多的人開始用註解了,註解是JDK1.5以後纔有的新特性
JDK1.5以後內部提供的三個註解
@Deprecated 意思是「廢棄的,過期的」
@Override 意思是「重寫、覆蓋」
@SuppressWarnings 意思是「壓縮警告」
1 @Documented 2 @Retention(value=RUNTIME) 3 public @interface Deprecated
Java API中是這樣定義的@Override的
1 @Target(value=METHOD) 2 @Retention(value=SOURCE) 3 public @interface Override
Java API中是這樣定義的@SuppressWarnings的
1 @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE}) 2 @Retention(value=SOURCE) 3 public @interface SuppressWarnings
@SuppressWarnings是給javac(java編譯器)看的,編譯器編譯完java文件後,@SuppressWarnings註解就沒有用了,因此@SuppressWarnings的Retention的屬性值是RetentionPolicy.SOURCE
package com.my.zhujiedemo; public class FuLei { public void print(){ System.out.println("我是父類中的方法"); } }
package com.my.zhujiedemo; public class A extends FuLei{ @Override public void print() { System.out.println("我重寫了父類中的方法"); } @Deprecated public void say(){ System.out.println("我是過期的方法"); } @SuppressWarnings("depracation") public void speak(){ System.out.println("我是沒過期的方法"); } }
package com.my.zhujiedemo; public class ZhujieDemo { public static void main(String[] args) { A a = new A(); a.print(); a.say(); a.speak(); } }
在這出不來效果,我截圖表示,以圖爲主
運行結果: 我重寫了父類中的方法 我是過期的方法 我是沒過期的方法
註解的定義及應用
註解經過 @interface關鍵字進行定義。 public @interface AnnotationZhuJie { }
它的形式跟接口很相似,不過前面多了一個 @ 符號。上面的代碼就建立了一個名字爲 AnnotaionZhuJie 的註解。
你能夠簡單理解爲建立了一張名字爲 AnnotationZhuJie 的標籤
上面建立了一個註解,那麼註解的的使用方法是什麼呢?
@AnnotationZhuJie public class Test { }
當咱們發現JDK爲咱們提供的註解並不能知足咱們的某些需求的時候,好比,想要讓程序在運行的時候得到信息,這時候,咱們就須要使用一個工具來製做符合咱們需求的註解,這個工具叫作元註解。
元註解:給註解進行註解的註解
元標籤有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 種
Retention 的英文意爲保留期的意思。當 @Retention 應用到一個註解上的時候,它解釋說明了這個註解的的存活時間。
它的取值以下:
RetentionPolicy.SOURCE 註解只在源碼階段保留,在編譯器進行編譯時它將被丟棄忽視。
RetentionPolicy.CLASS 註解只被保留到編譯進行的時候,它並不會被加載到 JVM 中。
RetentionPolicy.RUNTIME 註解能夠保留到程序運行的時候,它會被加載進入到 JVM 中,因此在程序運行時能夠獲取到它們。
咱們能夠這樣的方式來加深理解,@Retention 去給一張標籤解釋的時候,它指定了這張標籤張貼的時間。@Retention 至關於給一張標籤上面蓋了一張時間戳,時間戳指明瞭標籤張貼的時間週期。
@Retention(RetentionPolicy.RUNTIME) public @interface AnnotationZhuJie { }
顧名思義,這個元註解確定是和文檔有關。它的做用是可以將註解中的元素包含到 Javadoc 中去。
你能夠這樣理解,當一個註解被 @Target 註解時,這個註解就被限定了運用的場景。
@Target元註解決定了一個註解能夠標識到哪些成分上,如標識在在類身上,或者屬性身上,或者方法身上等成分,@Target默認值爲任何元素(成分)
類比到標籤,本來標籤是你想張貼到哪一個地方就到哪一個地方,可是由於 @Target 的存在,它張貼的地方就很是具體了,好比只能張貼到方法上、類上、方法參數上等等。@Target 有下面的取值
ElementType.ANNOTATION_TYPE 能夠給一個註解進行註解
ElementType.CONSTRUCTOR 能夠給構造方法進行註解
ElementType.FIELD 能夠給屬性進行註解
ElementType.LOCAL_VARIABLE 能夠給局部變量進行註解
ElementType.METHOD 能夠給方法進行註解
ElementType.PACKAGE 能夠給一個包進行註解
ElementType.PARAMETER 能夠給一個方法內的參數進行註解
ElementType.TYPE 能夠給一個類型進行註解,好比類、接口、枚舉
1 @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE}) 2 @Retention(value=SOURCE) 3 public @interface AnnotationZhuJie
Inherited 是繼承的意思,可是它並非說註解自己能夠繼承,而是說若是一個超類被 @Inherited 註解過的註解進行註解的話,那麼若是它的子類沒有被任何註解應用的話,那麼這個子類就繼承了超類的註解。
說的比較抽象。代碼來解釋。
@Inherited @Retention(RetentionPolicy.RUNTIME) @interface Test {} @Test public class A {} public class B extends A {}
註解 Test 被 @Inherited 修飾,以後類 A 被 Test 註解,類 B 繼承 A,類 B 也擁有 Test 這個註解。
Repeatable 天然是可重複的意思。@Repeatable 是 Java 1.8 才加進來的,因此算是一個新的特性。
什麼樣的註解會屢次應用呢?一般是註解的值能夠同時取多個。
舉個例子,一我的他既是程序員又是產品經理,同時他仍是個畫家。
@interface Persons { Person[] value(); } @Repeatable(Persons.class) @interface Person{ String role default ""; } @Person(role="artist") @Person(role="coder") @Person(role="PM") public class SuperMan{ }
注意上面的代碼,@Repeatable 註解了 Person。而 @Repeatable 後面括號中的類至關於一個容器註解。
什麼是容器註解呢?就是用來存放其它註解的地方。它自己也是一個註解。
咱們再看看代碼中的相關容器註解。
@interface Persons { Person[] value(); }
按照規定,它裏面必需要有一個 value 的屬性,屬性類型是一個被 @Repeatable 註解過的註解數組,注意它是數組。
若是很差理解的話,能夠這樣理解。Persons 是一張總的標籤,上面貼滿了 Person 這種同類型但內容不同的標籤。把 Persons 給一個 SuperMan 貼上,至關於同時給他貼了程序員、產品經理、畫家的標籤。
咱們可能對於 @Person(role=「PM」) 括號裏面的內容感興趣,它其實就是給 Person 這個註解的 role 屬性賦值爲 PM ,你們不明白正常,立刻就講到註解的屬性這一塊。
註解能夠當作是一種特殊的類,既然是類,那天然能夠爲類添加屬性
註解的屬性也叫作成員變量。註解只有成員變量,沒有方法。註解的成員變量在註解的定義中以「無形參的方法」形式來聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型。
若是一個註解中有一個名稱爲value的屬性,且你只想設置value屬性(即其餘屬性都採用默認值或者你只有一個value屬性),那麼能夠省略掉「value=」部分。
package com.my.zhujie; public class Aaa { public void print(){ System.out.println("A中的方法"); } }
package com.my.zhujie; @Anno(name="小花",sex="女") public class Bbb extends Aaa { @Override @Anno(name="小明",sex="男",age=30) public void print() { super.print(); } }
package com.my.zhujie; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //註解的做用域 @Target({ElementType.METHOD,ElementType.TYPE}) //註解的生命週期 @Retention(RetentionPolicy.RUNTIME) public @interface Anno { //成員以無參無異常的方式聲明 String name(); String sex(); //能夠用default爲成員指定一個默認值 int age() default 20; }
package com.my.zhujie; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ZhuJie { public static void main(String[] args) { try { Class c = Class.forName("com.my.zhujie.Bbb"); Bbb b = (Bbb)c.newInstance(); Method method = c.getMethod("print"); method.invoke(b); //得到類上的註解 boolean b1 = c.isAnnotationPresent(Anno.class); if (b1){ //得到註解對象 Anno a = (Anno)c.getAnnotation(Anno.class); //打印註解成員的值 System.out.println(a.name()); System.out.println(a.sex()); System.out.println(a.age()); } //得到方法上的註解 Anno anno = method.getAnnotation(Anno.class); System.out.println(anno.name()); System.out.println(anno.sex()); System.out.println(anno.age()); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
運行結果: A中的方法 小花 女 20 小明 男 30
若是一個註解內僅僅只有一個名字爲 value 的屬性時,應用這個註解時能夠直接接屬性值填寫到括號內。
//註解的做用域 @Target({ElementType.METHOD,ElementType.TYPE}) //註解的生命週期 @Retention(RetentionPolicy.RUNTIME) public @interface Anno { String value(); }
上面代碼中,Check 這個註解只有 value 這個屬性。因此能夠這樣應用。
package com.my.zhujie; @Anno("值") public class Bbb extends Aaa { @Override @Anno("值") public void print() { super.print(); } }
最後,還須要注意的一種狀況是一個註解沒有任何屬性。好比
@Target({ElementType.METHOD,ElementType.TYPE}) //註解的生命週期 @Retention(RetentionPolicy.RUNTIME) public @interface Anno { }
那麼在應用這個註解的時候,括號均可以省略。
package com.my.zhujie; @Anno public class Bbb extends Aaa { @Override @Anno public void print() { super.print(); } }