Java 1.5新增的一项特性-注解。注解也称为元数据,为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便的使用这些数据。注解不直接对源代码产生作用,源码会直接忽略注解。注解的用处在于其他地方:注解可以帮助编译器发现错误,消除警告;在编译期或者部署时,可以通过注解生成代码或其他文件;注解还可以在运行时发挥作用。注解的最广泛的应用是减少配置文件的编写,Java主流的框架都依赖配置文件运行,程序员为了实现某个功能,要写一份业务代码(也就是Java代码),同时还要在另一个文件编写配置信息(通常是xml)。注解的出现提供了一种将业务代码与配置代码统一的方式,减少了编写累赘的配置文件,已经极大的影响了程序员的编码方式。
注解的使用
注解可以用来标注类,方法,域,参数,局部变量等,注解的使用也很简单,除了@符号外,其他的跟Java的基本语法一致:
@Override
public void fromBase() { ..... }
注解一般会包含一些元素以表示某些值:
@Auther (auther = "cxy")
public class MyAnnotaion { .... }
没有元素的注解称为标注注解,如果一个注解只有唯一的元素,且这个元素名为value,那么在使用注解的时候可以省略元素名,如@SuppressWarning只有一个元素value,那么在使用@SuppressWarnings来消除警告时,就可以省略value了:
@SuppressWarings("unchecked")
public void method() { ...... }
在Java中,可以对同一个目标多次使用同一个注解,这叫做重复注解(Repeating Annotation),注意这是Java8新增的特性:
@Auther( auther = "cxy" )
@Auther( auther = "yhy" )
pubic class MyAnnoation { ...... }
预定义注解和元注解
Java SE内置了5种注解,定义在java.lang中:
@Override,表示当前的方法定义将覆盖超类中的方法,如果方法签名不正确,编译器可以根据该注解发出错误提示,标注注解;
@Deprecated,标记目标已经过时,如果程序员使用了注解为@Deprecated的元素,编译器会发出警告信息;
@SuprressWarnings,关闭不当的编译器警告信息,@SuppressWarnings只有一个名为value的元素,故在使用时可以忽略元素名,value是String[]类型,可以传一字符串数组,如@SuppressWarnings( { "unchecked","deprecated" } )
@SafeVarargs,表示标注的构造方法或者方法不会在参数上执行潜在的不安全的操作,只能标注构造方法或者方法,Java 1.7新增;
@FunctionalInterface Java 8新增;
以上的5中注解是Java给我们定义好的,可以直接使用,但是这远远不够啊,大部分时间还是需要我们自己定义,Java为我们提供了4中元注解,元注解就是用来创建其他注解的注解,且来看:
@Target,表示注解可以用于什么地方,元素由枚举类ElementType提供,包括
ElementType.ANNOTATION_TYPE -
可用于注解类型ElementType.CONSTRUCTOR
- 可以用于构造方法ElementType.FIELD
- 用于域,或者叫做属性,或者叫做成员变量ElementType.LOCAL_VARIABLE
- 局部变量声明ElementType.METHOD -
用于方法声明ElementType.PACKAGE
- 用于包声明ElementType.PARAMETER
- 用于参数声明ElementType.TYPE -
类,接口(包括注解类型)或enum声明
@Retention,表示在什么级别保存该注解信息,元素有枚举类RetentionPolicy提供:
RetentionPolicy.SOURCE
– 源码级别,注解将被编译器丢弃RetentionPolicy.CLASS
– 注解在class文件中可用,但会被VM丢弃RetentionPolicy.RUNTIME
– VM运行时也会保留注解,可以通过反射机制读取注解信息
@Inherited,允许子类继承父类的注解,默认不继承
在Java 8中又提供了一个新的元注解@Repeatable,表示该注解可以多次用于同一目标。
自定义注解
注解其实是一种接口,所以注解的定义看上去跟接口差不多,只是在interface前面多了@,
@Target(ElementType.METHOD,ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auther {
//注解的元素只能是public或者default
public String auther() default "cxy";
String date()
}
在注解的元素中可以使用默认值,在使用该注解时,如果没有对该元素赋值,那么它就是默认值。在使用默认值时,元素不能有不确定的值,而在定义元素默认值时,不能以null做为其值。用于注解的元素也有限制:
- 基本类型,int,double等,不包括包装类型
- String
- Class
- enum
- Annotaion
- 以上类型的数组
//auther有默认值,可以不用显式赋值
@Auther(date = "2014-08-07")
public void f() { ...... }
//date没有默认值,必须赋值
@Auther(auther = "yhy",date = "2014-08-07")
public void other() { ...... }
有一点需要注意的是,注解中的所有元素都必须有值,要么在定义注解接口的时候给出默认值,要么在使用的时候显式的赋值。
使用注解
有了注解,如果无法使用,那么它不会比注释更有用。Java提供了一个类AnnotationElement用于反射的获取注解信息,所以它只能处理@Retention为RUMTIME的注解,这个接口有四个方法,分别是:
<T extends Annotation> T getAnnotation(Class<T> annotationClass):返回指定类型的注解
Annotation[] Annotation[] getAnnotations():返回该元素上的所有注解
Annotation[] getDeclaredAnnotations():返回直接作用在该元素上的注解,不包括继承的注解
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):该元素是否有指定的注解
Class,Field,Method等类均实现了该接口,所以我们可以通过Java的反射机制来获得类,构造器,方法,字段上的注解信息,可以举一个小例子
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 定义自己的注解
* 该注解可用于类,构造方法,方法,字段
* 该注解作用于运行时
*/
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
int id() default -1;
String author() default "cxy";
String date() default "2014-08-07";
String description();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface OnlyClass {
String description() default "只能在类型上使用该注解";
}
//使用自定的注解标注类
@MyAnnotation(id = 1,description = "这是一个用来演示如何处理注解的类")
@OnlyClass
public class AnnotationProcessor {
@MyAnnotation(id = 2,description = "类的实例域")
private String addr = "China";
private String phone = "010-84950493";
@MyAnnotation(id = 3,description = "类的实例方法")
public void f() {
System.out.println("----------------------");
}
//打印注解的信息
public static void print(MyAnnotation clanno) {
System.out.println("author: " + clanno.author() + "\nid: " + clanno.id() + "\ndate: " + clanno.date() + "\ndescription: " + clanno.description());
}
public static void main(String[] args) {
Class<AnnotationProcessor> clazz = AnnotationProcessor.class;
//获取类上的所有注解
Annotation[] annos = clazz.getAnnotations();
for(Annotation anno : annos) {
//注解其实跟类型无异,也可以使用instanceof
if(anno instanceof MyAnnotation)
print((MyAnnotation)anno);
else {
System.out.println("这是一个OnlyClass注解,其描述是:" + ((OnlyClass)anno).description());
}
}
//逐字段获取其上的注解信息
Field[] feilds = clazz.getDeclaredFields();
for(Field f : feilds) {
if(f.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation fanno = f.getAnnotation(MyAnnotation.class);
print(fanno);
}else {
System.out.println(f.getName() + "没有MyAnnotation注解");
}
}
//逐方法获取其上的注解信息
Method[] methods = clazz.getDeclaredMethods();
for(Method method : methods) {
if(method.isAnnotationPresent(MyAnnotation.class)) {
MyAnnotation manno = method.getAnnotation(MyAnnotation.class);
print(manno);
}else {
System.out.println(method.getName() + "没有MyAnnotation注解");
}
}
}
}
自从Java 6后有了更好的处理注解的API,javax.annotation.processing包里有更强的工具,只是还未涉猎。暂且行文至此,还有Java 8新增的若干注解特性还有待学习。
转载请注明出处:喻红叶《Java Annotation》