注解Annotation
1、什么是注解
Java中有五大引用数据类型:类、接口、枚举、数组、注解。
Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。
Annotation其实就是代码里的特殊标记, 它用于替代配置文件,也就是说,传统方式通过配置文件告诉类如何运行,有了注解技术后,开发人员可以通过注解告诉类如何运行。在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
Java为我们提供了三个基本的Annotation:
- @Override:限定重写父类方法, 该注解只能用于方法;
- @Deprecated:用于表示某个程序元素(类, 方法等)已过时;
- @SuppressWarnings:抑制编译器警告;
2、自定义注解
(1)自定义注解介绍
自定义新的 Annotation 类型使用@interface关键字 ,并在注解中声明属性。注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述。
声明属性的方法:
- Annotation的属性声明方式:String name();
- 属性默认值声明方式:String name() default “张三”;
- 特殊属性value:如果注解中有一个名称value的属性,那么使用注解时可以省略value=部分,如@MyAnnotation(“xxx");
- 特殊属性value[];
注解中的属性可以使以下几种类型;
- String类型
- 基本数据类型
- Class类型
- 枚举类型
- 注解类型
- 以上类型的一维数组
(2)自定义注解以及使用
自定义注解@MyAnnotation_1:
package StuAnnotation;
public @interface MyAnnotation_1 {
String value();
}
自定义注解@MyAnnotation:
package StuAnnotation;
public @interface MyAnnotation {
String name();
int age() default 20;
/*
Class<?> clazz();
Gender gender();
MyAnnotation_1 MY_ANNOTATION_1();
String[] value();
*/
}
我们使用反编译工具查看注解的字节码文件发现,其实注解Annotation只一个特殊的接口,我们自己定义的注解实际上是继承了继承了Annotation接口。
MyAnnotation反编译后的文件:
package StuAnnotation;
import java.lang.annotation.Annotation;
public interface MyAnnotation
extends Annotation
{
public abstract String name();
public abstract int age();
}
我们已经定义好了一个注解,那么我们如何使用呢。
我们可已通过反射来使用注解:
package StuAnnotation;
public class Student {
private String name;
private int age;
@MyAnnotation(name = "suxing",age = 18)
public static void info(String name,int age) throws Exception{
System.out.println("name = "+name);
System.out.println("age = "+age);
}
}
定义一个Student类,在该类的info方法上使用我们自定义的注解,在注解中给出各参数的值。
测试:
package StuAnnotation;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) throws Exception{
Class<Student> stuClazz = Student.class;
Method method = stuClazz.getMethod("info",String.class,int.class);
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
method.invoke(stuClazz,annotation.name(),annotation.age());
}
}
我们在测试类中使用反射获取Student类的Class类对象,然后获取Student的info()方法的Method类对象,最后通过getAnnotation()方法获取该方法上的注解,并获取注解上的参数值,最后调用该方法并传入各个参数。
但是这样直接运行会出现空指针异常,我们需要在我们自定义的注解@MyAnnotation上添加一个@Retention注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
@Retention注解是一个元注解,用于告诉JVM我们自定义的注解的保留政策,即我们的注解需要在什么级别保留该注解信息,用于描述注解的生命周期。
添加该元注解后运行程序:
name = suxing
age = 18
由结果看出,我们已经在自定义注解修饰的方法中拿到了注解中的值。
3、元注解
元注解指的是修饰用于修饰Annotation的Annotation。
JDK为我们提供了四个元注解:
- @Retention
- @Target
- @Documented
- @Inherited
@Retention:
只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域, @Rentention 包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。
- RetentionPolicy.CLASS: 编译器将把注解记录在 class文件中. 当运行 Java 程序时, JVM 不会保留注解。即源文件保留,这是默认值
- RetentionPolicy.RUNTIME:编译器将把注解记录在 class文件中. 当运行 Java 程序时, JVM 会保留注解.。程序可以通过反射获取该注释 ,即运行时保留
- RetentionPolicy.SOURCE: 编译器直接丢弃这种策略的注释即源文件保留。
@Target:
指定注解用于修饰类的哪个成员。@Target 包含了一个名为value,类型为ElementType的成员变量。
ElementType类源码:
package java.lang.annotation;
/**
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 4.1 The Kinds of Types and Values
*/
public enum ElementType {
TYPE,
FIELD,
METHOD,
PARAMETER,
CONSTRUCTOR,
LOCAL_VARIABLE,
ANNOTATION_TYPE,
PACKAGE,
TYPE_PARAMETER,
TYPE_USE
}
@Documented:
用于指定被该元 Annotation 修饰的Annotation类将被 javadoc 工具提取成文档。
@Inherited:
被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的Annotation,则其子类将自动具有该注解。