注解的作用

  • 编译检查
    • 注解具有”让编译器进行编译检查的作用”,如@Override放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出;
  • 编写文档
    • 通过代码里标识的注解生成文档,常用的有@see@param@return 等;
  • 能够帮忙查看查看代码
    • 通过代码里标识的注解对代码进行分析(使用反射),通过@Override, @Deprecated等,我们能很方便的了解程序的大致结构;
  • 跟踪代码依赖性,实现替代配置文件功能
    • 因为注解大多都有自己的配置参数,而配置参数以键值对的方式出现,所以从某种角度来说,可以把注解看成是一个XML元素,该元素可以有不同的预定义的属性。

      替代配置文件的举例

  1. 定义一个注解 ```java package configuration;

import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface FruitProvider { int id() default -1;//供应商编号

  1. String name() default "";//供应商名称
  2. String address() default "";//供应商地址

}

  1. 2. 对一个类使用注解
  2. ```java
  3. package configuration;
  4. @FruitProvider(id = 1, name = "盒马", address = "杭州")
  5. public class Apple {
  6. private int providerId;
  7. private String providerName;
  8. private String providerAddress;
  9. public void setProviderId(int providerId) {
  10. this.providerId = providerId;
  11. }
  12. public void setProviderName(String providerName) {
  13. this.providerName = providerName;
  14. }
  15. public void setProviderAddress(String providerAddress) {
  16. this.providerAddress = providerAddress;
  17. }
  18. @Override
  19. public String toString() {
  20. return "Apple{" +
  21. "providerId=" + providerId +
  22. ", providerName='" + providerName + '\'' +
  23. ", providerAddress='" + providerAddress + '\'' +
  24. '}';
  25. }
  26. }
  1. 注解处理器 ```java package configuration;

import java.lang.reflect.Constructor;

public class FruitInfoUtil { public static Apple getAppleProvider(Class clazz) throws Exception { FruitProvider provider = clazz.getAnnotation(FruitProvider.class); Constructor constructor = clazz.getConstructor(); Apple apple = constructor.newInstance(); apple.setProviderId(provider.id()); apple.setProviderName(provider.name()); apple.setProviderAddress(provider.address()); return apple; } }

  1. ```java
  2. package configuration;
  3. public class AppleTest {
  4. public static void main(String[] args) throws Exception {
  5. Apple apple = FruitInfoUtil.getAppleProvider(Apple.class);
  6. System.out.println(apple);
  7. }
  8. }

内置注解

@Overwrite

若某个方法被 @Override 的标注,则意味着该方法会覆盖父类中的同名方法。如果有方法被 @Override 标示,但父类中却没有”被 @Override 标注”的同名方法,则编译器会报错。

@Deprecated

该注解标注的内容,已过时,不建议再使用,编译器会给相应的提示信息

@SuppressWarnings

指示编译器去忽略注解中声明的警告

自定义注解

注解格式

  1. 元注解
  2. public @interface 注解名称{
  3. }

注解本质

反编译后:

public interface cn.linsman.MyAnnotation extends java.lang.annotation.Annotation {
}

所以注解本质上就是一个接口,该接口默认继承Annotation接口

注解属性

注解属性就是接口中的抽象方法

属性返回值类型**:

  • 基本数据类型
  • String
  • 枚举
  • 注解
  • 以上四种类型的数组

    属性赋值

  • 定义了属性,在使用时需要给属性赋值:

    • 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不必需赋值
    • 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可
    • 数组赋值时,值使用{}包裹,如果数组只有一个值,则{}可以省略

      自定义注解的步骤

      @interface定义注解

      public @interface Report{
      }
      

      添加参数、默认值

      public @interface Report{
      int type() default 0;
      String level() default "info";
      String value() default "";
      }
      
      把最常用的参数定义为value(),推荐所有参数都尽量设置默认值。

      元注解

      用于描述注解的注解

  • @Target:描述注解能够作用的位置

    • 类或接口:ElementType.TYPE
    • 字段:ElementType.FIELD
    • 方法:ElementType.METHOD
    • 构造方法:ElementType.CONSTRUCTOR
    • 方法参数:ElementType.PARAMETER
  • @Retention:描述注解被保留的阶段

描述

  • 仅编译期(只在.java代码文件中):RetentionPolicy.SOURCE
  • 仅class文件(只在.class文件中,不会加载到JVM):RetentionPolicy.CLASS
  • 运行期(保留到class文件中,并被JVM读取到,运行时可以通过反射访问):RetentionPolicy.RUNTIME
  • 如果@Retention不存在,则该Annotation默认为RetentionPolicy.CLASS
    • @Document:注解是否会抽取到api文档中
    • @Inherited:注解是否会被子类继承

      推荐阅读