Annotation是代码里的特殊标记,这些标记可以在编译,类加载、运行时被读取,并执行相应的处理。通过使用注解,程序开发人员可以通过这些补充信息进行验证或者进行部署。
Annotation就像修饰符一样,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在Annotation的"name=value"对中。
Annotation能被用来为程序元素(类、方法、成员变量)设置元数据。Annotation不影响程序代码的执行,无论是删除、增加Annotation。
基本Annotation
@Override限定重写父类方法
@Deprecated标记已过时
@SuppressWarnings(name="value")抑制编译器警告
@SafeVarargs抑制堆污染警告
堆污染是指把一个不带泛型的对象赋给一个泛型的常量
@FunctionalInterface指定某个接口必须是函数式接口(只能修饰接口)
根据Annotation是否包含成员变量,将Annotation分为如下两类:
1、标记Annotation,没有定义成员变量的Annotation类型被称为标记。
2、元数据Annotation,包含成员变量,可以接收更多的元数据。
元Annotation
@Retention指定被修饰的Annotation可以保留多长时间
@Target执行被修饰的Annotation用于修饰哪些程序单元
@Documented指定被修饰的Annotation类将被javadoc工具提取成文档,如果定义Annotation类时使用了@Documented修饰,则使用该Annotation修饰的程序元素的API文档将会包含该Annotation说明。
@Inherited指定被修饰的Annotation具有继承性,如果某个类使用了@Inheritable修饰,则该类的子类自动使用@Inheritable修饰。
自定义Annotation:
public @interface MyTag{
String name() default "yeeku";
int age() default 32;
}
提取Annotation信息:
java.lang.reflect包下包含实现反射功能的工具类,也增加了读取运行时Annotation的能力。只有当定义Annotation时使用了@Retention(RetentionPolicy.RUNTIME)修饰,该@Retention才会在运行时可见,JVM才会在装在*.class文件时读取保存在class文件中的Annotation。
Class<?> clazz = Class.forName("annotation.case5.Test");
Annotation[] aArrays = clazz.getMethod("info").getAnnotations();
for(Annotation an:aArrays){
System.out.println(an);
}
for(Annotation tag:aArrays){
if(tag instanceof MyTag){
System.out.println(((MyTag) tag).name());
System.out.println(((MyTag) tag).age());
}
}
使用Annotation:
为了让Annotation起作用,要为注解提供一个注解处理工具,如果目标类使用了注解修饰,就通过反射来运行测试方法。
//定义一个标记注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Testable {
}
//使用@Testable来标记这些方法是可测试的
public class MyTest {
@Testable
public static void m1(){
}
public static void m2(){
}
@Testable
public static void m3(){
throw new IllegalArgumentException("参数出错了!");
}
public static void m4(){
}
@Testable
public static void m5(){
}
public static void m6(){
}
}
//为注解提供一个注解处理工具
public class ProcessorTest {
public static void process(String clazz) throws ClassNotFoundException{
int passed=0;
int failed=0;
for (Method m : Class.forName(clazz).getMethods()) {
if(m.isAnnotationPresent(Testable.class)){
try {
m.invoke(null);
passed++;
} catch (Exception e) {
// TODO: handle exception
System.out.println("方法"+m+"运行失败,异常:"+e.getCause());
failed++;
}
}
}
System.out.println("共运行了:"+(passed+failed)+"个方法,其中:\n"+"失败了:"+failed+"个,成功了:"+passed+"个!");
}
}
//测试主类
public class RunTests {
public static void main(String[] args) throws ClassNotFoundException {
// TODO Auto-generated stub
ProcessorTest.process("annotatiomethodn.MyTest");
}
}
其实Annotation就是对源代码增加一些特殊标记,这些标记可通过反射获取,当程序获取这些特殊标记后,程序可以做出相应的处理。
//为程序绑定事件监听器
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionListenerFor {
Class<? extends ActionListener> listener();
}
//测试类
<pre name="code" class="java">public class AnnotationTest {
private JFrame mainWin=new JFrame("使用注解绑定事件监听器");
@ActionListenerFor(listener=OkListener.class)
private JButton ok=new JButton("确定");
@ActionListenerFor(listener=CancelListener.class)
private JButton cancel=new JButton("取消");
public void init() {
JPanel jp=new JPanel();
jp.add(ok);
jp.add(cancel);
mainWin.add(jp);
ActionListenerInstaller.processAnnotations(this);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.pack();
mainWin.setVisible(true);
}
public static void main(String[] args){
new AnnotationTest().init();
}
}
class OkListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JOptionPane.showMessageDialog(null, "单机了确认按钮");
}
}
class CancelListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
JOptionPane.showMessageDialog(null, "单机了取消按钮");
}
}
//注解处理工具
public class ActionListenerInstaller {
public static void processAnnotations(Object object){
Class<? extends Object> c1=object.getClass();
System.out.println(c1.getDeclaredFields().length);
try {
for (Field f : c1.getDeclaredFields()) {
f.setAccessible(true);
ActionListenerFor a = f.getAnnotation(ActionListenerFor.class);
Object fObject=f.get(object);
if(a!=null&&fObject!=null&&fObject instanceof AbstractButton){
Class<? extends ActionListener> listenerClazz=a.listener();
ActionListener a1=listenerClazz.newInstance();
AbstractButton ab=(AbstractButton) fObject;
ab.addActionListener(a1);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
java8新增了重复注解,允许使用多个相同类型的Annotation来修饰同一个类。
为了使注解改造成重复注解,需要使用@Repeatable来修饰该注解,还得创造一个容器注解来包含这个value();为了获取一个类中的多个Annotation注解,可以使用java8新增的getDeclaredAnnotationByType()方法:
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(FkTags.class)
@Target(ElementType.TYPE)
public @interface FkTag {
String name() default "疯狂软件";
int age();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FkTags {
FkTag[] value();
}
@FkTag(age=5)
@FkTag(name="疯狂java",age=9)
public class FkTagTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Class<FkTagTest> clazz=FkTagTest.class;
FkTag[] tags=clazz.getDeclaredAnnotationsByType(FkTag.class);
for (FkTag fkTag : tags) {
System.out.println(fkTag.name()+"-->"+fkTag.age());
}
FkTags container = clazz.getDeclaredAnnotation(FkTags.class);
System.out.println(container);
}
}
APT(Annotation Processing Tool)是一种注解处理工具,对源代码文件进行检测,并找出源文件所包含的Annotation信息,针对Annotation信息进行额外的处理。例如通过注解可以在java源文件中放置一些Annotation,使用APT工具就可以生成另一份XML文件。