1、从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。【标签是对事物行为的某些角度的评价与解释,注解就如同一张标签。注解就是对应用程序的某一个部分的特殊说明,这个说明只针对关注这个说明的程序,如果其它程序不关注这个说明,那么这个注解对其无效也即对其没有意义。】
2、注解语法
通过@interface关键字进行定义【@interface 不是interface,是注解类 是jdk1.5之后加入的,java没有给它新的关键字,所以就用@interface 这么个东西表示了 这个注解类,就是定义一个可用的注解】
Java四大类型:
类 class
接口 interface
枚举 enum
注解 @interface
//定义注解
public @interface MyAnnotation {
}
//使用注解
@MyAnnotation
class Test{
}
3、元注解
元注解是可以注解到注解上的注解,或者说2元注解是一种基本注解,但是它能够应用到其它注解上面。【简单理解:元注解也是一张标签,但是它是一种特殊的标签,它的作用和目的就是给其它普通的标签进行解释说明的。】
①@Target
Target是目标的意思,@Target用于指定注解运用的地方
//定义注解
@Target(value= {ElementType.METHOD,ElementType.FIELD})
public @interface MyAnnotation {
}
//value取值有如下
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration (类,接口,枚举) */
TYPE,
/** Field declaration (includes enum constants) (属性)*/
FIELD,
/** Method declaration (方法) */
METHOD,
/** Formal parameter declaration (形参)*/
PARAMETER,
/** Constructor declaration (构造器)*/
CONSTRUCTOR,
/** Local variable declaration (局部变量) */
LOCAL_VARIABLE,
/** Annotation type declaration (注解)*/
ANNOTATION_TYPE,
/** Package declaration (包)*/
PACKAGE,
/**
* Type parameter declaration (类型参数)
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
②@Retention
retention为保留的意思。@Retention运用到某一个注解上的时候,它解释说明了这个注解的存活时间。
//定义注解
@Target(value= {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
/**
RetentionPolicy.SOURCE:注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视;
RetentionPolicy.CLASS:注解只保留到编译进行的时候,它将不会被加载到JVM中;
RetentionPolicy.RUNTIME:注解可以保留到程序运行的时候,它将会被加载到JVM中,所以在程序运行时可以获取到它们。
**/
③@Documented
这个注解用于指定被修饰的注解类将被javadoc工具提取成文档,这个注解只是用来标注生成javadoc的时候是否会被记录。
④@Inherited
Inherited是继承的意思,但是它不是说注解本身可以继承,而是说一个超类被@Inherited注解过的注解进行注解的话,那么它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
package com.cyl.reflect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//定义注解
@Target(value= {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {
}
//使用注解
@MyAnnotation
class Test{
}
//MyAnnotation标注了@Inherited,此时TestSon也拥有MyAnnotation注解
class TestSon extends Test{
}
//测试
package com.cyl.reflect;
import java.lang.annotation.Annotation;
public class TestDemo {
public static void main(String[] args) {
Class<?> obj = TestSon.class;
Annotation[] annotations = obj.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation.annotationType());//interface com.cyl.reflect.MyAnnotation
}
}
}
⑤@Repeatable
Repeatable是可重复的意思,@Repeatable是Java1.8才加进来的,通常是注解的值可以同时取多个的时候用到该元注解。
@interface User{
Role[] value();
}
/**
* 定义注解Role,里面有一个成员属性role,默认值为空串;
* @Repeatable注解了Role,而@Repeatable后面括号里的User类相当于一个容器注解,就是用来存放其它注解的地方,它本身也是一个注解。
**/
@Repeatable(User.class)
@interface Role{
String role() default "";
}
@Role(role="ddd")
@Role(role="ggg")
@Role(role="kkk")
//@User(value= {@Role(role="xxx"),@Role(role="yyy")})
class MyTest{
}
4、注解的属性
注解的属性也叫成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
//定义注解
@Target(value= {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {
//类型 属性名() default 默认值;不加默认值使用时必须给出属性值
//类型只能是 基本数据类型+String、注解类型
int id();
//Integer a();//语法错误
String name() default "cyl";
String[] b();
CylAnno cylAnno() default @CylAnno;
}
//注解CylAnno定义
@interface CylAnno{
}
//使用注解
@MyAnnotation(id=100,name="tom",b= {"a","b","c"})
class Test{
}
5、预置的注解
①限定父类重写方法:@Override
当子类重写父类方法时,子类可以加上这个注解,那这有什么什么用?这可以确保子类确实重写了父类的方法,避免出现低级错误。
②标示已过时:@Deprecated
这个注解用于表示某个程序元素类,方法等已过时,当其他程序使用已过时的类,方法时编译器会给出警告(删除线)。
③抑制编译器警告:@SuppressWarnings
被该注解修饰的元素以及该元素的所有子元素取消显示编译器警告。
④堆污染”警告与@SafeVarargs
⑤函数式接口与@Functionallnterface
函数式接口:接口中只有一个抽象方法(可以包含多个默认方法或多个static方法)。接口体内只能声明常量字段和抽象方法,并且被隐式声明为public,static,final。接口里面不能有私有的方法或变量。
@Functionallnterface这个注解保证这个接口只有一个抽象方法,注意这个只能修饰接口。
6、注解的提取
@Target(value= {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface AnnoA{
String name() default "tom";
}
@Target(value= {ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface AnnoB{
int[] arr();
}
@AnnoA
@AnnoB(arr= {1,2,3})
class UseAnnoTest{
@AnnoA(name="lucy")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class TestDemo {
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
Class<UseAnnoTest> clazz = UseAnnoTest.class;
//判断该类上是否有AnnoA注解
boolean annotationPresent = clazz.isAnnotationPresent(AnnoA.class);
//获取该类上AnnoA注解信息
AnnoA annoA = clazz.getAnnotation(AnnoA.class);
AnnoB declaredAnnotation = clazz.getDeclaredAnnotation(AnnoB.class);
//获取该类上标注的所有注解
Annotation[] annotations = clazz.getAnnotations();
System.out.println(annoA+"-->"+annoA.name());//@com.cyl.reflect.AnnoA(name=tom)-->tom
System.out.println(declaredAnnotation);//@com.cyl.reflect.AnnoB(arr=[1, 2, 3])
System.out.println(annotationPresent);//true
System.out.println(Arrays.asList(annotations));//[@com.cyl.reflect.AnnoA(name=tom), @com.cyl.reflect.AnnoB(arr=[1, 2, 3])]
Field field = clazz.getDeclaredField("name");
AnnoA declaredAnnotation2 = field.getDeclaredAnnotation(AnnoA.class);
System.out.println(declaredAnnotation2);//@com.cyl.reflect.AnnoA(name=lucy)
}
}