关于java注解

285 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 {
    //省略若干代码
}