Java注解(annotation)
什么是注解?
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
Annotation是被动的元数据,永远不会有主动行为
既然是被动数据,对于那些已经存在的注解,比如Override,我们只能看看而已,并不知道它具体的工作机制是什么;所以想要理解注解,就直接从自定义注解开始。
自定义注解
下面是一段代码示例,具体每个代码功能下面会讲到:
//用来标记自定义注解的有效范围
@Retention(RetentionPolicy.RUNTIME)
//指定Annotation用于修饰哪些程序元素
@Target({ElementType.TYPE})
//用于描述其它类型的annotation应该被作为被标注的程序成员的公共API
@Documented
//元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的
@Inherited
public @interface ContentView {//@interface表明这是一个注解
int value() default -1;
}
元注解:
元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。Java5.0定义的元注解:
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
这些类型和它们所支持的类在java.lang.annotation包中可以找到。下面我们看一下每个元注解的作用和相应分参数的使用说明。
@Target:
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
取值(ElementType)有:
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention:
@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
Retention meta-annotation类型有唯一的value作为成员,它的取值来自java.lang.annotation.RetentionPolicy的枚举类型值。
@Documented:
@Documented 用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员
@Inherited:
@Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
自定义注解:
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。
定义注解格式: public @interface 注解名 {定义体}
****注解参数的可支持数据类型:
1.所有基本数据类型(int,float,boolean,byte,double,char,long,short) 2.String类型
3.Class类型
4.enum类型
5.Annotation类型
6.以上所有类型的数组
Annotation类型里面的参数该怎么设定:
第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;
第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String;
第三,如果只有一个参数成员,最好把参数名称设为"value",后加小括号.
//注解解析
//一下步骤可以在base基类完成
// for (Class c = this.getClass(); c != Context.class; c = c.getSuperclass()) {
ContentView annotation = this.getClass().getAnnotation(ContentView.class);
if (annotation != null) {
try {
this.setContentView(annotation.value());
} catch (RuntimeException e) {
e.printStackTrace();
}
return;
// }
}
第一步:遍历所有的子类
第二步:找到修饰了注解ContentView的类
第三步:获取ContentView的属性值。
第四步:为Activity设置布局。
总结
你可能会觉得setContentView(R.layout.activity_home)和@ContentView(R.layout.activity_home)没什么区别,用了注解反而还增加了性能问题。
但你要知道,这只是注解最简单的应用方式。举一个例子:AndroidEventBus的注解是运行时注解,虽然会有一点性能问题,但是在开发效率上是有提高的。
注意
很多人会问,在java代码中利用反射会不会对app性能产生影响,答案是肯定的,但是有解决的办法呢,就是这个 Java Annotation Processing
Annotation processing 是javac中用于编译时扫描和解析Java注解的工具。Annotation processing是在编译阶段执行的,它的原理就是读入Java源代码,解析注解,然后生成新的Java代码。新生成的Java代码最后被编译成Java字节码,注解解析器(Annotation Processor)不能改变读入的Java 类,比如不能加入或删除Java方法
这样性能的问题就解决了呢