注解是为程序元素设置元数据的。本质上是一个接口。
注解起的是标记作用,可以用来标记包、类、构造器、方法、成员变量、参数、局部变量。
但是,注解不会影响程序代码的执行。
如果想让注释在程序中起作用,需要配套的注释工具ATP(Annotation Processing Tool)
学Java的人,对@Override一定不陌生,当我们实现一个接口后,编译器自动添加的方法,上边都有这个注解。如:
public class OverrideTest implements father{ @Override public void m1() { // TODO 自动生成的方法存根 } } interface father { public void m1(); }告诉编译器,下边的方法是重写\实现的方法,让编译器检查有没有拼写错误。好处是:避免低级错误,有的编译器里边 o、O、0 还有 1、I、l 基本没区别,当我们想重写父类方法,却因为这种“小”问题出错,而且这种“小”问题,排错非常非常难!!(教训过于惨痛,不举例说明了。。。)
图示: 代码
public class DeprecatedTest { public static void main(String[] args) { // 使用过时方法会被警告 new Apple().info(); } } class Apple{ // 定义 info 方法,并设置过时 @Deprecated public void info() { System.out.println("Apple 的info方法"); } }图示: 代码
import java.util.ArrayList; import java.util.List; public class SuppressWarningsTest { // 抑制没有进行类型检查操作的警告 @SuppressWarnings(value="unchecked") public static void main(String[] args) { // 使用 generics(泛型) 时忽略没有指定相应的类型 @SuppressWarnings("rawtypes") List myList = new ArrayList(); myList.add(""); } }它代表着此注解会被javadoc工具提取成文档。
允许子类继承父类中的注解。
点开我们常见的 @Override看看 发现结构只有两个:
元注解注解体我们模仿上边的注解,写个注解试试。
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface MyOverridn { }好像有点看不太懂,反编译一下,看看具体是什么。 反编译后生成的代码:
public class annotation.test.OverrideTest implements annotation.test.father { public annotation.test.OverrideTest(); public void m1(); }这下就可以看出来了。本质上就是一个类,去实现了一个接口
我们上边已经说过,注解就是一个标签,那么我们这样做个 标签,它又不会影响代码执行,那注解有什么用?
想让注解发挥更大的作用,需要用到反射!
反射可以拿到注解标记的元素,方法如下。
返回值方法及详解<T extends Annotation>getAnnotation(Class annotationClass)T如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。Annotation[]getAnnotations()返回此元素上存在的所有注释。Annotation[]getDeclaredAnnotations()返回直接存在于此元素上的所有注释。booleanisAnnotationPresent(Class<? extends Annotation> annotationClass)如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。学过框架或者反射的人,应该都听“过框架就是注解+反射”、“反射是框架的灵魂”这些话,下边咱们就做2个小“框架”,来练练手。
一个类的方法有很多,平常咱们想用哪个就调那个,现在,咱们要用自定义注解去标识一下,然后就让这些被标示的方法跑起来。并且记录一下异常出现的次数。 Testable——自定义注解
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; // 指定 Testable 注释可以保留多久 @Retention(RetentionPolicy.RUNTIME) // 指定 Testable 注释能修饰的目标(只能是方法) @Target(ElementType.METHOD) public @interface Testable { }MyTest——有需要运行方法的类
public class MyTest { @Testable public static void m1() { } public static void m2() { } @Testable public static void m3() { throw new RuntimeException("Boom"); } public static void m4() { } @Testable public static void m5() { } public static void m6() { } @Testable public static void m7() { throw new RuntimeException("Crash"); } public static void m8() { } }TestProcessor——使用反射加载被标识方法,并运行的核心列
import java.lang.reflect.Method; public class TestProcessor { public static void process(Class<MyTest> class1) throws SecurityException, ClassNotFoundException { int passed = 0; int failed = 0; for (Method m : class1.getMethods()) { if (m.isAnnotationPresent(Testable.class)) { try { m.invoke(null); passed++; } catch (Exception e) { System.out.println("方法"+m+"运行失败,异常:"+e.getCause()+"\n"); failed++; } } } System.out.println("共运行了:"+(passed+failed)+"个方法,其中\n"+ "失败了:"+failed+"个,\n"+ "成功了:"+passed+"个,\n" ); } }RunTests——调用核心类
public class RunTests { public static void main(String[] args) throws Exception { TestProcessor.process(MyTest.class); } }运行结果
方法public static void annotation.useAnnotation.MyTest.m7()运行失败,异常:java.lang.RuntimeException: Crash 方法public static void annotation.useAnnotation.MyTest.m3()运行失败,异常:java.lang.RuntimeException: Boom 共运行了:4个方法,其中 失败了:2个, 成功了:2个,学习JavaSE,做小游戏,相信按钮监听让很多人心烦,每个按钮都需要一个个添加监听,一个个写事件处理,不如只用一个注解,给添加得了 ActionListenerFor——自定义注解
import java.awt.event.ActionListener; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ActionListenerFor { // 定义一个成员变量,用于保存监听器实现类 Class<? extends ActionListener> listener(); }ActionListenerInstaller——核心处理类
import java.awt.event.ActionListener; import java.lang.reflect.Field; import javax.swing.AbstractButton; public class ActionListenerInstaller { // 处理注释的方法,其中 obj 是包含注释的对象 public static void processAnnotations(Object obj) { try { // 获取 obj 对象的类 Class cl = obj.getClass(); // 获取指定 obj 对象的所有成员变量,并遍历每个成员变量 for (Field f : cl.getDeclaredFields()) { // 将成员变量设置为可自由访问 f.setAccessible(true); // 获取成员变量上 ActionListenerFor 类型的Annotation ActionListenerFor a = f.getAnnotation(ActionListenerFor.class); // 获取成员变量 f 的值 Object fObj = f.get(obj); // 如果 f 是 AbstractButton 的实例,且 a 不为 null if(a != null && fObj != null && fObj instanceof AbstractButton) { // 获取 a 注解里的 listener 源数据(它是一个监听类) Class<? extends ActionListener > listennerClazz = a.listener(); // 使用反射创建 listener 类的对象 ActionListener al = listennerClazz.newInstance(); AbstractButton ab = (AbstractButton)fObj; // 为 ab 按钮添加事件监听器 ab.addActionListener(al); } } } catch (Exception e) { e.printStackTrace(); } } }AnnotationTest——需要添加按钮的类(以及按钮点击事件的处理类)
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; public class AnnotationTest { private JFrame mainWin = new JFrame("使用注解绑定事件监听器"); // 使用注解为 ok 按钮绑定事件监听 @ActionListenerFor(listener=OkListener.class) private JButton ok = new JButton("确定"); // 使用注解为cancel 按钮绑定事件监听 @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(); } } // 定义 ok 按钮的事件监听器实现类 class OkListener implements ActionListener{ @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null, "单击了确认按钮"); } } // 定义 cancel 按钮的事件监听器实现类 class CancelListener implements ActionListener{ @Override public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null, "单击了取消按钮"); } }效果
学到这里,有些问题,在你心里边应该有个大致答案了。 比如: Spring的注解@Table——怎么把对象映射到数据库的数据表?? SpringMVC的注解@Autowired——怎么实现自动注入??
让你自己实现类似功能,你是否能实现?