注解是 JDK5.0 才开始引入的技术.
1.1 什么是注解
以下引用维基百科的解释, 看不懂也没事, 我会做进一步解释.
Java 的注解是一种声明元数据的语法, 所谓的元数据. 我在前面的文章有做过解释, 即是用来
描述数据的数据
.
注解是一种元数据, 它可以被加到 Java 源代码, 类, 方法, 变量, 参数, 包.
注解描述的信息可以从源码中读出, 并且因为
注解可以嵌入到字节码文件
, 所以注解也可以被保留到运行时, 并通过反射的技术进行读取注解信息.
In the Java computer programming language https://en.wikipedia.org/wiki/Java_(programming_language) , an annotation is a form of syntactic metadata https://en.wikipedia.org/wiki/Metadata that can be added to https://en.wikipedia.org/wiki/Java_(programming_language) source code.[1] https://en.wikipedia.org/wiki/Java_annotation#cite_note-1 Classes, methods, variables, parameters and packages may be annotated. Like https://en.wikipedia.org/wiki/Javadoc tags, Java annotations can be read from source files. Unlike https://en.wikipedia.org/wiki/Javadoc tags, Java annotations can also be embedded in and read from class files https://en.wikipedia.org/wiki/Class_(file_format) generated by the compiler. This allows annotations to be retained by Java VM at run-time https://en.wikipedia.org/wiki/Run_time_(program_lifecycle_phase) and read via reflection https://en.wikipedia.org/wiki/Reflection_(computer_science) .[2] https://en.wikipedia.org/wiki/Java_annotation#cite_note-2 It is possible to create meta-annotations out of the existing ones in Java.[3] https://en.wikipedia.org/wiki/Java_annotation#cite_note-3
1.2 注解的语法
通过上面的介绍, 我们可以推断注解主要是用在
为 java 中的各种类型提供元数据, 并结合反射技术, 在运行时进行读取
. 我们先来了解下注解的语法, 写个最简单的注解.
语法
注解的语法和接口的语法十分类似. 它们最显著的不同在于, 注解类型的关键字是 @interface, 其次注解是使用
方法类声明元素
. 注解中声明的一个方法表示这个注解类拥有的这个元素和其对应的类型. 如下
[public | 缺省] @interface 注解名 () {
返回类型 元素名或者说是属性名 () default 默认值;
}
修饰符
可以是 public 或者缺省
返回类型 (属性类型)
返回类型其实就是注解类中元素的类型或者说是其属性的类型. 注解的返回类型只能是基本数据类型, 类, 枚举, 注解,
或者是这四种类型构成的数组
最简单的注解
- package com.sweetcs.study.annotation;
- public @interface MyAnnotation {
- }
测试下注解可以加入的位置, 如下代码.
直接给包贴上注解是会报错的
. 其他测试都没有问题, 在实际中在包上贴注解也不是很常用, 后面在进行研究.
- //@MyAnnotation
- package com.sweetcs.study.annotation;
- @MyAnnotation
- public class TestAnnaotation {
- @MyAnnotation
- public void myPrint(@MyAnnotation int a, @MyAnnotation Object b) {
- }
- public static void main(String[] args) {
- @MyAnnotation
- String name = "test";
- System.out.println(name);
- }
- }
尝试了注解的最简单的结构. 同时我们也尝试了在类上贴上注解, 在
方法上贴上注解
, 在
参数上贴上注解
, 在
变量上贴上注解
. 那么它们有什么作用呢? 我们以 Java 中内建的常用三个注解来举例.
1.3 Java 内建的三个注解
@SuppressWarnings
抑制警告信息注解
该注解的功能是消除编译器的警告信息. 如下我是贴在 year 变量上, 所以只对 year 变量有用. 并且主要是抑制
变量没有使用警告
和方法过期警告
- @Test
- public void testBuildInAnnotation() {
- Date date = new Date();
- @SuppressWarnings({ "deprecation", "unused" })
- int year = date.getYear();
- }
- @Deprecated
该注解表示该方法已经过期, 不推荐使用, 如果使用编译器会给出警告
- @Deprecated
- public int getYear() {
- return normalize().getYear() - 1900;
- }
上面代码即使 Date 类中的 getYear
@Override 检查贴上取的方法是否是复写父类的方法.
该注解一般是用在子类的同名方法中, 用来保证子类的同名方法是在复写父类的方法. 如果不是, 贴上该注解的方法编译器会给出错误, 让你去更改
以上三个注解在 java 的经常的使用, 我们打开 @Override 和 @注解, 看起内部的定义的.
- @Target(ElementType.METHOD)
- @Retention(RetentionPolicy.SOURCE)
- public @interface Override {
- }
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
- public @interface Deprecated {
- }
可以很清楚的看到这两个注解, 没有任何的成员方法. 所以这两个注解其实只是相当于一个标签, 贴上标签后结合编译器和运行时可以玩出不同花样.
还可以看出注解的上面还有 @Target,@Retention,@Documented 三个注解. 这三个注解又称为元注解, 即描述注解的注解. 下面我们分别介绍下这三个注解的功能.
元注解
@Target
该元注解可以贴在那个地方, 即使由这个注解来指定说明. 其描述的属性是一个 value 数组,
数组元素的类型是 ElmentType 枚举类型
- public enum ElementType {
- TYPE,
- FIELD,
- METHOD,
- PARAMETER,
- CONSTRUCTOR,
- LOCAL_VARIABLE,
- ANNOTATION_TYPE,
- PACKAGE,
- TYPE_PARAMETER,
- TYPE_USE
- }
- @Retention
该元注解表示保留注解到哪个阶段, 即用于指定注解的生命周期. 其描述注解的生命周期的属性类型是枚举.
- public enum RetentionPolicy {
- SOURCE,
- CLASS,
- RUNTIME
- }
- @Documented
该元注解用来表示贴在这些地方的注解是否可以被 javadoc 解析. 贴上了 @Documented 表示可以被解析.
@ Inherited
该元注解用来表示一个类的注解是否可以被子类继承. 默认是不行的.
我们开发中最常用的是前两个, 也是必不可少的.
1.4 自定义注解
接扫了注解的定义语法, 描述注解的元注解. 我们可以自己进行自定义注解了.
对于返回值是数组类型的, 我们可以使用
方法名 = {类型值 1, 类型值 2}
进行赋值
并且我们需要用元注解, 说明自定义注解的生命周期和注解可以贴在哪些地方
自定义步骤
声明注解类型和其方法.
使用元注解 @Retation 和 @Target 说明注解的使用范围和注解的生命周期
- package com.sweetcs.study.annotation;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- @Retention(RetentionPolicy.RUNTIME)
- @Target(value={ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER})
- public @interface MyCustomerAnnotation {
- String name() default "SweetCS";
- String[] sons();
- }
- @Test
- public void testCustomerAnnotation() {
- @MyCustomerAnnotation(name="test", sons={"son1", "son2"})
- int a = 1;
- }
1.5 注解的本质和便捷写法
注解的本质
我们写的注解底层其实是会在编译阶段自动的编译成继承
java.lang.annotation.Annotation 接口
的接口. 但是不允许我们显示的继承自该接口.
注解简便写法
阅读底层注解的实现代码, 我们会发现其方法名无论返回类型是啥. 方法名都清一色的命名为 value. 这是因为 Java 的注解中规定.
只要方法名为 value, 在使用注解的时候, 不同指定注解的元素名
. 如下代码
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.ANNOTATION_TYPE)
- public @interface Retention {
- /**
- * Returns the retention policy.
- * @return the retention policy
- */
- RetentionPolicy value();
- }
我们在使用 @Retention 的使用
不需要指定元素名
. 默认就是 value
1.6 获取注解信息
为了运行时能准确获取到注解的相关信息, Java 在 java.lang.reflect 反射包下新增了
AnnotatedElement 接口
, 它主要用于表示目前正在 VM 中运行的程序中已使用注解的元素. 实现了 AnnotatedElement 的类. 可以看到
实现 AnnotatedElement 接口的类都是和反射相关的类
, 所以我们必须结合反射技术来获取注解信息.
image.png
下面代码, 使用反射来获取 Father 类的 Class 对象, 然后获取其上的注解
- @Test
- public void testCustomerAnnotation() {
- // 获取 Father 类上的注解
- MyCustomerAnnotation annOfFatherClass = Father.class.getAnnotation(MyCustomerAnnotation.class);
- System.out.println(annOfFatherClass);
- System.out.println(annOfFatherClass.name() + " " + Arrays.toString(annOfFatherClass.sons()));
- }
运行输出
后续
注解使用场景主要是反射结合使用. 因为反射结合使用, 所以注解使用的最多的是在类上和类中的成员上. 后续我会分享基于注解和反射来动态的创建表. 可以大大提高我们的编程体验, 让我们无需去关注建表语句.
来源: http://www.jianshu.com/p/7cefe50cca5d