此文将介绍注解的三个方面:
1. Java内建的注解
Java有三个内建的注解
@Override
- 当重写父类方法的时候,我们使用这个注解来通知编译器我们正在重写这个方法。当父类的这个方法被移除或改变的时候,编译器会提示一个错误信息。@Deprecated
- 这个注解用来告诉编译器某个方法已经被废弃。当某个地方调用此方法,编译器会提示该方法已被废弃。@SuppressWarnings
- 此注解用来告诉编译器忽略掉某个(些)警告。
@Deprecated
public static void oldMethod(){
System.out.println("old method. Do not use it.");
}
public static void genericsTest() throws FileNotFoundException{
List list = new ArrayList<>();
list.add("abc");
oldMethod();
}
比如上面这两个方法,oldMethod()方法被@Deprecated注解了。所以编译器中在genericsTest()方法里面调用oldMethod()时,代码会出现如下的形态以提示该方法已被废弃:
oldMethod()
而list.add(“abc”)代码处编译器会提示:Unchecked call to add(E) as a member of the raw type java.util.List
这个警告的出现是因为此处list未指定数据类型。如果想忽略掉此警告,可以在genericsTest()方法上面添加注解:
@SuppressWarnings("unchecked")
SuppressWarnings注解可以传入多个值,下面列举一些常见的:
值 | 作用 |
---|---|
all | 所有警告 |
boxing | boxing/unboxing操作相关的警告 |
cast | 类型转换操作相关的警告 |
dep-ann | 被废弃的注解相关的警告 |
deprecation | 弃用相关的警告 |
fallthrough | switch语句中丢失break相关的警告 |
finally | finally块中不返回相关的警告 |
hiding | 本地变量隐藏父类变量相关的警告 |
incomplete-switch | swtich语句中入口缺失相关的警告(枚举变量) |
null | null值分析相关的警告 |
rawtypes | 在类的参数中使用未指定类型的泛型时相关的警告 |
restriction | 使用了不推荐的或者禁止的引用时的警告 |
serial | 在serializable类中缺失serialVersionUID时的警告 |
static-access | 用错误的方式访问static 值/方法 时的警告 |
synthetic-access | 来自内部类未经优化的访问相关的警告 |
unchecked | 未经检查的操作相关的警告 |
unqualified-field-access | 不合格地访问字段相关的警告 |
unused | 有未使用的代码相关的警告 |
2. 自定义注解
先看下面的一个自定义注解:
@Documented
@Target(ElementType.METHOD)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInfo{
String author() default "xiaoqiang";
String date();
int revision() default 1;
String comments() default "";
}
几个值得注意的地方:
- 定义一个注解和定义一个接口类似,不过关键字是
@interface
- 注解的方法不能有参数
- 注解方法的返回值只能是原始数据类型、枚举、Annotation或者这些的数组
- 注解方法可以有默认值
- 注解可以有原始注解,原始注解定义了注解的一些属性
Java中有四个原始注解:
-
@Documented: 意味着使用该注解的元素应该被
javadoc
和类似工具记录。如果使用Documented注解类型声明,其注解将成为该元素的公共API的一部分。 -
@Target: 指明该注解的类型,其中一些值有
TYPE
、METHOD
、CONSTRUCTOR
、FIELD
。比如被@Target(ElementType.METHOD)
标记的注解只能用来注解方法。如果一个注解没有@Target,那么它可以被用来注解任意元素。 -
@Inherited: 表示自动继承注解类型。 如果用户在类声明上查询该注解类型,并且类声明没有此类型的注解,则将自动查询类的父类以获取注解类型。 此过程将重复直到找到此类型的注解,或者直到查找到Object类。
-
@Retention: 表示该注解将保留多久。它有三个值
RetentionPolicy.SOURCE
、RetentionPolicy.CLASS
、RetentionPolicy.RUNTIME
。SOURCE表示注解将被编译器丢弃。CLASS表示注解会被编译器记录下来,但会在运行时被丢弃,这是注解的默认状态。RUNTIME表示注解会被编译器记录,并且在运行时仍然被保留,因此它可以被反射。
3. 反射注解
反射注解就是在运行时将注解解析出来。前面已经提到,只有标注了@Retention(RetentionPolicy.RUNTIME)
的注解才能保留到运行时,因此反射也只能运用在此类注解上。
我们将在 2 中自定义的注解用到如下的方法上:
public final class Gas95{
@MethodInfo(comments = "gas95 is good", date = "Jan 24 2019")
public void printGasInfo( boolean includeCompany, String test){
//do anything
}
}
接着可以通过反射来解析printGasInfo方法的注解,具体代码如下:
//通过反射获取方法printGasInfo
Method method = Gas95.class.getMethod("printGasInfo", boolean.class, String.class);
//获取方法的MethodInfo注解
MethodInfo annotation = method.getAnnotation(MethodInfo.class);
//打印注解的值
System.out.println("author = " + annotation.author()
+ "\ndate = " + annotation.date()
+ "\nversion = " + annotation.version()
+ "\ncomments = " + annotation.comments());
打印的结果如下:
author = xiaoqiang
date = Jan 24 2019
version = 1
comments = gas95 is pretty good
可以看到,comments
和date
的值就是我们在使用注解时传入的值,而author
和version
,由于使用时没有赋值,因此都是默认值。对于没有默认值的注解方法,比如这里的date
,在使用时必须赋值。
以上是注解的一些常见用法,欢迎交流讨论。