什么是注解
注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。《Thinking in Java》
怎么理解呢,注解好比一个职业的名称,比如老师这个名称类似一个注解,当我们听到老师这个职称,我们就知道他会教书育人;他是一个律师,我们的第一反应就是他很懂法律知识,注解类似一个标签,他告诉我们这个方法或者类有什么特征之类的,使我们更清楚的了解类或方法。
我们常见的注解,@Override表示这个方法重写了父类的方法;@Deprecated表示这个方法或者类过时了,不建议使用;@Test表示这个方法是测试方法。
如何定义注解
注解和类,接口一样,需要关键字@interface来定义。是的你没看错,和定义接口的interface差不多,只是在interface前面加了个@。
public @interface MyAnnotation {//定义注解}

我们查看这个注解的字节码文件,发现定义的注解实际上实现了Annotation接口。
public interface Annotation {//Annotation接口源码boolean equals(Object obj);int hashCode();String toString();/***获取注解类型*/Class<? extends Annotation> annotationType();}
那我们能不用@interface关键字,自己写一个接口继承Annotation接口呢,答案是可以的 ```java public interface MyAnnotation2 extends Annotation {
}
<br />发现编译后的字节码文件 一个是@interface定义的,一个是interface定义的,其他事一模一样的。- 当我们在使用的时候,发现直接用接口继承Annotation接口的MyAnnotation2是无法用@在方法上使用的,编译会报错。<br />**所以我们自定义注解的时候,肯定是@interface来定义注解。**<br />当我们定义一个注解的时候,里面并没有写任何的代码(即不含任何元素),这个注解就是个标记注解,如@Test,@Override等等。<a name="mQBHP"></a>### 元注解元注解其实就是**注解的注解**,在注解中使用元注解,更好的帮我们开发想要的功能代码。(PS: 定义注解的属性用括号结尾)<br />以JDK1.8为例,有五种元注解,如下:- **@Target**表示注解的可以用于什么地方,可选参数是枚举ElementType中的属性```javapublic enum ElementType {TYPE, //类,接口,枚举,注解的声明FIELD,//域(属性字段或枚举常量)的声明METHOD,//方法的声明PARAMETER,//方法参数的声明CONSTRUCTOR,//构造函数的声明LOCAL_VARIABLE,//局部变量的声明ANNOTATION_TYPE,//注解的声明PACKAGE,//包的声明TYPE_PARAMETER,//泛型的声明TYPE_USE//此类型包括类型声明和类型参数声明}
例子:
@Target(ElementType.CONSTRUCTOR)public @interface MyAnnotation {//表示该注解作用于构造函数}
一般使用的最多的是@Target(ElementType.TYPE)
- @Retention
 
表示注解的保留方式,可选参数是枚举RetentionPolicy中的属性
public enum RetentionPolicy {SOURCE,//表示注解会在编译时被丢弃CLASS,//默认策略,表示注解在class文件中可用,但是在运行时,不会被VM保留RUNTIME//表示不仅会在编译后的class文件中存在,而且在运行时保留,因此它们主要用于反射场景,可以通过getAnnotation方法获取注解信息}
例子:
首先定义三种保留方式的注解
//存在于class文件中,会被VM丢弃@Target(ElementType.TYPE)@Retention(RetentionPolicy.CLASS)public @interface MyAnnotation {String str();}//在运行时保留@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface MyAnnotation2 {String str();}//在编译时被丢弃@Target(ElementType.TYPE)@Retention(RetentionPolicy.SOURCE)public @interface MyAnnotation3 {String str();}
接下来写下测试方法,看结果
@MyAnnotation(str="1")@MyAnnotation2(str="2")@MyAnnotation3(str="3")public class test {public static void main(String[] args) {Annotation[] annotations = test.class.getAnnotations();for (Annotation annotation :annotations){System.out.println("保留的注解:"+annotation.toString());}//输出: 保留的注解:@demo.a5.MyAnnotation2(str=2)}}
使用反射,我们可以得到运行时的注解属性,从输出结果看到只有@MyAnnotation2注解的属性输出了,因为它的元注解@Retention参数是RetentionPolicy.RUNTIME
- @Documented
 
此注解表示,将修饰的注解包含在Javadoc中,Javadoc工具会将此注解标记元素的注解信息包含在javadoc中。默认,注解信息不会包含在Javadoc中
- @Inherited
 
此注解表示,修饰的注解允许子类继承父类(PS:此注解只对注解标记的超类有效,对接口是无效的。)
@Inherited注解标记的注解,在使用时,如果父类和子类都使用的注解是同一个,那么子类的注解会覆盖父类的注解
例子:
@Inherited@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface MyAnnotation {String str();}@MyAnnotation(str = "222")public class Father {}public class Son extends Father {}public class demo {public static void main(String[] args){son son = new son();Annotation[] annotations = son.class.getAnnotations();for (Annotation annotation :annotations){System.out.println("保留的注解:"+annotation.toString());}//输出:保留的注解:@demo.a5.mytest(str=222)}}
从结果可以看出,子类能获取到父类注解的属性。
- @Repeatable(JDK1.8加入)
 
此注解表示,标记的注解可以多次应用于相同的声明或类型
例子:
@Repeatable(MyAnnotation2.class)@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface MyAnnotation{String value() default "";}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface MyAnnotation2{MyAnnotation[] value();}@MyAnnotation(value="1")@MyAnnotation(value="11")@MyAnnotation(value="111")public class test {public static void main(String[] args) {Annotation[] annotations = test.class.getAnnotations();for (Annotation annotation :annotations){System.out.println("保留的注解:"+annotation.toString());}//输出:保留的注解:@demo.a5.MyAnnotation2(value=[@demo.a5.MyAnnotation(value=1), @demo.a5.MyAnnotation(value=11), @demo.a5.MyAnnotation(value=111)])}}
从结果可以看到,注解中的1,11,111都输出了,表示该注解可以多次应用于相同的声明或类型
自定义注解的使用

因为元注解@Retention(RetentionPolicy.RUNTIME)使我们能够用反射的方法,拿到注解的属性值,这样我们可以利用注解做很多事情,贯穿到我们的业务中去。
我们看以下例子,具体了解,怎么通过反射获取的。
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface People {//定义注解String name() default "";int age() default 10;}public class Student {//定义Student类private String info;public String getInfo() {return info;}public void setInfo(String info) {this.info = info;}@Override@People(name="小张", age = 18)public String toString() {return "Student{" +"info='" + info + '\'' +'}';}}//编写测试类public class test {public static void main(String[] args) {getStudentInfo(Student.class);//参数为Studen类的class对象}public static void getStudentInfo(Class<?> clazz){Method[] methods = clazz.getMethods();//通过反射获取Student类的所有方法for (Method method : methods) {//便利所有方法if (method.isAnnotationPresent(People.class)) {//判断注解是否为People注解People annotation = method.getAnnotation(People.class);//获取该方法上的注解System.out.println("姓名:"+annotation.name() + ",年龄:"+ annotation.age());//输出该注解的属性信息}}}}
注解的作用
- 灵活使用注解,穿插到我们的业务代码中去,能够大大的提高我们的开发效率
 - 利用注解,帮我们生成Javadoc文档
 - 利用注解类型检测能力,在代码编译前帮我们排错
 
