本文介绍Java中的注解机制。
1. 注解概述
注解,也被称为注释,英文单词是:Annotation。注解是一种引用数据类型,编译之后生成xxx.class文件。自定义注解语法格式如下所示:
1 2 3
| [修饰符列表] @interface 注解类型名{ }
|
那么注解作为一种引用数据类型,主要用在哪里呢?怎么使用呢?
第一:注解使用时的语法格式是:@注解类型名
。
第二:注解可以出现在类上、接口上、属性上、方法上、变量上等等…。注解也可以出现在注解类型上(默认情况下注解可以出现在任意位置,可以通过在注解上添加其他注解,来限制该注解的使用范围,如Override只能出现在方法上)。
简单案例如下所示:
1 2 3 4 5 6 7
|
public @interface MyAnnotation {
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
@MyAnnotation public class AnnotationTest01 {
@MyAnnotation private int no; public String name;
@MyAnnotation public AnnotationTest01() { }
@MyAnnotation public static void m1(){ @MyAnnotation int i = 100; }
@MyAnnotation public String m2(@MyAnnotation String name){ return ""; } }
|
2. JDK内置注解
注解作为一种数据类型,Java提供了一些注解,主要在java.lang
包下,可以查看帮助文档,如下所示:
主要掌握Deprecated和Override两种注解即可。
2.1 Override
表示一个方法声明打算重写父类中的另一个方法声明,只能注解方法。这个注解是给编译器参考的,和运行阶段没有关系。凡是Java中的方法带有这个注解的,编译器都会进行编译检查,如果不是重写父类的方法则会报错。
例如,重写父类方法,但是怕写错了,可以添加注解,交给编译器来检查是否重写了父类方法。所以注解不是必须的。
2.2 Deprecated
表示所修饰的元素已经过时了,不再建议使用,主要是为了向程序员传达该元素已经过时,有更好的解决方案。在IDEA中的体现就是出现了横线。
2.3 元注解
用于修饰注解的注解被称为元注解,如下面的Target以及Retention就是元注解,这两个注解也是常用的元注解。
- Target注解主要用来限制被标注的注解可以出现在哪些位置上(方法、属性、类等等)。
- Retention注解主要用来限制被标注的注解最终保存在什么位置上,source表示只停留在源文件中,不会出现在class文件中。除了source还有class(保留在class文件中)和runtime(保留在class文件中,且可以被反射机制读取)。
3. 注解属性
我们通常可以在注解中定义属性,注解中的属性在语法上和方法类似,但是实际上是属性。注意,如果注解中有属性,那么在应用注解的时候必须给属性赋值,除非在定义的时候指定默认值,这样在使用的时候可以不用赋值了,语法格式如下:
1 2 3 4 5
| 数据类型 属性名() [default 默认值];
@注解名(属性名=属性值)
|
案例如下所示:
1 2 3 4
| public @interface MyAnnotation02 { String name(); int age() default 18; }
|
1 2 3 4 5 6 7
| public class AnnotationTest03 {
@MyAnnotation02(name = "zhangSan") public static void doSOme(){ System.out.println("------------"); } }
|
注意,如果注解中只有一个属性,并且属性名是value,那么在使用注解的时候,给属性赋值可以不用写属性名value。如@Annotation("haha")
。
3.1 属性类型
注解中的属性都可以是哪些数据类型呢?
byte,short,int,long,float,double,boolean,char
String,Class,枚举
以及上述类型的数组形式
4. 反射注解
要想通过反射获取注解,首先应该限制注解的保留策略为RUNTIME
。
- 首先判断是否有该注解修饰
- 如果有的话,获取到该注解修饰类对象
- 通过该对象获取到注解中的属性
案例一:
1 2 3 4 5 6 7 8 9 10
|
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation03 {
String name() default "河北省"; }
|
1 2 3 4 5 6 7 8 9 10 11 12
| @MyAnnotation03 public class AnnotationTest04 { int i;
public AnnotationTest04(){}
@MyAnnotation03 public void doSome(){ int j; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
public class ReflectAnnotationTest01 { public static void main(String[] args) {
try {
Class annotationtest04 = Class.forName("annotation.AnnotationTest04");
boolean annotationPresent = annotationtest04.isAnnotationPresent(MyAnnotation03.class);
if(annotationPresent){ MyAnnotation03 annotation = (MyAnnotation03)annotationtest04.getAnnotation(MyAnnotation03.class); System.out.println(annotation);
String name = annotation.name(); System.out.println(name); }
} catch (ClassNotFoundException e) { e.printStackTrace(); } } }
|
案例二:获取方法上修饰的注解类
要想获取方法,首先要获取类。获取类之后获取该方法,然后判断该方法是否有该注解修饰,有的话,获取该注解对象,然后通过该对象获取属性值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class ReflectAnnotationTest02 { public static void main(String[] args) { try {
Class myAnnotation04 = Class.forName("annotation.AnnotationTest04"); Method doSome = myAnnotation04.getDeclaredMethod("doSome");
if(doSome.isAnnotationPresent(MyAnnotation03.class)){ MyAnnotation03 annotation = doSome.getAnnotation(MyAnnotation03.class); System.out.println(annotation.name()); }
} catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } }
|
5. 注解的作用
一个需求如下:如果一个类被@Id
注解修饰,那么要求这个类必须有int类型的id属性,这样才合法,否则不合法。
这时候,首先判断该类是否有该注解修饰,有的话,进而判断该类是否有int类型的id属性。
实际上,注解等同于一种标记,我们可以根据注解的不同而执行不同的程序。
6. 备注
参考B站《动力节点》。