Java进阶_08_注解


本文介绍Java中的注解机制。

1. 注解概述

注解,也被称为注释,英文单词是:Annotation。注解是一种引用数据类型,编译之后生成xxx.class文件。自定义注解语法格式如下所示:

1
2
3
[修饰符列表] @interface 注解类型名{

}

那么注解作为一种引用数据类型,主要用在哪里呢?怎么使用呢?

第一:注解使用时的语法格式是:@注解类型名

第二:注解可以出现在类上、接口上、属性上、方法上、变量上等等…。注解也可以出现在注解类型上(默认情况下注解可以出现在任意位置,可以通过在注解上添加其他注解,来限制该注解的使用范围,如Override只能出现在方法上)。

adv_022.png (417×136) (gitee.io)

简单案例如下所示:

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包下,可以查看帮助文档,如下所示:

adv_020.png (1492×417) (gitee.io)

主要掌握DeprecatedOverride两种注解即可。

2.1 Override

表示一个方法声明打算重写父类中的另一个方法声明,只能注解方法。这个注解是给编译器参考的,和运行阶段没有关系。凡是Java中的方法带有这个注解的,编译器都会进行编译检查,如果不是重写父类的方法则会报错。

例如,重写父类方法,但是怕写错了,可以添加注解,交给编译器来检查是否重写了父类方法。所以注解不是必须的

adv_021.png (402×210) (gitee.io)

2.2 Deprecated

表示所修饰的元素已经过时了,不再建议使用,主要是为了向程序员传达该元素已经过时,有更好的解决方案。在IDEA中的体现就是出现了横线。

adv_023.png (595×678) (gitee.io)

2.3 元注解

用于修饰注解的注解被称为元注解,如下面的Target以及Retention就是元注解,这两个注解也是常用的元注解。

  • Target注解主要用来限制被标注的注解可以出现在哪些位置上(方法、属性、类等等)。
  • Retention注解主要用来限制被标注的注解最终保存在什么位置上,source表示只停留在源文件中,不会出现在class文件中。除了source还有class(保留在class文件中)和runtime(保留在class文件中,且可以被反射机制读取)。

adv_022.png (417×136) (gitee.io)

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. 通过该对象获取到注解中的属性

案例一:

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
// 反射注解
// 通过反射机制获取AnnotationTest04类上修饰的注解类及其属性
public class ReflectAnnotationTest01 {
public static void main(String[] args) {

try {

// 获取类
Class annotationtest04 = Class.forName("annotation.AnnotationTest04");


// 判断类上是否有指定的注解修饰
boolean annotationPresent = annotationtest04.isAnnotationPresent(MyAnnotation03.class);
// System.out.println(annotationPresent);
if(annotationPresent){
// 获取注解对象,注意要转型,否则拿到的是注解的父类Annotation,这样后续无法访问到具体的属性(以为内父类没有)
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站《动力节点》。


文章作者: hianian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hianian !
  目录