持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情
背景
从 JDK1.5 开始,Java 引入了注解机制。
注解使得 Java 源代码中不但可以包含功能性的实现代码,还可以添加元数据。
元数据
在介绍注解的概念前,首先了解元数据的概念。所谓元数据,就是描述数据的数据。
举个例子来说,比如一张图片,图片的内容为主体数据,是需要展现给图片浏览者看到的信息,而图片的创建日期、图片大小等这类信息就是元数据。
看到这里,你可能会问,元数据有啥用呢?
按照片创建日期举例,假设现在找一下2020年1月1日照片,那么在计算机里面,就可以按照这个日期找到该照片。
其实在之前,我们已经接触过注解了,使用继承的时候,如果要对父类的某个方法进行重写,那么就可以在子类重写方法的前面加上 @Override 注解,表明该方法不是子类特有的,而是覆盖了父类中的方法。
注解和注释区别
注解和注释都属于对代码的描述:
- 注释的作用只是简单地描述程序的信息,方便开发者再次阅读,不会被程序所读取。
- 注解则是 Java 代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。
语法上,注解前面有一个 “@” 符号,注释有三种 //xx,/xx/,/* xx/
关于注解
Java 注解有以下三种形式:
- 不带参数的注解:
@Annotation,例如@Override。 - 带一个参数的注解:
@Annotation(参数),例如@SuppressWarnings(value="unused")。 - 带多个参数的注解:
@Annotiation({参数1, 参数2, 参数3...}),例如@MyTag(name="jhon",age=20)。
注解定义
注解之所以强大,能被众多框架所使用,一个主要的原因就在于它允许程序员自己定义注解。
定义注解的语法形式和接口差不多,只是在 interface 前面多了一个 ”@“ 符号,结构如下:
[public] @interface 注解名{
// 定义属性
数据类型 属性名() [default 属性值];
}
⭐ 注意:注解名和类名的命名要求一致,属性定义可以有,可以没有。有属性定义时,当设置默认属性值时类型必须和声明的数据类型一致。
⭐ 还可以在定义注解时定义属性,在注解类型的定义中以无参方法的形式来声明,其方法名和返回值分别定义了该属性的名字和类型
例子
创建 MyAnnotation.java 和 TestAnnotation.java 源文件,使用 MyAnnotation 注解:
public class TestAnnotation {
@MyAnnotation("good")
public void getObjectInfo(){
}
}
public @interface MyAnnotation{
//定义一个属性 value
String value();
}
如果没有写属性名,而这个注解又只有 value 属性,则将这个值赋给 value 属性。
如果注解里面有多个属性怎么办呢?
@MyAnnotation(value="good",name="Tom",age=18)
这里我有一个问题,注解赋值只能在使用里面吗?
在注解中可以定义属性,也可以给属性赋默认值,代码如下:
public @interface MyAnnotation{
// 定义两个属性 name 和 age,带上默认值
String name() default "张三";
int age() default 18;
}
name 和 age 使用默认值 @MyAnnotation(value="Java") public void getDefaultInfo(){ }
⭐ 注意:使用带属性的注解时,需要给属性赋值。不过如果在定义注解时给属性赋了默认值,则可使用不带属性值的注解,也就是让注解使用自己的默认值。
Java 元注解
啥?元注解?哦,是之前元数据的注解吗?
不对哦,Java 元注解主要是存放在 java.lang.annotation 包中,所有的注解的本质就是一个继承了 Annotation 接口的接口。元注解是用于修饰注解的注解,通常用在注解的定义上。
JDK 5 中主要提供了 4 个元注解:
@Target 注解使用
@Target 注解的目的是用于指定被修饰的注解能用于修饰哪些程序元素(属性、方法、类或者接口等)。
用 @Target 进行注解,以限制此注解只能使用在属性上。
TestAnnotation2.java 源文件,并写入以下代码:
//限制此注解只能使用在属性上
@Target(ElementType.FIELD)
@interface MyAnnotation2{
String name() default "zhangsan";
int age() default 18;
}
@Target 元注解的使用
public class TestAnnotation2 {
//在方法上使用自定义注解
@MyAnnotation public void getObjectInfo() {
}
@Retention 注解使用
当 @Retention 注解的属性 value 设置为 RetentionPolicy.RUNTIME 时,编译器将把注解记录在 class 文件中,当运行 Java 程序时,虚拟机保留注解,程序可以通过反射获取该注解
// 可以在不同的成员上加注解,多个是需要加上{},用逗号分隔。
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD,
ElementType.PARAMETER,ElementType.CONSTRUCTOR,ElementType.LOCAL_VARIABLE})
//当运行 Java 程序时,虚拟机保留注解
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String name() default "zhangsan";
int age() default 18;
}
//@Retention 注解的使用
public class TestAnnotation3 {
@MyAnnotation3(name="Rose", age=10)
private String info;
@MyAnnotation3 public void getObjectInfo() { }
@Documented 注解使用
使用 javadoc 生成文档时,注解将被忽略掉。如果想在文档中也包含注解,就需要使用 @Documented 为文档注解。
@Documented 元注解类型中没有成员变量。
如果定义注解时使用了 @Documented 修饰,则所有使用该注解修饰的程序元素的 API 文档中都将包含该注解说明。
@Documented
@interface MyAnnotation4 {
String name() default "zhangsan";
int age() default 18;
}
@MyAnnotation4
public class TestAnnotation4{
}
@Inherited 注解的使用
注解是程序的一个基本组成部分,那么父类的注解是否被子类继承呢?
默认情况下,父类的注解不被子类继承,如果要想继承父类注解,就必须使用 @Inherited 元注解。
import java.lang.annotation.Inherited;
@Inherited
@interface MyAnnotation5{
String name() default "zhangsan";
int age() default 18;
}
@MyAnnotation5
class Person {
public void getInfo(){
//省略若干代码
}
}
class Student extends Person {
//省略若干代码
}