### 元注解 {#元注解}
元注解的作用就是負責注解其他注解。Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它 annotation類型作說明。Java5.0定義的元注解:
* @Target
* @Retention
* @Documented
* @Inherited
這些類型和它們所支持的類在java.lang.annotation包中可以找到。下面我們看一下每個元注解的作用和相應分參數的使用說明。
#### @Target {#Target}
@Target說明了Annotation所修飾的對象范圍:Annotation可被用于 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。
取值\(ElementType\)有:
* CONSTRUCTOR:用于描述構造器
* FIELD:用于描述域
* LOCAL\_VARIABLE:用于描述局部變量
* METHOD:用于描述方法
* PACKAGE:用于描述包
* PARAMETER:用于描述參數
* TYPE:用于描述類、接口\(包括注解類型\) 或enum聲明
使用實例:
```
@Target(ElementType.TYPE)
public @interface Table {
public String tableName() default "className";
}
@Target(ElementType.FIELD)
public @interface NoDBColumn {
}
```
上述注解Table 可以用于注解類、接口\(包括注解類型\) 或enum聲明,而注解NoDBColumn僅可用于注解類的成員變量。
### @Retention {#Retention}
@Retention 表示需要在什么級別保存該注解信息。可選的RetentionPolicy參數包括:
* SOURCE:注解將被編譯器丟棄
* CLASS:注解在class文件中可用,但會被JVM丟棄
* RUNTIME:JVM將在運行期間保留注解,因此可以通過反射機制讀取注解的信息。
Retention meta-annotation類型有唯一的value作為成員,它的取值來自java.lang.annotation.RetentionPolicy的枚舉類型值。具體實例如下:
```
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
```
Column注解的的RetentionPolicy的屬性值是RUTIME,這樣注解處理器可以通過反射,獲取到該注解的屬性值,從而去做一些運行時的邏輯處理 。
### @Documented {#Documented}
@Documented 表示含有該注解類型的元素\(帶有注釋的\)會通過javadoc或類似工具進行文檔化,Documented是一個標記注解(類似@Override 這種只需要一個簡單的聲明即可的注解即為標記注解),沒有成員。該類型應用于注解那些影響客戶使用帶注釋\(comment\)的元素聲明的類型。如果類型聲明是用@Documented 來注解的,這種類型的注解被作為被標注的程序成員的公共API。
實例如下:
```
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}
```
### @Inherited {#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類型被發現,或者到達類繼承結構的頂層。
實例如下:
```
package com.jadyer.annotation.inherited;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface InheritedTest {
String value();
}
//默認的父類中的Annotation并不會被繼承至子類中,若想讓子類也繼承父類中的Annotation的話
//可以在定義Annotation型態時加上java.lang.annotation.Inherited型態的Annotation
package com.jadyer.annotation.inherited;
@InheritedTest("Jadyer")
public class Parent {
@InheritedTest("JavaEE")
public void doSomething() {
System.out.println("Parent do something");
}
}
package com.jadyer.annotation.inherited;
public class Child extends Parent {
@Override
public void doSomething() {
System.out.println("Child do something");
}
}
package com.jadyer.annotation.inherited;
import java.lang.reflect.Method;
/**
* 通過反射的方式測試Child類中是否繼承了Parent中的InheritedTest注解
*/
public class Test {
public static void main(String[] args) throws SecurityException, NoSuchMethodException {
classTest();
methodTest();
}
/**
* 通過反射方式測試子類是否會繼承父類中定義在類上面的注解
*/
public static void classTest(){
Class<Child> c = Child.class;
if (c.isAnnotationPresent(InheritedTest.class)) {
InheritedTest inheritedTest = c.getAnnotation(InheritedTest.class);
String value = inheritedTest.value();
System.out.println(value);
}
}
/**
* 通過反射方式測試子類是否會繼承父類中定義在方法上面的注解
*/
public static void methodTest() throws SecurityException, NoSuchMethodException{
Class<Child> c = Child.class;
Method method = c.getMethod("doSomething", new Class[]{});
if (method.isAnnotationPresent(InheritedTest.class)) {
InheritedTest inheritedTest = method.getAnnotation(InheritedTest.class);
String value = inheritedTest.value();
System.out.println(value);
}
}
}
/**********【運行輸出】*********************************************************************/
//通過運行輸出,我們會發現,結果只輸出了Jadyer,而沒有JavaEE
//若注釋掉Child.java中的doSomething(),再運行,結果輸出了Jadyer和JavaEE
//這是為什么呢?問題就出在Child.java重寫了Parent.java的doSomething()方法
```
當父類中的注解被@Inherited標注的情況下
1. 如果父類的注解是定義在類上面,那么子類是可以繼承過來的
2. 如果父類的注解定義在方法上面,那么子類仍然可以繼承過來
3. 如果子類重寫了父類中定義了注解的方法,那么子類將無法繼承該方法的注解
4. 即子類在重寫父類中被@Inherited標注的方法時,會將該方法連帶它上面的注解一并覆蓋掉
_注意:_
1. _JDK文檔中對此種情況的敘述并不全面_
2. _接口的實現類是無法繼承接口中所定義的被@Inherited標注的注解的_
- java演變
- JDK各個版本的新特性
- JDK1.5新特性
- JDK1.6新特性
- JDK1.7新特性
- JDK1.8新特性
- JAVA基礎
- 面向對象特性
- 多態
- 方法重載
- 方法重寫
- class
- 常量
- 訪問修飾符
- 類加載路徑
- java-equals
- 局部類
- java-hashCode
- Java類初始化順序
- java-clone方法
- JAVA對象實例化的方法
- 基礎部分
- JAVA基礎特性
- JAVA關鍵字
- javabean
- static
- 日期相關
- final
- interface
- 函數式接口
- JAVA異常
- 異常屏蔽
- try-with-resource資源泄露
- JAVA引用
- WeakReference
- SoftReference
- PhantomReference
- 位運算符
- try-with-resource語法糖
- JDK冷知識
- JAVA包裝類
- JAVA基本類型與包裝類
- java.lang.Boolean
- java.lang.Integer
- java.lang.Byte
- java.lang.Short
- java.lang.Long
- java.lang.Float
- java.lang.Double
- java.lang.Character
- 日期相關
- TemporalAdjusters
- String
- 字符串常量池
- String拼接
- String編譯期優化
- StringBuilder&StringBuffer
- intern
- 注解
- java標準注解
- 內置注解
- 元注解
- 自定義注解
- 注解處理器
- JVM注解
- Java8 Annotation新特性
- 反射-Reflective
- Reflection
- Class
- Constructor
- Method
- javabean-property
- MethodHandles
- 泛型
- 類型擦除
- bridge-method
- Accessor&Mutator方法
- enum
- JAVA數組
- finalize方法
- JAR文件
- JAVA高級編程
- CORBA
- JMX
- SPI
- Java SPI使用約定
- ServiceLoader
- 實際應用
- IO
- 工具類
- JDK常用工具類
- Objects
- System
- Optional
- Throwable
- Collections
- Array
- Arrays
- System
- Unsafe
- Number
- ClassLoader
- Runtime
- Object
- Comparator
- VarHandle
- 數據結構
- 棧-Stack
- 隊列(Queue)
- Deque
- PriorityQueue
- BlockingQueue
- SynchronousQueue
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- ConcurrentLinkedQueue
- 列表
- 迭代器
- KV鍵值對數據類型
- HashMap
- TreeMap
- Hash沖突
- ConcurrentHashMap
- JDK1.7 ConcurrentHashMap結構
- jdk7&jdk8區別
- 集合
- Vector
- Stack
- HashSet
- TreeSet
- ArrayList
- LinkedList
- ArrayList && LinkedList相互轉換
- 線程安全的集合類
- 集合類遍歷性能
- 并發容器
- CopyOnWriteArrayList
- ConcurrentHashMap
- 同步容器
- BitMap
- BloomFilter
- SkipList
- 設計模式
- 設計模式六大原則
- 單例模式
- 代理模式
- 靜態代理
- 動態代理
- JDK動態代理
- cglib動態代理
- spring aop
- 策略模式
- SpringAOP策略模式的運用
- 生產者消費者模式
- 迭代器模式
- 函數式編程
- 方法引用
- 性能問題
- Lambda
- Lambda類型檢查
- Stream
- findFirst和findAny
- reduce
- 原始類型流特化
- 無限流
- 收集器
- 并行流
- AOP
- 靜態織入
- aspect
- aspect的定義
- AspectJ與SpringAOP
- 動態織入
- 靜態代理
- 動態代理
- JDK動態代理
- CGLib動態代理
- Spring AOP
- SpringAOP五種通知類型
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
- Aspect優先級
- SpringAOP切點表達式
- within
- execution
- 嵌套調用
- 系統優化與重構
- 重疊構造器模式
- 工具類構造器優化
- 常見面試題
- new Object()到底占用幾個字節
- 訪問修飾符
- cloneable接口實現原理
- 異常分類以及處理機制
- wait和sleep的區別
- 數組在內存中如何分配
- 類加載為什么要使用雙親委派模式,有沒有什么場景是打破了這個模式
- 類的實例化順序
- 附錄
- JAVA術語
- FAQ
- 墨菲定律
- 康威定律
- 軟件設計原則
- 阿姆達爾定律
- 字節碼工具
- OSGI