注解
(又被称为 元数据
) ,是 Java 提供的一种让我们可以为代码添加信息的一种方式。
元数据
: 中介数据,为描述数据的数据,主要是描述数据属性的信息。
通过注解可以有效的减少样板式代码
,所以注解式开发是现在框架的主流趋势。除此之外,注解还可以进行类型检查 和 注释说明 等作用。
认识注解
从 Class类
可以看出,对于 JVM 而言,注解是一种特殊的接口,并且所有的注解类型都继承了 Annotation接口
。
我们知道一些框架需要一些额外信息才能与源代码协同工作。以前一般是配置文件,现在更喜欢使用注解。因为源代码已经提供了一些有用的信息,例如类名,包名等,所以使用注解会有效的减少配置。
元注解
元注解
: Java 中内置了一些元注解,元注解专职负责注释其他注解。
涉及 | 说明 |
---|---|
@Target |
目标,指注解的作用位置。 |
@Retention |
保留期,指注解的生命周期。 |
@Inherited |
继承,允许子类继承父类的该注解。只会继承作用在 ElementType.TYPE 的注解。 |
@Documented |
文档,生成 javadoc 时会包含注解信息。 |
@Repeatable |
可重复,表示可多次使用,JDK8新增。 |
@Target
@Target
: 表示注解的作用位置,需要设置元素类型集 ElementType[]
。
ElementType枚举值 | 说明 |
---|---|
ElementType.TYPE |
类,接口(包括注释类型)或枚举声明。 |
ElementType.FIELD |
字段声明(包括枚举常量)。 |
ElementType.METHOD |
方法声明。 |
ElementType.PARAMETER |
正式参数声明。 |
ElementType.CONSTRUCTOR |
构造函数声明。 |
ElementType.LOCAL_VARIABLE |
局部变量声明。 |
ElementType.ANNOTATION_TYPE |
注释类型声明。 |
ElementType.PACKAGE |
包声明。 |
ElementType.TYPE_PARAMETER |
输入参数声明, JDK8新增。 |
ElementType.TYPE_USE |
使用一种类型, JDK8新增。 |
@Retention
@Retention
: 表示注解的生命周期,需要设置存活策略 RetentionPolicy
。
RetentionPolicy枚举值 | 说明 |
---|---|
RetentionPolicy.SOURCE |
只存在于源码,编译器将丢弃注释。 |
RetentionPolicy.CLASS |
注释将由编译器*记录在类文件中,但在运行时不需要由VM保留。 这是默认的行为 。 |
RetentionPolicy.RUNTIME |
注释将由编译器记录在类文件中,并且在运行时由VM保留,因此 可以反射性地读取它们 。 |
@Inherited
@Inherited
: 表示被修饰的注解作用于超类后,超类的子类会继承该注解。但只会继承作用在 ElementType.TYPE
的注解。
这种继承是隐蔽的,光从子类无法看出,但可以通过反射获取所继承注解。
@Documented
@Documented
: 表示在生成 javadoc 时,被修饰的注解会被包含在文档中。
@Repeatable
@Repeatable
: 表示被修饰的注解可多次使用,是 JDK8 新增的注解,例如 Spring 的 @Scheduled
可以设置多种定时策略。
注解分类
按注解的生命周期分类:
按生命周期分类 | 说明 | 应用 |
---|---|---|
源码注解 | 注解只在源码中存在,编译成 *.class 文件就不存在了。 |
|
编译时注解 | 注解在源码和 *.class 文件中都存在。 |
TODO一般用于编译期检查。 |
运行时注解 | 注解在运行阶段还会起作用,甚至会影响运行逻辑。 | 第三方技术喜欢用其实现功能逻辑。 |
按注解来源分类:
按来源分类 | 示例 |
---|---|
JDK自带 | @Override , @Deprecated , @SuppressWarnings 等 |
来自第三方的注解 | @Service , @Autowired 等Spring常用注解。 |
自定义的注解 | 可以使用 @interface 等来创建自定义注解。 |
JDK注解
这里介绍一下,日常开发时常会使用到的 JDK注解 。
注解是 JDK1.5 时添加的特性,JDK5只提供了这3个注解。
涉及 | 说明 |
---|---|
@Override |
覆盖,覆盖来自超类型(父类或接口)的方法。 |
@Deprecated |
标识元素过时,使用处编译器会显示警告信息。 |
@SuppressWarnings |
抑制编译器警告,可以制定警告集。例如 @SuppressWarnings("unchecked") 抑制未检查的警告。 |
@Override
@Override
: 可以使用它来表示覆盖超类型(父类或接口)的方法,它是一个源码注解。
实际上,
@Override
是可选的,不写编译器不会报错,但使用在非继承来的方法上会报错。
1 | (ElementType.METHOD) |
@Deprecated
@Deprecated
: 标识元素过时,使用处编译器会显示警告信息,它是一个运行时注解。
1 |
|
@SuppressWarnings
@SuppressWarnings
: 抑制编译器警告,可以制定警告集,它是一个源码注解。
1 | /** |
这里仅列出几个常用的警告关键字,详情可以查看 JLS 。
抑制警告的关键字 | 用途 |
---|---|
all |
压制所有警告。 |
unchecked |
禁止相对于未经检查的操作的警告。 |
cast |
禁止相对于强制转换操作的警告。 |
rawtypes |
在类params上使用泛型时,禁止相对于非特定类型的警告。 |
deprecation |
禁止相对于弃用的警告。 |
使用注解
除去 JDK 和 框架技术外,大多数时候需要 自定义注解 并 编写处理器来解析它们。
自定义注解
基础语法
定义注解需要使用关键字
@interface
,其语法类似接口 ,但不允许继承。注解中可以添加
元素
,看起来十分像接口的方法。注解类可以没有元素,没有元素的注解称为
标识注解
。元素
的类型受限,只能使用 :基本类型
,String
,Class
,enum
,Annotation
和以上类型的数组
。不能使用基本类型的包装类型,但支持自动装箱。
虽然看上去像抽象方法,但
元素
是不允许有 入参 和 异常声明的。元素
不能有不确定的值,即要么使用default
设置默认值,要么使用时提供元素值。默认值不允许使用 null ,为绕开这个约束,可以使用 空字符串 或 负数,来表示不存在。
约定俗成的规范 : 如果只有一个注解元素,则取名为 value()
,这样在使用时可以忽略成员名和赋值号 =
。
1 | package tk.gushizone.java.basic.annotation; |
自定义注解 @Description
注解可以作用于 类型 和 方法 上。
注解的元素赋值一般采用 元素名=元素值
的方式设置,符合约定俗称的规范除外。
1 | package tk.gushizone.java.basic.annotation.entity; |
@Description
注解可以被继承,可以看出注解的继承是比较隐蔽的,无法直接看出。
1 | package tk.gushizone.java.basic.annotation.entity; |
编写注解处理器
如果没有用来读取注解的工具,那注解也不会比注释更有用。 使用 反射
可以获取 注解
提供的 元数据
。
从没有打印
getName:It's User method annotation.
,可以看出@Inherited
只会继承作用在ElementType.TYPE
的注解。
1 |
|
1 | It's BaseItem class annotation. |
常见应用
ORM
这里用注解简单实现一个ORM框架的查询功能,类似于 Hibernate。
定义注解
先定义两个注解,用于表示 数据表 和 字段 。
1 | package tk.gushizone.java.basic.annotation.orm; |
1 | package tk.gushizone.java.basic.annotation.orm; |
定义注解解析器
定义一个工具类用于解析注解,并完成简单查询的 SQL 拼接。
1 | public class ORM { |
注解的使用
将两个注解用于实体类上,形成对象和数据表的映射关系。
1 | package tk.gushizone.java.basic.annotation.entity; |
如此这般,一个简单的动态的条件查询就完成了。
1 |
|