疯狂Java讲义 第十四章 Annotation(注解)

jefxff 153,591 2020-04-20

1. Annotation 基础

  1. Annotation (注解) 是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理, 注解本身不直接影响代码的执行
  2. 通过使用注解, 程序开发人员可以在不改变代码原有逻辑的情况下, 在源文件种嵌入一些补充信息, 通过反射, 就可以获取这些注解的信息
  3. 5个基本的Annotation:
    • @Override 限定重写父类的方法, 如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误
    • @Deprecated 标示已过时, 如果使用该方法,会报编译警告
    • @SuppressWarnings("all") 抑制编译器警告, 指示编译器去忽略注解中声明的警告
    • @SafeVarargs Java7的 "堆污染" 警告
    • @FunctionInterface Java8的函数式接口, 检查该接口是否只有一个抽象方法, 不给不是就报编译警告

1.1 限定重写父类方法: @Override

@Override 就是用来指定方法覆载的, 它可以强制一个子类必须覆盖父类的方法, 它的作用是告诉编译器检查这个方法, 保证父类要包含一个被该方法重写的方法, 否则就会编译错误; @Override 只能修饰方法

代码示例

public class Fruit {
    public void info(){
        System.out.println("水果中的 info() 方法");
    }
}
class Apple extends Fruit{
    @Override
    public void info(){
        System.out.println("苹果中的 info() 方法");
    }
}

1.2 标示已过时: @Deprecated

@Deprecated 用于表示某个程序元素(类, 方法等)已过时, 当其他程序使用了已过时的类, 方法时, 编译器会给出警告

代码示例

class Apple {
    // 定义 info 方法已过时
    @Deprecated
    public static void info() {
        System.out.println("苹果中的 info() 方法");
    }
}
public class DeprecatedTest {
    public static void main(String[] args) {
//        编译器警告, 使用的 info() 方法已过时
//        info();
    }
}

1.3 抑制编译器警告: @SuppressWarnings

@SuppressWarnings 指示该Annotation修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告

代码示例

// 关闭整个类里的编译器警告
@SuppressWarnings(values="unchecked")
public class SuppressWarningsTest {
    public static void main(String[] args) {
        List<String> myList = new ArrayList();
     }
}

1.4 Java7的 "堆污染" 警告: @SafeVarargs

"堆污染(Heap pollution)": 当把一个不带泛型的对象赋给一个带泛型的变量时, 往往会发生这种"堆污染"

1.5 Java8的函数式接口: @FunctionInterface

Java8 规定: 如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法), 该接口就是函数式接口
@FunctionInterface 就是用来指定某个接口必须是函数式接口

代码示例

@FunctionalInterface
public interface Demo {
    int getStatus();
    
    static void fun(){
        System.out.println("It's Fun !");
    }
    
    default void bar(){
        System.out.println("it's a bar");
    }
}

2. 元注解

元注解理解 就是用在注解上面的注解, 就是通过这几个注解来指定或限制注解的作用或功能; 有 @Retention、 @Target、 @Document、 @Inherited和@Repeatable(JDK1.8加入)五种元注解

2.1 @Target 元注解

  • Target 翻译的意思就是目标, 用于@Target修饰的注解, 指定该注解作用范围, 这个范围是通过ElementType枚举类来指定的, 即将 ElementType 枚举类的实例, 作为参数传递给 @Target()

  • ElementType 枚举类

package java.lang.annotation;

public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */

    FIELD,              /* 字段声明(包括枚举常量)  */

    METHOD,             /* 方法声明  */

    PARAMETER,          /* 参数声明  */

    CONSTRUCTOR,        /* 构造方法声明  */

    LOCAL_VARIABLE,     /* 局部变量声明  */

    ANNOTATION_TYPE,    /* 注释类型声明  */

    PACKAGE             /* 包声明  */
}

2.2 @Retention 元注解

  • Retention 翻译的意思是保留, 将该元注解用于注解, 就是指定该注解存在的阶段, 从 resource, calss, 到 runtime 三个阶段, 一般情况下是指定用于 runtime阶段; 这个阶段也是通过枚举类 RetentionPolicy 来指定的, 即将 RetentionPolicy 枚举类的实例, 作为参数传递给 @Retention()

  • RetentionPolicy 枚举类

package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,            /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了  */

    CLASS,             /* 编译器将Annotation存储于类对应的.class文件中。默认行为  */

    RUNTIME            /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}

2.3 @Documented 元注解

  • Documented 翻译是文档的意思, 该元注解的作用是确定是否将@Documented修饰的注解中的元素包含到 javadoc 中, 写了@Documented元注解就包含, 不写就不包含

2.4 @Inherited 元注解

  • Inherited 翻译是继承的意思, @Inherited元注解修饰的注解修饰了一个类, 那么这个类的派生类也会继承这个注解, 不写该元注解就不会继承

2.5 @Repeatable 元注解

  • Repeatable 翻译是可重复的, @Repeatable元注解修饰的注解可以同时作用一个对象多次, 且每次作用注解可以代表不同的含义

3. 自定义注解

自定义注解, 首先要记住语法; 其次可以通过元注解来指定该注解的作用或功能; 最后, 就是要理解注解中属性定义的特殊语法, 看着和定义抽象方法很类似, 但是在这里他却不是抽象方法, 而是定义的该注解的属性, 且该属性是可以有默认值的, 即定义该注解的时候, 可以指定默认值

  • 注解的属性可以是 8个基本数据类型, String, 枚举类型, 注解类型, Class类型, 或前面这些类型的一维数组类型

代码示例

import java.lang.annotation.*;

/**
 * @author jefxff
 * @date 2020/5/25 - 10:10
 */
public class TestAnnotation {
    @MyAnnotation(name="hello", id=1) // 使用注解
    public static void test(){
        System.out.println("test Annotation");
    }
}

@Target({ElementType.METHOD, ElementType.TYPE}) // 如果指定多个目标, 就用数组形式传递参数
@Retention(RetentionPolicy.RUNTIME) // 指定注解保持的范围是 RUNTIME
@Inherited  // 指定子类可以继承父类的注解
@Documented // 指定该注解会生成在 javadoc 中
@interface MyAnnotation{ // 自定义注解, 按照内部类的形式来写, 这里就不能有 public
    int id();  // 这是定义了注解的属性, 没有default指定默认值, 使用该注解时必须传递该参数
    String name() default "jeff";  // 使用default指定默认值 
}

3.1 注解的本质

  • 要了解注解的本质就要先看一下注解的源码, 注解本质上就是一个 Annotation 接口的子接口, 接口的话可以有属性或方法, 属性的话是 public static final 修饰的, 没有实际的意义; 而接口的方法就类似于注解的属性, "所以这里的属性是带括号的"

Annotation接口源码

// Annotation接口
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();
}

4. 获取注解的属性

了解的注解以及怎么定义注解之后, 最重要的事情就是怎么获取注解的属性, 这个要在学了反射之后才能了解, 先这样, 去学反射啦


# Java