Java——注解(Annotation)

一、注解简介

1、注解是什么

Java 的注解(Annotation)是一种特殊的语法,用于在代码中添加元数据(Metadata)。

2、注解有什么用

注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段。

二、注解的定义与使用

1、注解的定义

1)基本语法

注解是使用 @interface 关键字定义的,并且默认继承自 Annotation 接口。基本的语法如下:

public @interface MyAnnotation {
    String value() default "";
    int count();
}

2)注解的属性

    String value() default "";
    int count();

这两个都是注解的属性,它们是以这种抽象方法的格式定义的,这样可以支持更多类型,因为方法的返回类型比字段可以定义的类型多:

public @interface MyAnnotation {
    // 方法返回Class类型
    Class<?> classType() default Object.class;

    // 方法返回注解类型
    MyAnnotation nestedAnnotation() default @MyAnnotation;
}

3)默认值

可以使用 default 关键字为注解的属性添加默认值,这样在使用注解时,可以不为这一项传入属性值。

如果一个属性没有默认值,则在使用时必须传入属性值。

2、注解的使用

1)基本语法

注解可以用在类、方法、字段、参数等多种地方。例如:

class SuperClass {
	public void foo() {
		System.out.println("SuperClass~");
	}
}

class SubClass extends SuperClass {
	@Override
	public void foo() {
		System.out.println("SubClass~");
	}
}

2)有属性的注解的使用

如果一个注解有属性,而且这个属性有默认值,则可以不传入属性值,这时属性值为默认值:

@interface MyAnnotation {
	int attribute() default 0;
}

@MyAnnotation()
class Sample {

}

如果这个属性没有默认值,则在使用注解时就需要传入属性值:

@interface MyAnnotation {
	int attribute();
}

@MyAnnotation(attribute = 0)
class Sample {
	
}

而且这里必须使用 属性名 = 属性值 的格式传值。

如果有多个属性,传值也是类似的:

@interface MyAnnotation {
	int attribute1();
	int attribute2();
}

@MyAnnotation(attribute1 = 1, attribute2 = 2)
class Sample {

}

3)属性名为 value

当一个注解中只有一个属性时,如果这个属性被命名为 value,则在使用该注解时可以省略属性名称。这是为了提高代码的简洁性和易读性。

@interface MyAnnotation {
	int value();
}

@MyAnnotation(1)
class Sample {

}

如果只有一个属性,属性名不是 value 的话,这样写就会报错:

@interface MyAnnotation {
	int attribute();
}

@MyAnnotation(1) // 这里会报错,必须写成 @MyAnnotation(attribute = 1)
class Sample {

}

如果有多个属性,属性中有 value 这个属性的话,也需要按 属性名 = 属性值 的格式指明每一个属性的属性值:

@interface MyAnnotation {
	int value();
	int attribute();
}

@MyAnnotation(value = 1, attribute = 1)
class Sample {

}

三、元注解

元注解是用来修饰注解的注解。

1、@Retention

@Retention 注解定义了一个注解的生命周期,即该注解在哪个阶段可用。

1)属性可取值

  • RetentionPolicy.SOURCE:注解仅在源代码中存在,编译器使用后就会丢弃它,在编译后不会保留。
  • RetentionPolicy.CLASS:注解在编译后仍然存在于类文件中,但在运行时虚拟机不保留注解。
  • RetentionPolicy.RUNTIME:注解在运行时仍然可用,运行时虚拟机会保留注解,可以通过反射读取。

对于 @Retention 的元素的默认值是 RetentionPolicy.CLASS。

2)源代码

我们可以查看 @Retention 的源代码:

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

对于:

@Documented

表明 @Retention 元注解会保留在 Javadoc 中。

对于:

@Retention(RetentionPolicy.RUNTIME)

表示 @Retention 会在运行时保留。

对于:

@Target(ElementType.ANNOTATION_TYPE)

表示 @Retention 只能修饰注解类型。

 可以看到 @Retention 的唯一一个属性的类型是 RetentionPolicy 类型,这个类型也是一个枚举类型,它的源码是:

public enum RetentionPolicy {
    SOURCE,
    CLASS,
    RUNTIME
}

3)示例

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

可以看到对于 @Override 是只在源码中保留的。

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

对于 @Deprecated 是运行时也会保留的。

2、@Target

@Target 注解定义了一个注解可以应用于哪些 Java 元素。

