jdk1.5之后提供了注解(Annotation)这一种语法。其主要作用是编译检查(比如@override)和代码分析(通过代码中添加注解,利用注解解析器对添加了注解的代码进行分析,获取想要的结果,一般自定义的注解都是这一种功能)
一、JDK提供的常用注解(Override、Deprecated、SuppressWarnings)
- @Override
表示子类重写了父类的方法,或者实现了接口的方法。帮助开发者确认子类是否正确的覆盖了父类的方法,若父类中没有此方法,编译器即报错。但是,子类与父类有同样的方法,但子类的方法上没有@Override注解,是不会报错。
class MyThread implements Callable<Integer>{//实现callable接口的方法@Overridepublic Integer call() throws Exception {return 200;}
}
- @Deprecated
用于提示开发者,标注此注解的方法已经被弃用了。请使用另外推荐的方法
@Deprecatedpublic void call(){System.out.println("expired...");}
- @SuppressWarnings
抑制警告的意思。例如新建一个变量,但是没有用,编译器会提示此变量未使用的警告。如果在方法中,添加了@SuppressWarnings的相关注解,这个警告就不会再提示了。
@SuppressWarnings({"unused"})public static void main(String[] args){List<Integer>list=new ArrayList<>();}
完整架构:
二、自定义注解
除了java自带的注解,也可以自定义注解,用于帮助为相关代码打上标签,然后我们在解析注解的逻辑中就可以通过这些标签来完成相关的工作,比如,权限控制,日志等。
- 语法
1.定义一个自定义注解,与定义一个接口类似,只不过在interface前加是哪个@。
2.其内部可以添加属性值,其属性值的定义为:修饰符 返回值类型 属性名() [default value]其中,修饰符只能用public 和abstract。返回值为基本类型、字符串、枚举、注解以及以上类型的一维数组。
3.定义自定义注解,还需要用到元注解,用于修饰自定义注解,
一般我们会用到两个。@Retention和@Target。
@Retention
//用于确定注解的生命周期。其有三个枚举变量可选
public enum RetentionPolicy {/*** SOURCE级别表示代码级别可见,经过编译器编译生成字节码对象时,此注解就没了。* 比如@override就是代码级别可见*/SOURCE, /*** CLASS表示字节码对象级别可见,但是字节码对象被虚拟机加载时,* 这个注解会被抛弃,这是默认的可见级别*/CLASS,/*** RUNTIME表示运行时也可见,当虚拟机加载字节码对象时,此注解仍然可见。* 因此可以通过反射获取注解信息,然后完成相应的注解解析工作,一般自定义的注解都是运行时可见。*/RUNTIME
}
@Target
//用于修饰此注解可以用于什么类型上。比如注解可以用在类级别、方法、成员字段或者构造函数上。
public enum ElementType {/** 可以修饰类*/TYPE, /**可以修饰字段*/FIELD,/** 可以修饰方法*/METHOD,/** Formal parameter declaration */PARAMETER,/** 构造方法*/CONSTRUCTOR,LOCAL_VARIABLE,ANNOTATION_TYPE,PACKAGE,/*** Type parameter declaration** @since 1.8*/TYPE_PARAMETER,/*** Use of a type** @since 1.8*/TYPE_USE
}
- 简单的自定义注解例子
@Target({ElementType.METHOD,ElementType.TYPE})//这个注解可以修饰方法和类
@Retention(RetentionPolicy.RUNTIME)//可见范围到运行时都可见。
public @interface LogRecord {/** 下面时候注解的属性 **/ public String operationType() default "";
}
注意,注解的返回值只能是基本类型、Class、String、enum、Annotation 类型、以上所有类型的数组
- 完整的注解自定例子
上述定义的自定义注解,只是一个空的定义,没有任何的意义。
因此需要我们自己定义相关的自定义注解的解析。上面提到,自定义的注解需要定义注解的可见范围。一般我们都定义为运行时可见。因此,通过反射,我们可以拿到注解的内容。通过反射拿到代码的注解内容,进行相关的逻辑处理工作,以达到注解的目的。
//通过反射获得注解内容的常用方法有:
T getAnnotation(Class) : 获得当前对象的指定的注解。
Annotation[] getAnnotations() X: 获得当前对象的所有注解
boolean isAnnotationPresent(annotationClass): 当前对象是否有注解。
球队注解:
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface FootBallTeam {public String value() default "Guangzhou Evergrande Taobao Football Club";
}
public class testAnnotation {public static void main(String[] args) {testAnnotation test = new testAnnotation();test.getInformation(Team.class);}public void getInformation(Class<?> clazz) {//因为注解是在成员字段上,因此需要获得类的所有字段信息Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {//判断这个字段上是否有相应的注解信息(FootBallTeam.class)if (field.isAnnotationPresent(FootBallTeam.class)) {FootBallTeam footBallTeam = field.getAnnotation(FootBallTeam.class);System.out.println("球队名字是" + footBallTeam.value());}}}
}
class Team{@FootBallTeam("Real Madrid")private String teamName;
}
在spring中大量应用了注解结合反射机制,实现ioc、aop等功能,更多详解看大佬文章。