1. Annotation 基础
- Annotation (注解) 是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理, 注解本身不直接影响代码的执行
- 通过使用注解, 程序开发人员可以在不改变代码原有逻辑的情况下, 在源文件种嵌入一些补充信息, 通过反射, 就可以获取这些注解的信息
- 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. 获取注解的属性
了解的注解以及怎么定义注解之后, 最重要的事情就是怎么获取注解的属性, 这个要在学了反射之后才能了解, 先这样, 去学反射啦