[77→100]神奇的说明——Java Annotation(注
2016-07-12 本文已影响271人
沉思的Panda
在Android代码中经常看到 @Override
这个标志,AS里面重写父类的某个方法是,它是默认写上去的。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
如果你把这个方法的名字改一下,改成一个父类没有的方法名,就会看到编译器报错,告诉你Method does not override method from its superclass
这个神奇标志,叫Annotation,一般被翻译为注解。
定义
百度百科上这么解释:
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
所以,可以这么理解,注解就是一个说明,可以用来说明包、类、接口、注解、字段、方法、局部变量、方法参数等。
语言内置的注解
- @Override,表示当前的方法定义将覆盖父类中的方法。
- @Deprecated,表示当前方法、变量、报名、参数、构造器被废弃,不赞成使用。如果还是被外部调用了,会有编译警告产生。
- @SuppressWarnings,给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默,即不给出错误或者警告提示。
用途:
- 在编译时对标注对象进行某些分析、处理。原生的
@Override
、@Deprecated
、@SuppressWarnings
都是如此,页面绘制利器butterknife
也是在编译生成相关的代码。
- 可以为编译器提供而外信息,以便于检测错误,抑制警告等。
- 可以通过处理Annotation信息来生成原代码,xml文件等等。
- 在运行时对标注对象进行某些分析、处理。这一类注解比较少,主要是因为运行时,解析注解是一个耗时操作,对程序性能有影响。比如
eventbus
的注解就是运行时的。
原理
如果你去看@Override的源码,你会发现它的内容如下:
package java.lang;
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 Override {
}
你会发现和普通的类相比,有两个重要的不同:
- 关键字是
@interface
,说明注解在java中的地位和class
、interface
、enum
是并列的,属于重要基础。 - 它被另外两个注解——
Target
和Retention
注解着,这两个注解就是整个annotation机制的核心。
Target:指明注解对象
package java.lang.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Target {
ElementType[] value();
}
取值如下:
- ElementType.ANNOTATION_TYPE:注解
- ElementType.CONSTRUCTOR:构造方法
- ElementType.FIELD:成员字段、枚举常量
- ElementType.LOCAL_VARIABLE:局部变量
- ElementType.METHOD:方法
- ElementType.PACKAGE:包
- ElementType.PARAMETER:方法参数
- ElementType.TYPE:类型(接口、类、枚举)
Retention:指明注解存在的生命周期
package java.lang.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface Retention {
RetentionPolicy value();
}
取值如下:
- RetentionPolicy.SOURCE:源码时。注解仅存在于源码中,在class字节码文件中不包含。
- RetentionPolicy.CLASS:编译时(默认的保留策略)。注解会在class字节码文件中存在,但运行时无法获得。
- RetentionPolicy.RUNTIME:运行时。注解会在class字节码文件中存在,在运行时可以通过反射获取到
由此可知,Override
是一个作用于方法、只存在于源码内的注解,在class文件和运行中都无法找到这个注解。
自定义注解
这里简单介绍一个运行时注解RunTimeAnnotation
package panda.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RunTimeAnnotation {
enum FontColor {
BLUE,
RED,
GREEN
};
String value();
String name() default "name";
FontColor fontColor() default FontColor.RED;
}
再建一个被注解的类
package panda.annotation;
/**
* Created by shitianci on 16/7/12.
*/
public class AnnotationTest {
@RunTimeAnnotation("main")
public static void main(String[]args) {
saying();
sayHelloWithDefaultFontColor();
sayHelloWithRedFontColor();
}
@RunTimeAnnotation("saying")
public static void saying() {
}
@RunTimeAnnotation(value = "sayHelloWithDefaultFontColor",name = "名字")
public static void sayHelloWithDefaultFontColor() {
}
@RunTimeAnnotation(value="sayHelloWithRedFontColor", fontColor=RunTimeAnnotation.FontColor.RED)
public static void sayHelloWithRedFontColor() {
}
}
最后测试
package panda.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[]args) throws ReflectiveOperationException {
showClassAnnotationInfo(Class.forName("panda.annotation.AnnotationTest"));
}
/**
* 展现指定class的运行时注解信息
* @param c
* @throws ClassNotFoundException
*/
public static void showClassAnnotationInfo(Class c) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
// 获取该类所有声明的方法
Method[] methods = c.getDeclaredMethods();
// 声明注解集合
Annotation[] annotations;
// 遍历所有的方法得到各方法上面的注解信息
for (Method method : methods) {
// 获取每个方法上面所声明的所有注解信息
annotations = method.getDeclaredAnnotations();
// annotations = method.getAnnotations();
// 再遍历所有的运行时注解,打印其基本信息
StringBuilder sb = new StringBuilder();
sb.append(method.getName() + "注解个数:" + annotations.length + "{");
for (Annotation an : annotations) {
sb.append("[" + an.annotationType().getSimpleName() + "(");
Method[] meths = an.annotationType().getDeclaredMethods();
// 遍历每个注解的所有变量
for (Method meth : meths) {
// Field f = c.getDeclaredField(meth);
sb.append(meth.getName() + "=" + meth.invoke(an) + ", ");
}
sb.append(")], ");
}
sb.append("};");
System.out.println(sb.toString());
}
}
}
测试结果为
main注解个数:1{[RunTimeAnnotation(name=name, value=main, fontColor=RED, )], };
saying注解个数:1{[RunTimeAnnotation(name=name, value=saying, fontColor=RED, )], };
sayHelloWithDefaultFontColor注解个数:1{[RunTimeAnnotation(name=名字, value=sayHelloWithDefaultFontColor, fontColor=RED, )], };
sayHelloWithRedFontColor注解个数:1{[RunTimeAnnotation(name=name, value=sayHelloWithRedFontColor, fontColor=RED, )], };
参考
Panda
2016-07-12