注解早在J2SE1.5就被引入到Java中,主要提供一种机制,这种机制允许程序员在编写代码的同时可以直接编写元数据。
例如“@deprecated”自从Java1.4便开始在Java文档中使用。注解可以被用在包,类,方法,变量,参数上。自Java8起,有一种注解几乎可以被放在代码的任何位置,叫做类型注解
被注解的代码并不会直接被注解影响。这只会向第三系统提供关于自己的信息以用于不同的需求。注解会被编译至class文件中,而且会在运行时被处理程序提取出来用于业务逻辑。当然,创建在运行时不可用的注解也是可能的,甚至可以创建只在源文件中可用,在编译时不可用的注解。
注解语法和元素
声明一个注解需要使用“@”作为前缀,这便向编译器说明,该元素为注解。例如:
@Annotation
public void annotatedMehod() {
...
}
上述的注解名称为Annotation,它正在注解annotatedMethod方法。编译器会处理它。注解可以以键值对的形式持有有很多元素,即注解的属性。
@Annotation(
info = "I am an annotation",
counter = "55"
)
public void annotatedMehod() {
...
}
如果注解只包含一个元素(或者只需要指定一个元素的值,其它则使用默认值),可以像这样声明:
@Annotation("I am an annotation")
public void annotatedMehod() {
...
}
就像我们看到的一样,如果没有元素需要被指定,则不需要括号。多个注解可以使用在同一代码上,例如类:
@ Annotation (info = "U a u O")
@ Annotation2
class AnnotatedClass { ... }
在什么地方使用
注解基本上可以在Java程序的每一个元素上使用:类,域,方法,包,变量,等等。
自Java8,诞生了通过类型注解的理念。在此之前,注解是限于在前面讨论的元素的声明上使用。从此,无论是类型还是声明都可以使用注解,就像:
@MyAnnotation String str = “danibuiza”;
使用案例
注解可以满足许多要求,最普遍的是:
- 向编译器提供信息:注解可以被编译器用来根据不同的规则产生警告,甚至错误。一个例子是Java8中@FunctionalInterface注解,这个注解使得编译器校验被注解的类,检查它是否是一个正确的函数式接口。
- 文档:注解可以被软件应用程序计算代码的质量例如:FindBugs,PMD或者自动生成报告,例如:用来Jenkins, Jira,Teamcity。
- 代码生成:注解可以使用代码中展现的元数据信息来自动生成代码或者XML文件,一个不错的例子是JAXB。
- 运行时处理:在运行时检查的注解可以用做不同的目的,像单元测试(JUnit),依赖注入(Spring),校验,日志(Log4j),数据访问(Hibernate)等等。
分类
- 标示的注解 成员个数为0,用来标示的
- 单值注解 成员个数为1
完整注解 成员个数>1
根据使用和用途
- 1.系统注解
- 1.@Override 实现或者重写接口或父类中的方法
- 2.@Deprecated 用来标识过时地方法
- 3.@SuppressWarnings 用来警告用户
参数 deprecation :使用了过时的方法或者类是的警告
unchecked: 执行了未检查的转换时的警告
failthrouth:当switch语句直接通往下一种情况,而没有break时的警告
path:在类路径,源文件路径等中有不存在的路径时的警告
serial:当在可序列化的类上缺少id
finally:任何finally子句不能正常完成的警告
all:关于以上所有警告的情况
2.元注解
- 1.@Target 指定注解存放的位置
- 1.CONSTRUCTOR:用于描述构造器
- 2.FIELD:用于描述域
- 3.LOCAL_VARIABLE:用于描述局部变量 var a;
- 4.METHOD:用于描述方法
- 5.PACKAGE:用于描述包
- 6.PARAMETER:用于描述参数 //eat(@override int a)
- 7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
- 1.CONSTRUCTOR:用于描述构造器
- 2.@Retention 注解存在的位置
- 1.SOURCE:在源文件中有效(即源文件保留)
- 2.CLASS:在class文件中有效(即class保留)
- 3.RUNTIME:在运行时有效(即运行时保留)
- 1.SOURCE:在源文件中有效(即源文件保留)
- 3.Documented 生成文档之后注解的信息会显示在文档中
- 4.@Inherited 父类的类上的注解可以被子类继承,被标注的注解retention必须是runTime
- 1.@Target 指定注解存放的位置
自定义注解
- 定义注解格式:
public @interface 注解名 {定义体} 注解参数的可支持数据类型:
1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
2.String类型
3.Class类型
4.enum类型
5.Annotation类型
6.以上所有类型的数组注解的处理器
- 方法1: T getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
- 方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
- 方法3:boolean is AnnotationPresent(Class
- 1.系统注解
注解中的继承
注解在Java中可以使用继承。这种继承和普通的面向对象继承几乎没有共同点。如果一个注解在Java中被标识成继承,使用了保留注解@Inherited,说明它注解的这个类将自动地把这个注解传递到所有子类中而不用在子类中声明。通常,一个类继承了父类,并不继承父类的注解。这完全和使用注解的目的一致的:提供关于被注解的代码的信息而不修改它们的行为。
我们通过一个例子更清楚地说明。首先,我们定义一个自动继承的自定义注解。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface InheritedAnnotation
{
}
有一个父类名为:AnnotatedSuperClass,已经被自定义的注解给注解上了:
@InheritedAnnotation
public class AnnotatedSuperClass{
public void oneMethod(){
}
}
一个子类继承父类:
public class AnnotatedSubClass extend AnnotatedSuperClass{
public void oneMethod()
{
}
}
子类 AnnotatedSubClass 展示了自动继承的注解 @InheritedAnnotation。我们看到下面的代码通过 isAnnotationPresent() 方法测试出了当前注解。
System.out.println( "is true: " + AnnotatedSuperClass.class.isAnnotationPresent( InheritedAnnotation.class ) );
System.out.println( "is true: " + AnnotatedSubClass.class.isAnnotationPresent( InheritedAnnotation.class ) );
输出如下:
is true: true
is true: true
我们可以看到子类虽然并没有声明注解,但还是被自动地注解上了。
如果我们尝试注解在一个接口中:
@InheritedAnnotation
public interface AnnotatedInterface
{
public void oneMethod();
}
一个实现了该接口的类:
public class AnnotatedImplementedClass implements AnnotatedInterface{
@Override
public void oneMethod(){
}
}
经过 isAnnotationPresent() 方法测试:
System.out.println( "is true: " + AnnotatedInterface.class.isAnnotationPresent( InheritedAnnotation.class ) );
System.out.println( "is true: " + AnnotatedImplementedClass.class.isAnnotationPresent( InheritedAnnotation.class ) );
结果如下:
is true: true
is true: false
这个结果说明继承注解和接口在一起使用时,接口中的注解在实现类中:仅仅被忽略。实现类并不继承接口的注解;接口继承仅仅适用于类继承。正如 AnnotatedSubClass。@Inheriated注解仅在存在继承关系的类上产生效果,在接口和实现类上并不工作。这条同样也适用在方法,变量,包等等。只有类才和这个注解连用。
案列(Android实现类似Xutils注解功能):
定义注解:
@Target(value = { ElementType.FIELD })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
}
引用注解:
public class MainActivity extends Activity {
@ViewInject(R.id.tv)
private TextView tv;
@ViewInject(R.id.bt)
private Button bt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewUtils.inject(this);
tv.setText("我修改了");
bt.setText("我修改了Button");
}
}
ViewUtil代码(通过反射获取view对象):
public class ViewUtils {
public static void inject(Activity activity){
Class<? extends Activity> clazz = activity.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
if(field.isAnnotationPresent(ViewInject.class)){
ViewInject viewInject = field.getAnnotation(ViewInject.class);
View view = activity.findViewById(viewInject.value());
field.setAccessible(true);
try {
field.set(activity, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}