打开 Eclipse,新建 Java 项目“注解与反射”,在 src 下右键并建立包 “注解与反射”,在包下右键并建立 Annotation (注解)文件,名称为“ItAnnotation”。
注解与反射调用
ItAnnotation.java 文件
package 注解与反射;
public @interface ItAnnotation {
}
以上就是一个注解文件。然后再新建一个 class 文件,取名为“TestAnnotation”
TestAnnotation.java 文件
public class TestAnnotation {
public static void main(String[] args) {
}
}
然后在类的上方加上 “@ItAnnotation”,并在 main 方法中检查注解是否存在。我们知道java代码编译后其实是一个以 class 文件存放。检查的方法就是
TestAnnotation.class.isAnnotationPresent(ItAnnotation.class)
如图所示:
如果 TestAnnotation 方法上方存在该注解则进行下一步操作,获取该方法指定类型的注解,代码如下:
ItAnnotation annotation = (ItAnnotation)TestAnnotation.class.getAnnotation(ItAnnotation.class);
然后打印输出 annotation 查看究竟,如图所示:
实际上什么都没有打印,这是为什么呢?我们切换到 ItAnnotation.java 文件,给注解添加一个元注解。我们知道“元”这个字往往是这个意思,注解的注解就是元注解,专门给注解服务的注解。
在注解的上方添加一个 @Retention(RetentionPolicy.RUNTIME)
表示修饰下方的注解始终不会丢弃,运行期间也保留该注解。如图所示:
现在再次运行,就会打印出:@注解与反射.ItAnnotation()
这边引出了个概念,@Retention(RetentionPolicy.RUNTIME)
的意思是运行时不丢弃,也就是说注解是会被丢弃的,那么注解到底在什么时候会被丢弃呢?其实还有另外两个关于 @Retention 元注解的生命周期分别如下:
RetentionPolicy.SOURCE 编译阶段就丢弃,不会生成在 class 文件里
RetentionPolicy.CLASS 在生成 class 文件的时候,会保留在 class 文件里
现在对 @Retention 元注解有一点概念了,那么还有其他的元注解么?有,接下来接触的就是 @Target,它的作用是他修饰的注解可以用在什么地方,也就是可以在哪个代码块区域的上方使用。如果不加 @Target 的话就是任何地方都可以用。
ElementType.TYPE: 用于类、接口、枚举(TYPE实际上是 class 的父级)
ElementType.FIELD:
ElementType.METHOD 用于方法
ElementType.PARAMETER
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE
ElementType.PACKAGE
切换回 ItAnnotation.java 文件,试着加上 @Target(ElementType.METHOD)
第二个元注解,如图所示:
然后切换回“TestAnnotation.java” 文件你会发现 IDE 报错了,把 “@ItAnnotation” 移动到 main 方法上方,错误就消失了。那是因为 ElementType.METHOD 只能用于方法。
我们将 @Target 进行修改,让它能够多选,一般来讲多选都是在括号里面加个大括号,改为 @Target({ElementType.METHOD,ElementType.TYPE})
,这样就可以同时用于类和方法上。
为注解增加一些属性
其实这个注解很像接口,我们在里面定义的相当于抽象方法。定义一个 String name()
,记住 Ctrl+S 保存代码。代码如下:
ItAnnotation.java 文件
......
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItAnnotation {
String name(); //增加一个字符串的属性
}
切换回 “TestAnnotation.java” 可以发现两个 @ItAnnotation 均报错,我们先删除main方法上方的注解,然后在注解后方加上括号填入属性值: @ItAnnotation(name = "小明")
,最后在这个文件 main 方法中之前通过 ItAnnotation annotation 获取的对象调用该方法:annotation.name()
,如下图所示:
TestAnnotation.java 文件
再次运行,控制台会输出“小明”。
有1种特殊的情况,可以在注解中省去属性名称并直接赋值。我们切换回 “ItAnnotation.java” 文件将 String name()
改为 String value()
,点击保存,再在“TestAnnotation.java”文件中,将注解中的值保留,前面的属性去除:@ItAnnotation("小明")
,然后将annotation.name()
改为 annotation.value()
再次运行,发现依然可以输出“小明”,这个情况是特殊的,当你定义的抽象方法为 value() 时,且只有1个的情况下,注解中可以省略属性名并直接赋值。
在拥有多个抽象方法(同时包含 value())时,直接赋值会报错,但在其他方法拥有默认值的情况下不会。切换回 “ItAnnotation.java” 文件,将抽象方法改为 String name default "小明";
,下方在加个 String value();
,完整代码如下:
ItAnnotation.java 文件
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItAnnotation {
String name() default "小明";
String value();
}
TestAnnotation.java 文件
@ItAnnotation("ABC")
public class TestAnnotation {
public static void main(String[] args) {
//检查指定的类上方的注解是否存在
if (TestAnnotation.class.isAnnotationPresent(ItAnnotation.class)) {
//获取 ItAnnotation 类型的注解
ItAnnotation annotation = (ItAnnotation)TestAnnotation.class.getAnnotation(ItAnnotation.class);
System.out.println(annotation.name());
System.out.println(annotation.value());
}
}
}
以上代码不会报错,再次运行,输出 name 的默认值(小明)与value的值(ABC)。