回忆一下,在Java文件中,除了我们的代码,还能“合法”的存在别的东西吗?——注释
注释是Java语言提供给我们的,在代码中增加“额外”信息的一种方式,这些额外信息,有时不太合适,或不能用Java代码来表示。
注释中的描述,是一种人为的约定
javac在编译时,对其“视而不见”
只有固定语法,没有标准形式
因此,注释中表达的描述代码或者代码之外的信息,只有人能看懂。
一、注解的引入
注解:为我们在代码中添加信息,提供了一种形式化(标准化,甚至连额外信息的表示都是通过java相应的数据类型的值来表示)的方法,使我们可以在稍后的某个时刻非常方便的使用这些信息(通过代码来获取,并使用这些定义的额外信息)。
其实我们之前已经不自觉的使用过注解了:
@Override:检查子类确实是覆盖了父类的方法
// 在使用时,就是在向编译器传达一种额外信息,该方法要覆盖父类中的方法。
@Deprecated:说明已经过时了。
过时:官方已经不推荐使用该方法(这些方法可能有一定的潜在缺陷),在后续的jdk中,可能会删除该方法。
注意事项:
注解本身,只是用来传达一种代码之外的额外的说明信息,但是,通常注解都会有一些特殊的效果表现来,但这些特殊效果和注解本身没有关系,注解只用来传达信息,而是由注解的接收者来产生,比如报错的效果是由编译器来产生。
注解只和信息相关,至于获取到这个信息“人”他们在获取到该信息之后,会产生的效果,和注解本本身无关。
二、 注解的定义(标准形式)
Java语言中本身只定义了极少数的注解,不能满足我们所有的需求,但是Java语言,可以让我们自定义注解,满足自己的需求。
自定义注解格式
public @interface 注解名 {
定义体
}
注意事项:
1.注解的定义和类定义,接口定义非常像(尤其是接口)。
2.注解与类、接口在java语言中的地位一样, 它也代表数据类型,定义的一个注解就是一种数据类型。
一个注解数据类型,定义了:
a.具体包含少条具体的信息;
b.每种信息是怎样的;
注意,注解体中定义的每一条具体的信息都有标准形式。
3.@必不可少,少了@就成了接口定义了。
4.注解之间不能继承。
注解体的定义格式:
@interface 注解名 {
// 第一条信息的标准形式
返回值类型 方法名1(); // 这就是某种类型额外信息中的,一条信息的标准化的格式定义
// 方法名代表一条信息的名字
// 该条信息数据取值类型
// 第二 条信息的标准形式
返回值类定 方法名2();
...
}
举个例子:
定义一个注解类型,表示学生年龄取值范围的约束:
1.包含两条信息
2.一个注解类型, 包含的每一条信息,都有自己的名字 -> 方法名
3. 一个注解类型,包含的每一条信息的数据类型 -> 方法的返回值
@interface AgeConstraint {
// 第一条(学生年龄取值的上界)
int upperBound();
// 第二条(学生年龄取值的下界)
int lowerBound();
}
自定义注解体的说明:
- 注解体的格式类似于接口中的方法定义,但含义完全不同
- 方法名就是数据的名称,方法的返回值类型,表示数据值的类型。
- 每一条注解信息,它的取值的类型只能是以下几种:
a. 所有的基本数据类型
b. String类型
c. Class类型
d. 注解类型
e. 以及以上类型的数组
不能是类或者接口类型
三、注解的使用(赋值)
定义某种类型的注解,实际上也就是定义了某种类型的额外信息(注解)的“标准形式”。
定义好了注解的标准形式,那么我们就要来使用注解了:
使用注解就是使用某个注解类型的注解实例给java代码添加额外信息。
类比:
注解类型 类比于 类
注解实例 类比于 对象
注解使用的语法:
@注解的类型名(属性名1=属性值1,属性名2=属性值2,…) // 注解实例(注解对象)
这里的属性名:是注解中定义的一条信息的名称。
注解使用的实质,是创建某个类型的注解实例,并且给该实例中的每个属性赋值,从而,一个注解实例表示了一条具体的额外信息。
注意事项:
在使用注解实例的时候,一定要保证,注解定义中的每条数据(每一个属性),必须有确定值(每个属性必须有值):
- 在使用注解实例的时候,给每一个属性显示赋值。
- 可以在定义注解的时候,给注解中定义的某个属性,声明默认值。默认值的含义是指,当在使用注解实例的时候,如果没有给某属性显示赋值,此时如果该属性定义了默认值,那么在注解实例中该属性的值就会自动取默认值。
- 对于合法的引用类型的数据, 比如String类型, 它的默认取值不能是null。
- 在有一种特殊情况下,对于注解实例中的属性赋值可以稍作简化,条件是:
a. 属性名称固定 value
b. 当在注解实例中,仅仅只需要给value属性赋值的时候
当且仅当, 在注解实例中,给满足上述两个条件的属性赋值时,可以简化赋值,在赋值时可省略value=。
四、注解的处理
注解的处理(定义注解处理器,用来接收并处理通过注解添加到java代码中的额外信息)
五、元注解
在定义自定义注解的时候,我们可以使用元注解,来声明自定义注解的一些特殊属性
元注解: 注解的注解(描述注解的注解)
Java中提供了4种元注解,我们最常用的是其中2个
@Target 该注解,用来声明和限定注解使用的地方(目标)
- 整个类或接口 ElementType.TYPE
- 成员变量 ElementType.FIELD
- 构造方法 ElementType.CONSTRUCTOR
- ElementType.METHOD
虽然一种类型的注解实例,可以作用域多种目标(类,成员变量,构造方法,成员方法)
但是,不管注解实例是添加在哪里的,我们都有办法去获取它。
isAnnotationPresent(Class type) 判断指定目标上是否添加了注解
getAnnotation(Class type) 在指定目标上获取指定类型的注解实例
- 作用在类上 Class对象.
- 成员变量 Field对象.
- 方法 Method对象.
- 构造方法 Constructor对象.
@Rentention 该注解用来声明注解的保留级别
1.RetentionPolicy.RUNTIME JVM在运行时,也会保留注解信息
2.RetentionPolicy.CLASS 注解在class文件中可用,但会被JVM丢弃(内存没有)
3.RetentionPolicy.SOURCE 注解将被编译器丢弃(class文件中没有)
注解默认情况下的保留级别是CLASS(运行时已经没了)
@Target 该注解用来声明和限定注解使用的地方
注解可以定义在如下位置
- 定义在类上 ElementType.TYPE
- 定义成员变量上 ElementType.FIELD
- 定义在构造方法上 ElementType.CONSTRUCTOR
- 定义在成员方法上ElementType.METHOD
可以同时声明,注解使用在多个位置