Annotations是一种元数据,其作用在于提供程序本身以外的一些数据信息,也就是说Annotation他不会属于程序代码本身,不参与逻辑运算,故而不会对原程序代码的操作产生直接的影响。
一般来说Annotation有如下三种使用情形:
为编译器提供辅助信息
— Annotations可以为编译器提供而外信息,以便于检测错误,抑制警告等.
编译源代码时进行而外操作
— 软件工具可以通过处理Annotation信息来生成源代码,xml文件等等.
运行时处理
— 有一些annotation甚至可以在程序运行时被检测,使用.
所谓注解,就是 注入数据+解释(标识作用)
1、注入数据
通过注解属性注入数据
2、解释或标识
比如@Override标识这个方法是重写的。
一、元注解
元注解:给注解类添加的注解JDK提供给我们的几种常用元注解:
1、Documented
Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中.
2、Retention
这种类型的注解会被保留到那个阶段. 有三个值:
RetentionPolicy.SOURCE
这种类型的Annotations只在源代码级别保留,编译时就会被忽略,因此一般用来为编译器提供额外信息,以便于检测错误,抑制警告等.
RetentionPolicy.CLASS
这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略,一般用来生成源代码,xml文件等
RetentionPolicy.RUNTIME
这种类型的Annotations将被JVM保留,所以它们能在运行时被JVM或其他使用反射机制的代码所读取和使用.
3、Target
定义该注解可定义在什么地方,可选值定义在ElementType枚举中:
--TYPE:类、接口、注解、枚举
--FIELD:成员变量,包括枚举常量
--METHOD:方法
--PARMETER:方法参数上
--CONSTRUCTOR:构造方法
--LOCAL_VARIABLE:局部变量
--ANNOTATION_TYPE:枚举
--PACKAGE:包
4、Inherited
允许子类继承父类的注解。
这个怎么理解呢,给大家举个列子
首先定义一个注解,给该注解添加@Inherited
@Target(value = { ElementType.TYPE,ElementType }) //该注解可以定义在类、方法上
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {
}
@MyAnnotation
public class Father{
@MyAnnotation
public void fun() {
}
}
public class Child extends Father {
@Override
public void fun() {
}
}
测试看看Child类是否继承了MyAnnotation注解
boolean annotationPresent = Child.class.isAnnotationPresent(MyAnnotation.class);
结果:annotationPresent=true
Child类上并没有添加注解,但是Child的父类添加了注解MyAnnotation,因为MyAnnotation是可继承的,所以Child类也就继承了该注解。
注意:只有在类上添加注解时,@Inherited才有效,在方法上添加的注解,子类重写该方法,则被子类重写的方法是不会继承该注解的。
Child类重写了父类中的fun方法,
boolean annotationPresent =Child.class.getMethod("fun").isAnnotationPresent(MyAnnotation.class);
结果:annotationPresent=false
二、定义注解
1、Java中使用@interface声明注解
2、注解支持的数据类型
基本数据类型及其数组、String及String[],枚举,注解,Class
@Target(value = {ElementType.TYPE,ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {
// 基本数据类型:byte,short,int,long,float,double,char,boolean
int intTest() default 0;
// 基本数据类型数组:byte[],short[],int[],long[],float[],double[],char[],boolean[]
int[] intArrayTest() default {};
// String
String strTest() default "";
// String数组
String[] strArrayTest() default {};
// 枚举
ElementType[] enumType() default {};
// 注解
MetaAnnotation anno() default @MetaAnnotation();
// Class
Class<?> clsType() default Object.class;
}
如果注解中只有一个属性,使用的时候就可以不用明确写出属性名
@Target(value = {ElementType.TYPE,ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SingleFieldAnnotation {
String value();
}
使用
//原始写法
@SingleFieldAnnotation(value="hello world")
public void method(){
}
//因为只有一个属性,可以不用指定属性名
@SingleFieldAnnotation("hello world")
public void method2(){
}
三、注解的使用相关API
为方便讲解,给大家一个直观的感觉,首先定义2个注解
MyAnnotation.java
@Target(value = { ElementType.TYPE,ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {
}
MyAnnotation2.java
@Target(value = { ElementType.TYPE,ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation2 {
}
使用注解
@MyAnnotation
public class Father{
@MyAnnotation
public void fun() {
}
}
@MyAnnotation2
public class Child extends Father {
@MyAnnotation
public void fun2() {
}
}
isAnnotationPresent
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。
比如:
boolean annotationPresent = Child.class.isAnnotationPresent(MyAnnotation2.class);
Child类上存在MyAnnotation2注解,所以返回true
getAnnotation
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
如果存在该元素的指定类型的注释(包括继承的注解 ),则返回这些注释,否则返回 null。
比如:
MyAnnotation annotation= Child.class.getAnnotation(MyAnnotation.class);
虽然Child类上没有添加MyAnnotation,但是由于Child的父类添加了MyAnnotation注解,MyAnnotation可继承,所以Child也就拥有了该注解。
getDeclaredAnnotation
<T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass)
如果存在该元素的指定类型的注释(不包括继承的注解 ),则返回这些注释,否则返回 null。
比如:
MyAnnotation annotation= Child.class.getDeclaredAnnotation(MyAnnotation.class);
结果annotation=null;因为Child类上只定义了MyAnnotation2.
getAnnotations
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
返回此元素上存在的所有注释,包括继承的注解
比如:
Annotation[] declaredAnnotations = Child.class.getAnnotation();
//由于MyAnnotation是可以继承的
返回结果有2个注解:MyAnnotation和MyAnnotation2。
getDeclaredAnnotations
Annotation[] getDeclaredAnnotations();
返回直接存在于此元素上的所有注释,不包括继承的注解。
比如:
Annotation[] declaredAnnotations = Child.class.getDeclaredAnnotations();
返回Child类上直接定义的注解,这里只有MyAnnotation2。
四、为什么要使用注解
知道了如何定义注解以及注解的相关API后,我们回过头来想想,注解到底有什么作用呢?
1、标识作用
注解一个非常重要的作用就是标识,JDK中就定义了很多的注解用来起到标识的作用。比如:
@Override 用来表示该方法是继承的或实现接口的。
@Deprecated 用来标识该元素是过时的。
Java框架中的注解是随处可见的,比如SpringMVC中的@Controller,@Service,.....................
2、注入属性
由于注解中可以定义属性,设置属性值,所以如果我们想预先给类或方法注入一些属性值的时候,就可以通过注解来注入。调用者可通过反射来获取注入的属性值。
3、减少耦合度
最后,建议大家多看一些优秀的框架设计,试图弄明白设计者的意图。由于注解的使用离不开反射,所以熟练掌握反射也是开发人员必掌握的技能之一。
另外补充下,与Object是所有类的父类一样,java.lang.annotation.Annotation是所有注解类的父类。