注解对于开发人员来讲既熟悉又陌生,熟悉是因为只要你是做开发,都会用到注解(常见的@Override);陌生是因为即使不使用注解也照常能够进行开发;注解不是必须的,但了解注解有助于我们深入理解某些第三方框架(比如Spring,Hibernate,Mybatis等),提高工作效率。
Java注解又称为标注,是Java从1.5开始支持加入源码的特殊语法元数据;Java中的类、方法、变量、参数、包都可以被注解。这里提到的元数据是描述数据的数据
1. 注解作用
格式检查: 告诉编译器信息,比如被@Override标记的方法如果不是父类的某个方法,IDE会报错;
减少配置: 运行时动态处理,得到注解信息,实现代替配置文件的功能;
减少重复工作:比如第三方框架xUtils,通过注解@ViewInject减少对findViewById的调用,
类似的还有(JUnit、ActiveAndroid等);
2. 注解分三类
1:JDK自带的注解(Java目前只内置了三种标准注解:@Override、@Deprecated、@SuppressWarnings,以及四种元注解:@Target、@Retention、@Documented、@Inherited)
2:第三方的注解——这一类注解是我们接触最多和作用最大的一类
3:自定义注解——也可以看作是我们编写的注解,其他的都是他人编写注解
3. 自定义注解
JVM5.0定义了4个标准的元注解:
- @Target,
- @Retention,
- @Documented
- @Inherited
3.1 @Target
作用:用于描述注解的使用范围
取值ElementType有:
- CONSTRUCTOR:用于描述构造器
- FIELD:用于描述域
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- TYPE:用于描述类、接口(包括注解类型) 或enum声明
举例
@Target(ElementType.TYPE)
public @interface Table {
/**
* 数据表名称注解,默认值为类名称
* @return
*/
public String tableName() default "className";
}
@Target(ElementType.FIELD)
public @interface NoDBColumn {
}
注解Table可以用于注解类、接口(包括注解类型)或enum声明,而注解NoDBColumn仅用于注解类的成员变量。
3.2 @Retention
作用:用于描述注解的生命周期 ,取值RetentionPolicy有:
SOURCE:在源文件中有效(即源文件保留)
CLASS:在class文件中有效(即class保留)
RUNTIME:在运行时有效(即运行时保留)
举例:
@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的属性值是RUNTIME,这样注解处理器可以通过反射,获取到该注解的属性值,从而去做一些运行时的逻辑处理
3.3 Documented
作用:用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
举例:
@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;
}
3.4 @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类型被发现,或者到达类继承结构的顶层。
实例代码:
@Inherited
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}
3.5 自定义注解
1. 使用interface自定义注解,自动继承java.lang.annotation.Annotation接口。
2. 不能继承其他的注解或接口。
3. 每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(只能是基本类型、Class、String、enum)。可以通过default声明参数的默认值。
定义注解格式:
public @interface 注解名(定义体)
注解参数的可支持数据类型:
所有基本数据类型(int,float,boolean,byte,double,char,long,short)
1. String类型
2. Class类型
3. enum类型
4.Annotation类型
以上所有类型的数组
3.6 代码实践
注解类代码
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ElementType.FIELD,ElementType.METHOD})//定义注解的作用目标**作用范围字段、枚举的常量/方法
@Documented//说明该注解将被包含在javadoc中
public @interface FieldAnnotation {
String name() default "哈哈";
}
业务类
public class Student {
@FieldAnnotation(name="牛逼")
private String name;
private String id;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试类
public class StudentTest {
public static void main(String[] args) {
Field[] declaredFields = Student.class.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println(f.isAnnotationPresent(FieldAnnotation.class));
if (f.isAnnotationPresent(FieldAnnotation.class)) {
//获得注解的对象
FieldAnnotation fa = f.getAnnotation(FieldAnnotation.class);
if (fa != null) {
System.out.println(fa.name());
}
}
}
}
}