1)属性可取值

  • ElementType.TYPE:可以应用于类、接口、枚举或注解。
  • ElementType.FIELD:可以应用于字段。
  • ElementType.METHOD:可以应用于方法。
  • ElementType.PARAMETER:可以应用于方法参数。
  • ElementType.CONSTRUCTOR:可以应用于构造函数。
  • ElementType.LOCAL_VARIABLE:可以应用于局部变量。
  • ElementType.ANNOTATION_TYPE:可以应用于注解类型。
  • ElementType.PACKAGE:可以应用于包声明。

2)源代码

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

对于:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)

第一行表示 @Target 元注解会保留在 Javadoc 中。第二行表示 @Target 注解会在运行时依然保留,最后一行表示 @Target 只能修饰注解类型。

对于 @Target 的属性,是一个 ElementType 类型的数组,对于 ElementType 类型,也是一个枚举类型:

public enum ElementType {
    TYPE,
    FIELD,
    METHOD,
    PARAMETER,
    CONSTRUCTOR,
    LOCAL_VARIABLE,
    ANNOTATION_TYPE,
    PACKAGE,
    TYPE_PARAMETER,
    TYPE_USE
}

3)示例

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

可以看到,@Override 只能修饰方法类型。

3、@Documented

@Documented表明被它修饰的注解应该被包含在Java文档中(如Javadoc)中。

1)源代码

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

@Documented 元注解没有属性,会在运行时保留,只能修饰注解类型,会被包含在 Javadoc 中。

2)示例

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

这里的代码就说明 @Deprecated 注解会被包含在 Javadoc 中。

4、@Inherited

@Inherited 注解表明一个注解是可继承的。这意味着如果一个类被标记为使用某个注解,那么它的子类可以自动获得这个注解。

1)源代码

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

@Inherited 元注解没有属性,会在运行时保留,只能修饰注解类型,会被包含在 Javadoc 中。

四、Java 内置注解

这里介绍三个基本的注解。

1、@Override

1)介绍

用于标识一个方法是重写父类的方法。该注解只能用于方法。

例如:

class SuperClass {
	public void foo() {
		System.out.println("SuperClass's foo.");
	}
}

class SubClass extends SuperClass {
	@Override
	public void foo() {
		System.out.println("SubClass's foo");
	}
}

2)源代码

我们可以查看 @Override 的源码:

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

可以看到在定义了 @Override 注解时,@Override 的定义前面还加了两个元注解:

@Target(ElementType.METHOD)

这个元注解限制了 @Override 只能修饰方法。

@Retention(RetentionPolicy.SOURCE)

这个元注解限制了 @Override 只会在源代码中存在,编译后不再保留。

2、@Deprecated

1)介绍

表示某个元素(类、方法、字段等)已经过时,不建议使用。仍然可以使用,但是不建议使用,通常是因为这个元素已经被替换为较新的功能。

例如:

@Deprecated
class Sample {
	
}

在使用这个类时,会显示:

可以看到这个类被线划掉了,说明这个类已经过时。

2)源代码

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

这里的源代码说明,@Deprecated 注解会包含在 Javadoc 中,会在运行时保留,可以修饰构造器、字段、局部变量等类型

3、@SuppressWarnings

1)介绍

用于抑制编译器发出的警告。

例如:

public class Example {
	public static void main(String[] args) {
		int i;
	}
}

当我们声明了一个整型变量,而没有使用它时,就会出现警告:

这时,我们就可以使用 @SuppressWarnings 抑制这个警告:

public class Example {
	@SuppressWarnings({"all"}) // 这个注解有属性,所以需要传入属性值,all 代表忽略所有类型警告
	public static void main(String[] args) {
		int i;
	}
}

这样,上面的警告就消失了。

2)源代码

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

这里的源代码说明,@SuppressWarnings 注解能修饰类、接口、注解、枚举、字段等类型,只会在源代码中保留。

3)属性可取值

unchecked:

  • 抑制与未检查的操作相关的警告,通常与泛型类型的使用有关。例如,在将原始类型赋值给泛型参数时可能会出现此警告。

deprecation:

  • 抑制关于使用已弃用(deprecated)API的警告。当使用的类或方法标记为过时时,编译器会发出警告。

rawtypes:

  • 抑制与使用原始类型(raw types)相关的警告。当使用未指定类型参数的集合(如List而不是List<String>)时会出现此警告。

unused:

  • 抑制未使用的变量、方法或类的警告。这在开发过程中可能会使用,以避免对未使用代码的警告。

fallthrough:

  • 抑制在switch语句中缺少break语句导致的警告。

对于 @SuppressWarning 的 value 属性,可取值比较多,上面只是一部分。