Java中的注解
一.注解(Annotation):
1.解释:其实就是代码的特殊标记,这些标记可以在编译、运行时、类加载时被读取,并执行相应的操作。
2.代码中常常出现的注解,java提供的基本注解:@Override、@Deprecated、@SuppressWarnings、@SafeVarargs、@FunctionalInterface。其中ov是最常见用来指定方法的覆盖,强制一些子类必须覆盖父类的一些方法,编译器会检查是否重写,避免类名写错等低级的错误。Deprecated标示已过时,使用过时的类或者方法会给出警告。这两个是最常见的注解。
3.元Annotation:
在java.lang.annotation包下提供了6个Meta Annotation,这些注解用于修饰自定义的注解,即元注解;
1.@Retention:标示注解可以保留多长时间
@Retention((RetentionPolicy.CLASS)) 记录在class文件中 运行java程序时jvm获取不到注解信息 这个是默认值;
@Retention((RetentionPolicy.SOURCE)) 只保留在源代码中 编译器直接丢弃这种注解;
@Retention(RetentionPolicy.RUNTIME) 这个可以保留到运行时JVM可以可以获取注解信息 可以通过反射获取信息;
2.@Target: 这个元注解可修饰注解标示用于哪种程序单元 (即类、方法、成员变量)
@Target(ElementType.METHOD) 这里只能修饰方法;
@Target(ElementType.FIELD) 成员变量;
PACKAGE包、TYPE类或接口或枚举定义、LOCAL_VARIABLE局部变量。
3.@Documented 被该元Annotation修饰的Annotation类将被javadoc工具提取成文档 ;
4.@Inherited:被修饰的注解具有集成性,父类中使用该注解则子类自动被该自定义注解修饰;
4.自定义注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
String name() default "noName";
int age();
}
上面是一个名为Test的自定义注解,用@interface声明,注解中的成员变量以方法的形式来定义,可以通过default来定义默认值。ElementType.METHOD标示该注解只能用于修饰方法,RetentionPolicy.RUNTIME指注解可以在运行时获取。
使用该注解:
public class MyTest {
@Test(age = 1)
publicstatic void info() {
}
@Test(age = 2)
publicstatic void test() {
}
publicstatic void test2(){
}
@Test(age = 3)
public void test3(){
}
}
在MyTest类中定义了四个方法,其中三个使用了Test注解,使用注解的方法都抛出了一个参数错误的异常。
那么定义的这些注解怎么使用呢?首先要知道的是仅仅使用注解来标记 程序元素对程序是不会有任何影响的, 为了让这些注解起作用必须为这些注解提供一个注解处理工具,使用的场景受@Retention的限制。下面定义一个类用于处理注解:
public class AnnatationUtil {
public static void process(String clazz)throws ClassNotFoundException{
int passed=0;
int failed=0;
//遍历clazz对应类里的所有方法
for (Method method : Class.forName(clazz).getDeclaredMethods()) {
//如果这个方法使用了@Test注解
if (method.isAnnotationPresent(Test.class)) {
try {
method.invoke(null);
passed++;
System.out.println("age"+method.getAnnotation(Test.class).age());
System.out.println("方法"+method+"运行成功");
} catch (Exception e) {
System.out.println("方法"+method+"运行失败,异常");
failed++;
e.printStackTrace();
}
}
}
System.out.println("总"+passed+failed);
}
}
其中process方法会对clazz进行反射操作,这个处理场景是在运行时,通过反射获取类中的方法集合进行遍历,通过isAnnotationPresent方法判断是否被Test注解修饰了,如果被修饰了则调用此方法,下面以MyTest方法为例:
public static void main(String[] args) {
try {
AnnatationUtil.process("annotation.MyTest");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
在主方法中调用process方法并出入MyTest的类路径,执行后的日志:
二.什么是APT:即注解处理工具或者叫注解处理器,ButterKnife,dagger等常用的项目框架中都有使用。
1.怎么工作的:对元代码文件进行检测,并找出源文件包含的Annotation信息,针对Annotation做出特别的处理。
2.为什么使用:主要目的是为了简化开发者的工作量,APT可以在编译程序源代码的同时生成一些附属文件,可以代替传统的对代码信息和附属文件的维护工作。在JAVA中的运行很广泛。
3.注解处理器:每个出列器都要实现javax.annotation.processing包下的Processor接口,通常是通过继承AbstractProcessor,一个处理器可以处理一种或多种Annotation类型。
实现一个生成XML文件的注解处理器:
@SupportedAnnotationTypes({"Persistent","Id","Property"})
public class MyAnnotationProcessor extends AbstractProcessor {
//循环处理每个需要处理的程序对象
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//文件输出流
PrintStream ps=null;
//RoundEnvironment获取Annotation信息 getElementsAnnotatedWith可根据Annotation获取需要处理的程序单元由Element代表,
//Element中包含一个getKind()方法 该方法返回Element所代表的程序单元,
// getEncloseElements()获取该Element中定义的所有程序单元包括类、方法、构造器、内部类等;
for (Element t : roundEnv.getElementsAnnotatedWith(Persistent.class)) {
//正在处理的类名
Name clazzName=t.getSimpleName();
Persistent per = t.getAnnotation(Persistent.class);
try {
ps=new PrintStream(new FileOutputStream(clazzName+".hbm.xml"));
ps.println("<?xml version=\"1.0\"?>");
///.....................................//
for (Element f : t.getEnclosedElements()) {
//只处理成员变量上的Annotation
if (f.getKind() == ElementKind.FIELD) {
Id id = t.getAnnotation(Id.class);
if (id != null) {
ps.println("<id name=\""+f.getSimpleName()+">");
//......................................//
}
Property p = f.getAnnotation(Property.class);
if (p != null) {
//.............................//
}
}
}
ps.println(" </class>");
//...............
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (ps != null) {
try {
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return false;
}
}
头部的SupportedAnnotationTypes表示该注解器可以处理的注解有哪些。java中的使用配置和Android中是不同的。