### 元注解 {#元注解}
元注解的作用就是负责注解其他注解。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