Q1:什么是注解,他们的典型用例是什么
Q2:请说出有哪些常用注解
Q3:怎么自定义注解
Q3.1:注解方法中可以返回哪些类型
Q3.2:怎么限制注释的元素对象
Q3.3:自定义注解怎么实现我们想要的逻辑功能(简要描述主要思想)
Q4:说一下什么是重复注解
Q5:说一下注解的继承关系,它是怎么实现的
Q6:定义一个@Getter 和 @Setter注解,他们只能修饰成员变量。为这两个注解编写APT工具,APT工具会为他们修饰的成员变量对应添加getter、setter方法。
使用注解的优势
之前总是用大量的xml来实现描述元数据,但是xml太过于繁琐和松耦合。在有些情况下我们希望使用一些较为简单的方式来描述元数据,来达到我们快速开发的目的,还有些特殊情况,我们希望用紧耦合的一下东西来描述元数据。这就用到了注解,现在大多数的框架都是xml配饰和注解相结合使用,平衡这两种方式的利弊。
常用注解
-
java.lang包下的五个常用注解
-
@override
- 标记注解
- 只能修饰方法
- 告诉编译器检查这个方法,父类一定要有一个被该方法重写的方法,否则会报错
-
@Deprecated
- java9增强获得两个属性
- forRemoval:该Boolean类型的属性指定该API将来是否会被删除
- since:该String类型的属性指定API从那个版本被标记过期
@Deprecated public void deprecated(){ } new Appel().deprecated();// 编译器警告显示该方法过期
- java9增强获得两个属性
-
@SuppressWarnings
- 备注解修饰的元素及其子元素,取消显示编译器警告
-
@SafeVarargs
- 标记注解
- 消除堆污染警告,当把一个不带泛型的对象赋给一个带泛型的对象,往往会发生堆污染
-
@Functionallnterface
- 和override类似
- 告诉编译器,该接口是一个函数式的接口(一个接口中只有一个抽象方法 -> 可以使用拉姆达表达式)
提取注解的信息
- getAnnotation(Class annotation)
- 返回该程序元素上存在的指定类型的注释,如果该类型的注释不存在,则返回null
- Annotation[] getAnnotations()
- 返回该程序元素上存在的所有注释
- boolean isAnnotationPresent(Class<? extends AnnotationClass>)
- 判断该程序元素上是否包含指定类型的注释,存在则返回true,否则返回false
jdk的元注解
- 元注解就是注解注解的注解,在我们自定义注解的时候使用
- java.lang.annnotation包下的6个Meta注解,其中四个常用注解
- @Retention
- 只能用于修饰注解定义
- 指定被修饰的注解可以保留多少时间
- 包含一个RetentionPolicy类型的value的成员变量
- 三个value的值
- RetentionPolicy.CLASS
- 默认值
- 注解记录在class文件中
- 运行java程序时,jvm不可获取注解信息
- RetentionPolicy.RUNTIME
- 注解记录在class文件中
- 运行java程序时,jvm也可以获取注解信息
- 程序可通过反射获取该注解
- RetentionPolicy.SOURCE
- 注解值保留在源代码中,编译器直接丢弃这种注解
- RetentionPolicy.CLASS
- @Target
- 指定被修饰的注解作用于哪些程序单元
- 8个value的值
- ElementType.ANNOTATION_TYPE
- 指定该注解只能修饰注解
- ElementType.CONSTRUCTOR
- 修饰构造器
- ElementType.FIELD
- 修饰成员变量
- ElementType.LOCAL_VARIABLE
- 修饰局部变量
- ElementType.METHOD
- 修饰方法
- ElementType.PACKAGE
- 修饰包
- ElementType.PARAMETER
- 修饰参数
- ElementType.TYPE
- 修饰类 、接口(包括注解累些)、枚举
- ElementType.ANNOTATION_TYPE
- @Decumented
- 指定被该元注解修饰的注解类将被javadoc工具提取成文档
- 使用该注解修饰的程序元素的API文档中将会包含该注解说明
- @Inherited
- 指定被修饰的注解具有继承性
- 使用该注解的子类也会被该注解修饰
自定义注解
- 使用@interface关键字
- 可以设定参数
- 可以给参数设定默认值
- 注解分类
- 标记注解
- 没有定义成员变量的注解
- 仅利用自身的存在与否来提供信息
- 元数据注解
- 包含成员变量的注解
@Retention(RetentionPolicy.RUNTIME) // jvm可以获得参数信息 @Target(ElementType.METHOD) // 该注解是执行在方法上面的 @Documented //可以被提取为文档 public @interface Annotation1 { String colour() default "red"; int time() default 20; }
- 标记注解
提取注解信息
- 注解不会自动生效,需要开发者提取响应的工具来提取并处理注解信息
- java.lang.annotation.Annotation
- 接口
- 表示程序元素前面的注解
- 所有注解的父类
- java.lang.reflect.Annotation
- 接口
- 主要实现类
- Class
- Constructor
- Field
- Method
- Package
- 通过发射获取AnnotatedElement对象后,可以调用该对象的几个方法访问注解信息
- getAnnotation …
Annotation[] annotations = Class.forName("day01.Fruit").getMethod("deprecated").getAnnotations(); for (Annotation an : annotations){ System.out.println(an); }
java8的重复注解
- 在8之前不予许在同一个程序元素上使用重复类型的注解,8之后可以
- 自定义重复注解
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface Annotation1 { String colour() default "red"; int time() default 20; } //重复注解 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface RepeatAnnotation { Annotation1[] param(); //注解参数类型类型是另外一个注解 }
java8新增的类型注解
java8为ElementType枚举增加了TYPW_PARAMETER、TYPE_USE两个枚举类,这样就允许定义枚举是使用@Targer(ElementType.TYPE_USE)修饰,这种注解别称为类型注解,类型注解课用于修饰在任何地方出现的类型。
类型注解可以让编译器更严格的代码检查,从而提高程序的健壮性
//自定义一个类型注解
@Target(ElementType.TYPE_USE)
public @interface NotNull {
}
//类型注解可以修饰任何地方出现的类型
@NotNull
public class TypeAnnotation implements @NotNull Serializable {
public static void main(String[] args) {
Object obj = "Hellolvzi";
String str = ( @NotNull String) obj;
}
public void foor(Class<? extends @NotNull String> info){
}
}
编译时处理注解
该内容涉及到了反射,在反射章节会来解决这个问题
注解的继承关系
所有的注解都是继承与java.lang.annotation.Annotation
public @interface aa {
}
但是在我们自定义注解的时候并没有写extends,来继承这个Annotation。但是在看annotation继承关系的时候(idea atrl + h 打开继承树 ,可以看我的这篇博客自己工作总结常用快捷键),我们看到这我们的注解继承与annotation
那么它是怎么实现的呢?
如果你想不到不用extends确有继承关系,那么你一定不理解为什么Object是所有类的父类。
请移步我的这篇文章,之前写过不想在粘贴麻烦各位了。
同理,我们自定义注解的继承关系也是在编译阶段完成的,当识别到@interface,编译器会给你添加一个默认的父类Annotation,而不是object
许多知识都是相互联系的,最好的记忆方法就是把所有的知识形成一个框架