? ? ? ? ? ? Java注解
一、什么是注解
? ? ? ? ? ??注解(Annotation)相當(dāng)于一種標(biāo)記,在程序中加入注解就等于為程序打上某種標(biāo)記
,沒有加,則等于沒有任何標(biāo)記,以后,javac編譯器、開發(fā)工具和其他程序可以通過反射來了解你的類及各種元素上有無何種標(biāo)記,看你的程序有什么標(biāo)記,就去干相應(yīng)的事,
標(biāo)記可以加在包、類,屬性、方法,方法的參數(shù)以及局部變量上。
?
注解就相當(dāng)于一個(gè)你的源程序要調(diào)用一個(gè)類,在源程序中應(yīng)用某個(gè)注解,得事先準(zhǔn)備好這個(gè)注解類。就像你要調(diào)用某個(gè)類,得事先開發(fā)好這個(gè)類。
?
?
?
注解的分類
注解根據(jù)它的生命周期可以分為,源碼注解、編譯時(shí)注解、運(yùn)行時(shí)注解
? ? ? ?源碼注解:只存在于源碼中,當(dāng)源碼進(jìn)行編譯以后,注解就不存在了
? ? ? ?編譯時(shí)注解:存在于源碼和字節(jié)碼文件中,當(dāng)程序開始運(yùn)行注解就不存在了
? ? ? ?運(yùn)行時(shí)注解:不僅存在于源碼和字節(jié)碼文件中,在程序運(yùn)行時(shí)依然存在,并且可以影響程序的運(yùn)行
按照運(yùn)行機(jī)制分
? ? ? ?源碼注解:注解只在源碼中存在,編譯成.class文件就不存在了;
? ? ? ?編譯時(shí)注解:注解在源碼和.class文件中都存在;
? ? ? ?運(yùn)行時(shí)注解:在運(yùn)行階段還起作用,甚至?xí)绊戇\(yùn)行邏輯的注解;
?
按照來源分
? ? ? JDK注解
? ? ? 第三方注解
? ? ? 自定義注解
?
二、JDK內(nèi)部提供的注解
?
注解(Annotation)很重要,未來的開發(fā)模式都是基于注解的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.x以后也是基于注解的,現(xiàn)在的Struts2有一部分也是基于注解的了,注解是一種趨勢(shì),現(xiàn)在已經(jīng)有不少的人開始用注解了,注解是JDK1.5之后才有的新特性
JDK1.5之后內(nèi)部提供的三個(gè)注解
?????? @Deprecated 意思是“廢棄的,過時(shí)的”
?????? @Override 意思是“重寫、覆蓋”
?????? @SuppressWarnings 意思是“壓縮警告”
?
@Deprecated
Java API中是這樣定義的@Deprecated的
1 @Documented 2 @Retention(value=RUNTIME) 3 public @interface Deprecated
@Override
Java API中是這樣定義的@Override的
?
1 @Target(value=METHOD) 2 @Retention(value=SOURCE) 3 public @interface Override
?
@SuppressWarnings?
?
?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("我是過時(shí)的方法"); } @SuppressWarnings("depracation") public void
speak(){ System.out.println("我是沒過時(shí)的方法"); } }
package com.my.zhujiedemo; public class ZhujieDemo { public static void
main(String[] args) { A a = new A(); a.print(); a.say(); a.speak(); } }
在這出不來效果,我截圖表示,以圖為主
?
運(yùn)行結(jié)果: 我重寫了父類中的方法 我是過時(shí)的方法 我是沒過時(shí)的方法
?
三、自定義注解
?
注解的定義及應(yīng)用
?
注解通過 @interface關(guān)鍵字進(jìn)行定義。 public @interface AnnotationZhuJie { }
?
它的形式跟接口很類似,不過前面多了一個(gè) @ 符號(hào)。上面的代碼就創(chuàng)建了一個(gè)名字為 AnnotaionZhuJie 的注解。
你可以簡(jiǎn)單理解為創(chuàng)建了一張名字為 AnnotationZhuJie 的標(biāo)簽
?
上面創(chuàng)建了一個(gè)注解,那么注解的的使用方法是什么呢?
?
@AnnotationZhuJie public class Test { }
?
?
?
?
?
? ? ? ? ?
當(dāng)我們發(fā)現(xiàn)JDK為我們提供的注解并不能滿足我們的某些需求的時(shí)候,比如,想要讓程序在運(yùn)行的時(shí)候獲得信息,這時(shí)候,我們就需要使用一個(gè)工具來制作符合我們需求的注解,這個(gè)工具叫做元注解。
? ? ? ?元注解:給注解進(jìn)行注解的注解
? ? ? ?元標(biāo)簽有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 種
?
?
?
@Retention
Retention 的英文意為保留期的意思。當(dāng) @Retention 應(yīng)用到一個(gè)注解上的時(shí)候,它解釋說明了這個(gè)注解的的存活時(shí)間。
?
? ? ? 它的取值如下:
?
? ? ? ? ? ? ?RetentionPolicy.SOURCE 注解只在源碼階段保留,在編譯器進(jìn)行編譯時(shí)它將被丟棄忽視。
? ? ? ? ? ? ?RetentionPolicy.CLASS 注解只被保留到編譯進(jìn)行的時(shí)候,它并不會(huì)被加載到 JVM 中。
? ? ? ? ? ? ?RetentionPolicy.RUNTIME 注解可以保留到程序運(yùn)行的時(shí)候,它會(huì)被加載進(jìn)入到 JVM
中,所以在程序運(yùn)行時(shí)可以獲取到它們。
我們可以這樣的方式來加深理解,@Retention 去給一張標(biāo)簽解釋的時(shí)候,它指定了這張標(biāo)簽張貼的時(shí)間。@Retention
相當(dāng)于給一張標(biāo)簽上面蓋了一張時(shí)間戳,時(shí)間戳指明了標(biāo)簽張貼的時(shí)間周期。
?
?
@Retention(RetentionPolicy.RUNTIME) public @interface AnnotationZhuJie { }
?
@Documented
顧名思義,這個(gè)元注解肯定是和文檔有關(guān)。它的作用是能夠?qū)⒆⒔庵械脑匕?Javadoc 中去。
?
?
@Target
Target 是目標(biāo)的意思,@Target 指定了注解運(yùn)用的地方。
?
你可以這樣理解,當(dāng)一個(gè)注解被 @Target 注解時(shí),這個(gè)注解就被限定了運(yùn)用的場(chǎng)景。
?
@Target元注解決定了一個(gè)注解可以標(biāo)識(shí)到哪些成分上,如標(biāo)識(shí)在在類身上,或者屬性身上,或者方法身上等成分,@Target默認(rèn)值為任何元素(成分)
?
?
類比到標(biāo)簽,原本標(biāo)簽是你想張貼到哪個(gè)地方就到哪個(gè)地方,但是因?yàn)?@Target
的存在,它張貼的地方就非常具體了,比如只能張貼到方法上、類上、方法參數(shù)上等等。@Target 有下面的取值
?
? ? ? ?ElementType.ANNOTATION_TYPE 可以給一個(gè)注解進(jìn)行注解
? ? ? ?ElementType.CONSTRUCTOR 可以給構(gòu)造方法進(jìn)行注解
? ? ? ?ElementType.FIELD 可以給屬性進(jìn)行注解
? ? ? ?ElementType.LOCAL_VARIABLE 可以給局部變量進(jìn)行注解
? ? ? ?ElementType.METHOD 可以給方法進(jìn)行注解
? ? ? ?ElementType.PACKAGE 可以給一個(gè)包進(jìn)行注解
? ? ? ?ElementType.PARAMETER 可以給一個(gè)方法內(nèi)的參數(shù)進(jìn)行注解
? ? ? ?ElementType.TYPE 可以給一個(gè)類型進(jìn)行注解,比如類、接口、枚舉
1 @Target(value={TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIABLE}) 2
@Retention(value=SOURCE) 3 public @interface AnnotationZhuJie
?
?
?
@Inherited
?
Inherited 是繼承的意思,但是它并不是說注解本身可以繼承,而是說如果一個(gè)超類被 @Inherited
注解過的注解進(jìn)行注解的話,那么如果它的子類沒有被任何注解應(yīng)用的話,那么這個(gè)子類就繼承了超類的注解。
說的比較抽象。代碼來解釋。
?
@Inherited @Retention(RetentionPolicy.RUNTIME) @interface Test {} @Test public
class A {} public class B extends A {}
?
注解 Test 被 @Inherited 修飾,之后類 A 被 Test 注解,類 B 繼承 A,類 B 也擁有 Test 這個(gè)注解。
?
?
@Repeatable
?
Repeatable 自然是可重復(fù)的意思。@Repeatable 是 Java 1.8 才加進(jìn)來的,所以算是一個(gè)新的特性。
?
什么樣的注解會(huì)多次應(yīng)用呢?通常是注解的值可以同時(shí)取多個(gè)。
?
舉個(gè)例子,一個(gè)人他既是程序員又是產(chǎn)品經(jīng)理,同時(shí)他還是個(gè)畫家。
?
@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 后面括號(hào)中的類相當(dāng)于一個(gè)容器注解。
什么是容器注解呢?就是用來存放其它注解的地方。它本身也是一個(gè)注解。
我們?cè)倏纯创a中的相關(guān)容器注解。
?
@interface Persons { Person[] value(); }
?
按照規(guī)定,它里面必須要有一個(gè) value 的屬性,屬性類型是一個(gè)被 @Repeatable 注解過的注解數(shù)組,注意它是數(shù)組。
如果不好理解的話,可以這樣理解。Persons 是一張總的標(biāo)簽,上面貼滿了 Person 這種同類型但內(nèi)容不一樣的標(biāo)簽。把 Persons 給一個(gè)
SuperMan 貼上,相當(dāng)于同時(shí)給他貼了程序員、產(chǎn)品經(jīng)理、畫家的標(biāo)簽。
我們可能對(duì)于 @Person(role=“PM”) 括號(hào)里面的內(nèi)容感興趣,它其實(shí)就是給 Person 這個(gè)注解的 role 屬性賦值為 PM
,大家不明白正常,馬上就講到注解的屬性這一塊。
?
?
?
?
?
?
四、注解的屬性
注解可以看成是一種特殊的類,既然是類,那自然可以為類添加屬性
注解的屬性也叫做成員變量。注解只有成員變量,沒有方法。注解的成員變量在注解的定義中以“無形參的方法”形式來聲明,其方法名定義了該成員變量的名字,其返回值定義了該成員變量的類型。
?
?
value屬性
?
如果一個(gè)注解中有一個(gè)名稱為value的屬性,且你只想設(shè)置value屬性(即其他屬性都采用默認(rèn)值或者你只有一個(gè)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為成員指定一個(gè)默認(rèn)值 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){ //獲得注解對(duì)象
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(); } } }
運(yùn)行結(jié)果: A中的方法 小花 女 20 小明 男 30
如果一個(gè)注解內(nèi)僅僅只有一個(gè)名字為 value 的屬性時(shí),應(yīng)用這個(gè)注解時(shí)可以直接接屬性值填寫到括號(hào)內(nèi)。
?
//注解的作用域 @Target({ElementType.METHOD,ElementType.TYPE}) //注解的生命周期
@Retention(RetentionPolicy.RUNTIME) public @interface Anno { String value(); }
?
上面代碼中,Check 這個(gè)注解只有 value 這個(gè)屬性。所以可以這樣應(yīng)用。
?
package com.my.zhujie; @Anno("值") public class Bbb extends Aaa { @Override
@Anno("值") public void print() { super.print(); } }
?
最后,還需要注意的一種情況是一個(gè)注解沒有任何屬性。比如
?
@Target({ElementType.METHOD,ElementType.TYPE}) //注解的生命周期
@Retention(RetentionPolicy.RUNTIME) public @interface Anno { }
?
那么在應(yīng)用這個(gè)注解的時(shí)候,括號(hào)都可以省略。
?
package com.my.zhujie; @Anno public class Bbb extends Aaa { @Override @Anno
public void print() { super.print(); } }
?
本人原創(chuàng),轉(zhuǎn)載請(qǐng)說明出處https://www.cnblogs.com/zyx110/ <https://www.cnblogs.com/zyx110/>
如若對(duì)你有用,記得推薦,如若有誤,歡迎改正!
?