【Annotation】详解 Java 中的注解(上)--- 注解的基本介绍

1. 注解概念

首先看看官方对注解的描述:

An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.

大致意思:注解是一种能被添加到 Java 代码中的元数据,类、方法、变量、参数和包都可以用注解来修饰。注解对于它所修饰的代码并没有直接的影响。

简单来说,注解就是一个标记(可以给类、方法、字段等打一个标记)。在 Java 中,使用 @interface 来定义一个注解

定义注解

注解也属于一种类型,同 类(class)一样。

注解通过 @interface 关键字进行定义,如下:

public @interface TestAnnotation {

}

它的形式跟接口很类似,不过前面多了一个 @ 符号。上面的代码就创建了一个名字为 TestAnnotaion 的注解。

那么又是如何使用这个注解的呢?

@TestAnnotation
public class Test {

}

创建一个类 Test,然后在类定义的地方加上 @TestAnnotation 注解, 就可以用 TestAnnotation 注解了。当然,这并不是一个正确的写法。要想真正地去使用注解,还得先了解下 元注解

其实,在底层实现上,所有定义的注解都会自动继承 java.lang.annotation.Annotation 接口

接下来咱们先来自定义一个注解 MyTable,如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTable {
    String name() default "test";
}

通过 JAD 工具反编译后:
在这里插入图片描述

2. 元注解

仔细观察下那块关于注解的代码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTable {
    String name() default "test";
}

发现 MyTable 上面被 @Target@Retention 修饰了。那么它们又是什么呢? ------ 它们就是 Java 中的元注解

什么是元注解?它的作用又是什么?

Java5.0 定义了 4 个标准的 元注解(meta-annotation) 类型,它们被用来提供对其它 Annotation 类型作说明。Java5.0 中定义的元注解:

  • @Target
  • @Retention
  • @Documented
  • @Inherited

这些类型和它们所支持的类位于 java.lang.annotation 包下

2.1 @Target 注解

查看 @Target 源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

@Target 作用:用于描述注解的使用范围

它的取值来源于 ElementType 类型的数组元素 value。其中 ElementType 是一个枚举类型:TYPE、FIELD、METHOD…其含义如下:

  • TYPE:标明该注解可以用于类、接口(包括注解类型)或 enum 声明
  • FIELD:标明该注解可以用于字段(域)声明,包括 enum 实例
  • METHOD:标明该注解可以用于方法声明

如:

// 定义一个注解
@Target(ElementType.TYPE)
public @interface MyTable {
    String name() default "test";
}

// 使用此注解
@MyTable(name = "tab_user")
public class User {
    private String name;
}

自定义一个注解 @MyTable,并将此注解用在一个 User 类上。

2.2 @Retention 注解

查看 @Retention 注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}

@Retention 作用:约束注解的生命周期

它的取值来源于 RetentionPolicy 类型的元素 value。其中 RetentionPolicy 是一个枚举类型:SOURCE、CLASS、RUNTIME。其含义如下:

  • SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)
  • CLASS:注解在 class 文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和 class 文件里,在执行的时候,不会加载到虚拟机中),请注意,当注解未定义 Retention 值时,默认值是 CLASS,如 Java 内置注解,@Override、@Deprecated、@SuppressWarnning 等
  • RUNTIME:注解信息将在运行期(JVM)也保留,因此可以通过反射机制读取注解的信息(源码、class 文件和执行的时候都有注解的信息),如 SpringMvc 中的 @Controller、@Autowired、@RequestMapping 等

2.3 @Documented 注解

查看 @Documented 注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

@Documented 作用:被修饰的注解会生成到 javadoc 中

2.4 @Inherited 注解

查看 @Inherited 注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

@Inherited 作用:可以让注解被继承,但这并不是真的继承,只是通过使用 @Inherited,可以让子类 Class 对象使用 getAnnotations() 获取父类被 @Inherited 修饰的注解

如:

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Doc {
}

@Doc
public class Parent {}

public class Son extends Parent {}

public class Test {
    public void static main(String[] args) {
        A a = new B();
        System.out.println("已使用的@Inherited注解:" + Arrays.toString(a.getClass().getAnnotations()));
    }
}

3 Java 内置注解

看看Java提供的内置注解,主要有3个,位于 java.lang 包下

如下:
@Override:用于标明此方法覆盖了父类的方法,源码如下

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Deprecated:用于标明已经过时的方法或类

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@SuppressWarnnings:用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}

其内部有一个String数组,主要接收值如下:

deprecation:使用了不赞成使用的类或方法时的警告;
unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 
fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
path:在类路径、源文件路径等中有不存在的路径时的警告; 
serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告; 
finally:任何 finally 子句不能正常完成时的警告; 
all:关于以上所有情况的警告。

4. 注解类型元素

之前,在类中可以定义构造方法、属性、方法等。那么,在注解中只可以定一个东西:注解类型元素。它就是注解中的属性,但它却是以类中的成员方法的形式存在的,即需要加括号 “()”。并且,其返回值定义了该属性的类型。如下代码:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface MyTest {
    String name();
    int age();
}

上面代码定义了 MyTest 这个注解中拥有 name 和 age 两个属性。在使用的时候,我们应该给它们进行赋值。

赋值的方式是在注解的括号内以 key=value 的形式,多个属性之前用 ,隔开:

@MyTest(name = "zzc", age = 23)
public class TestAnnotation {
}

注解中的属性可以有默认值,默认值需要用 default 关键值指定:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyTest {
    String name() default "java";
    int age();
}

MyTest 注解中的 name 属性默认为 “java”

它可以这样应用:

@MyTest(age = 23)
public class TestAnnotation {
}

因为有默认值,所以无需要再在 @MyTest 后面的括号里面进行赋值了。

定义注解中的属性,它的数据类型必须是:

  • 所有基本类型(int, float, boolean, byte, double, char, long, short)
  • String
  • Class
  • enum
  • Annotation
  • 上述类型的数组

如:

// 定义一个注解 MyTable
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTable {
    String name() default "test";
}

public @interface MyTest {
    // 声明一个枚举
    enum Status {FIXED, NORMAL};

    Status status() default Status.FIXED;
    boolean isTrue() default false;
    String name() default "test";
    Class clazz() default Void.class;
    MyTable table() default @MyTable(name = "table");
    int[] value() default {1, 2, 3};
}

5. 特殊用法

5.1 特殊语法一:

如果注解本身没有注解类型元素,那么在使用注解的时候可以省略(),直接写为:@注解名,它和标准语法@注解名()等效!

如:

@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
// 等效于 @TestAnnotation()
@TestAnnotation
public class Test {}

5.2 特殊语法二:

如果注解本本身只有一个注解类型元素,而且命名为value,那么在使用注解的时候可以直接使用:@注解名(注解值),其等效于:@注解名(value = 注解值)

@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    String value();
}
// 等效于 @TestAnnotation(value = "This is a Test")
@TestAnnotation("This is a Test")
public class Test {}

5.3 特殊用法三:

如果注解中的某个注解类型元素是一个数组类型,在使用时又出现只需要填入一个值的情况,那么在使用注解时可以直接写为:@注解名(类型名 = 类型值),它和标准写法:@注解名(类型名 = {类型值})等效!

@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
    String[] name();
}
// 等效于 @TestAnnotation(name = {"This is a Test"})
@TestAnnotation(name = "This is a Test")
public class Test {}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值