Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
XML vs Annotation
假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来。
注解本身是没有功能的,和XML一样,都是一种元数据,即解释数据的数据。
Java SE5内置三种注解
@Override
它的作用是对覆盖超类中方法的方法进行标记,如果被标记的方法并没有实际覆盖超类中的方法,则编译器会发出错误警告。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
@Deprecated
当我们希望编译器知道某一方法不建议使用时(编译器会画横线),我们应该使用这个注解。Java在javadoc 中推荐使用该注解,我们应该提供为什么该方法不推荐使用以及替代的方法。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
@SuppressWarnings
这个仅仅是告诉编译器忽略特定的警告信息,例如在泛型中使用原生数据类型。它的保留策略是SOURCE(译者注:在源文件中有效)并且被编译器丢弃。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
其参数有:
deprecation,使用了过时的类或方法时的警告
unchecked,执行了未检查的转换时的警告
fallthrough,当 switch 程序块直接通往下一种情况而没有 break 时的警告
path,在类路径、源文件路径等中有不存在的路径时的警告
serial,当在可序列化的类上缺少serialVersionUID 定义时的警告
finally ,任何 finally 子句不能正常完成时的警告
all,关于以上所有情况的警告
举例:
可以展示不同场景下的应用。
@Override @MethodInfo(author = "aa", comments = "Main method", date = "Nov 17 2012", revision = 1) public String toString() { return "Overriden toString method"; } @Deprecated @MethodInfo(comments = "deprecated method", date = "Nov 17 2012") public static void oldMethod() { System.out.println("old method, don't use it."); }
//若去掉"deprecation"则oldMethod()会被画横线 @SuppressWarnings({ "unchecked", "deprecation" }) @MethodInfo(author = "Pankaj", comments = "Main method", date = "Nov 17 2012", revision = 10) public static void genericsTest() throws FileNotFoundException { List l = new ArrayList(); l.add("abc"); oldMethod(); } @Documented @Target(ElementType.METHOD) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface MethodInfo{ String author() default "Pankaj"; String date(); int revision() default 1; String comments(); }
四种元注解
@Target
表示该注解可以用于什么地方
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE)//用于注解类型 public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ ElementType[] value(); }
ElementType.TYPE:用于描述类、接口或enum声明
ElementType.FIELD:字段声明(包括枚举常量)
ElementType.METHOD:方法声明
ElementType.PARAMETER:形参声明
ElementType.CONSTRUCTOR:构造函数声明
ElementType.LOCAL_VARIABLE:局部变量声明
ElementType.ANNOTATION_TYPE:注解类型声明
ElementType.PACKAGE:包声明
@Retention
表示什么时候使用该注解
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE)//用于注解类型 public @interface Retention { /** * Returns the retention policy. * @return the retention policy */ RetentionPolicy value(); }
RetentionPolicy.SOURCE–在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
RetentionPolicy.CLASS–在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。
RetentionPolicy.RUNTIME–始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Documented
注解是否将包含在JavaDoc中
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE)//用于注解类型 public @interface Documented { }
@Inherited
是否允许子类继承该注解。使用注解@Inherited可以让指定的注解在某个类上使用后,这个类的子类将自动被该注解标记。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE)//用于注解类型 public @interface Inherited { }
创建Java自定义注解
创建自定义注解和创建一个接口相似,但是注解的interface关键字需要以@符号开头。我们可以为注解声明方法。
@Documented @Target(ElementType.METHOD) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface MethodInfo{ String author() default "Pankaj"; String date(); int revision() default 1; String comments();
注解方法不能带有参数;
注解方法返回值类型限定为:基本类型、String、Enums、Annotation或者是这些类型的数组;
注解方法可以有默认值;
注解本身能够包含元注解,元注解被用来注解其它注解。
Java注解解析
使用反射技术来解析java类的注解。那么注解的RetentionPolicy应该设置为RUNTIME否则java类的注解信息在执行过程中将不可用那么我们也不能从中得到任何和注解有关的数据。
示例出处:http://ifeve.com/java-annotations/
public class Main { public static void main(String[] args) { System.out.println("Hello World!"); try { for (Method method : Main.class .getClassLoader() .loadClass(("Main")) .getMethods()) { // checks if MethodInfo annotation is present for the method if (method.isAnnotationPresent(MethodInfo.class)) { try { // iterates all the annotations available in the method for (Annotation anno : method.getDeclaredAnnotations()) { System.out.println("Annotation in Method "+ method + " : " + anno); } MethodInfo methodAnno = method.getAnnotation(MethodInfo.class); if (methodAnno.revision() == 1) { System.out.println("Method with revision no 1 = "+ method); } } catch (Throwable ex) { ex.printStackTrace(); } } } } catch (SecurityException | ClassNotFoundException e) { e.printStackTrace(); } }
}
输出(文件中包含上文举例中的方法声明):
Annotation in Method public java.lang.String Main.toString() : @Main$MethodInfo(author=aa, revision=1, comments=Main method, date=Nov 17 2012) Method with revision no 1 = public java.lang.String Main.toString() Annotation in Method public static void Main.oldMethod() : @java.lang.Deprecated() Annotation in Method public static void Main.oldMethod() : @Main$MethodInfo(author=Pankaj, revision=1, comments=deprecated method, date=Nov 17 2012) Method with revision no 1 = public static void Main.oldMethod() Annotation in Method public static void Main.genericsTest() throws java.io.FileNotFoundException : @Main$MethodInfo(author=Pankaj, revision=10, comments=Main method, date=Nov 17 2012)