47 注解
我们之后学习框架都是基于注解来实现
概念
我们知道注释是文字性描述,介绍代码
而注解也是描述代码的,是给计算机来看的,注解也叫元数据。jdk1.5以后的新特性,是代码级别的说明(用代码来说明代码),与类、接口、枚举在同一个层次,他可以声明在包,类,字段,局部变量,方法等前面,对这些元素进行注释,使用@注解名称
功能
编写文档:根据标识的元数据生成文档
代码分析:根据标识的元数据进行代码分析(使用反射)
编译检查:根据标识的元数据,IDE进行编译检查(如@Override,@FunctionalInterface)
对于第三个功能,我们不用说了,使用特点注解,可以使不满足要求的地方被IDE标红即编译不通过,
下面介绍第一个功能,如何生成文档
1如上,我们定义一个Method类,定义了一个方法add,这里我们在intelliJ里,输入/**按回车就会自动出文档注释,对于方法会自动添加参数和返回值,对于类,我们可以添加author,since,version等,IDE会补全,我们右侧输入我们的注释
2将其拷贝出来放到桌面test文件夹,把和之前定的package依赖那行删掉
3我们右键在此打开cmd,然后输入javadoc Method.java,会发现生成很多网页和js还有别的文件,我们只关心index.html
4 5可以看到我们生成的文档效果
大致了解后,我们注解主要学习JDK中预定义的注解和使用方式,并学会自定义注解
JDK内置注解
需要掌握以下3种
@Override 检测被标识的方法是否继承父类(或接口)的方法
@Deprecated 将标识的内容标记为过时的内容(已有更好的代码替代),不建议使用但是可以用
@SuppressWarnings 压制警告使用
第一个自然不用多说了
6 7我们介绍第二个,之前肯定也遇到过,我们定义个方法add2,然后使用注解2,调用时就会发现有一条横线
8对于第三个压制警告,我们在IDE右侧可以看到小横杆,点上去可以看到是编译期给的警告,如果我们不想看到警告,可以给上面加上@SuppressWarnings
9 10更一般情况我们会把压制警告写到类开头,就会压制类的所有警告,这个注解有字符串参数,我们一般使用"all"
自定义注解
我们知道了jdk内置注解,就会想使用自定义注解,先别急,我们对@Deprecated Ctrl+B查看下,如下
11可以看到注解里面最开始是一大段说明,然后是几行注解,有关的四虎是public @interface Deprecated{}
注解格式定义 新建注解名.java
几行元注解
public @interface 注解名{}
关于元注解后面讲
12 13我们简单定义了注解,然后就可以使用注解了,但是现在注解好像没什么用呢- -
注解本质
这里可以简单了解下java的反编译,
14我们将刚才的注解文件烤出,右键打开cmd,先Javac输出.class文件,然后使用javap反编译字节码,可以坎大搜代码的本质是接口继承自java.lang.annotation.Annotation
注解属性(接口里可以定义的内容)
前面也说了注解本质是接口,我们现在就关心注解里可以定义的抽象方法(也就是注解属性)
我们属性的返回值只能是基本类型,字符串,枚举,注解和以上类型的数组这5种
15像我们给接口使用void作为返回值没错,但是注解就不行
16枚举类型我们没接触过,可以简单介绍下,我们新建类时,可以IDE看到Enum一项,这就是创建枚举类
17我们创建的枚举类效果如上
18如上,我们给注解可以添加5种抽象方法,abstract和public可以省略的,,所以部分标灰了
19为了使用,我们先注释了后几个属性
20我们会发现被注解的方法标红了,因为我们添加了属性要给属性赋值
21我们使用属性名(函数名)=对应类型的值进行赋值,这里建议属性名(函数名一般起有意义的名字),因为可赋值所以我们叫属性
22如上我给名称改成age,
23同时赋值地方也修改,就明了了一些
24我们给注解多一个属性如上
25赋值的地方就可以看到是逗号分割各属性,此时只赋值一个会报错,如果我们就想给一个赋值,然后另一个设置默认值呢
26可以看到我们在注解那里属性声明时后面加上default 默认值 即可。
27同时注解也可以简单的只给age赋值,name使用了默认值
如果属性只有一个,并且属性值是value,那么赋值时可以直接传值
28 29如上
30我们的@SuppressWarnings就是传入了"all",可以看到其是使用value作为属性名
31我们这里将注释取消部分,如下
32 33赋值部分如上,因为不止一个属性赋值,value得写出来,对于枚举,写么枚举类名.变量,对于注解,使用@注解名,数组使用{}括起来的数组表达,当数组只有一个元素时,{}可以省略
元注解
用于描述注解的注解,如图11
jdk已经给我们定义好了一些元注解,我们需要了解的有4个
描述注解能描述的位置
@Retention 描述注解被保留的阶段(之前说过java代码3个阶段,是源码,class,runtime阶段)
@Documented 描述注解是否被api生成文档(javadoc生成文档有关)
@Inherited 描述注解是否自动被子类继承
下面我们分别展示
@Target
34我们切入Target文档,需要属性赋值value,其是ElementType数组
35我们再看ElementType里面,其是枚举类,我们可以看到几个属性TYPE(类),FIELD(成员变量),METHOD(方法),还有其他
36于是我们如上定义注解,其能描述的位置只能是类前面
37如上,放在类前面可以,放在方法前报错
38 39我们如上使用,就可以使注解既修饰方法也修饰类
@Retention
40Retention注解源码可以看到,属性也是value,类型为RetentionPolicy
41我们切换到RetentionPolicy,可以发现其就是个枚举类,可选即代码的3个阶段
42我们自定义注解属性赋值一般推荐使用RUNTIME阶段,并被jvm读取到,如果是CLASS则注解保留到class字节码,而不会被Jvm读取到,若是SOURCE,则字节码里都不会存在
@Documented就不做演示了,添加了注解,在index.html里可以看到(如果没有,即使使用也不会看到)
@Inherited注解也不做演示了,没有属性要赋值,添加后,类的子类会自动继承(这个注解一般放到类开头)
注解解析
我们知道怎么去定义注解了,但是我们怎么用呢,这就涉及到注解解析,我们之前定义给注解的属性如何使用的问题
我们之前使用反射做过一个案例,根据配置文件生成指定类,调用指定方法,之前是用配置文件,这次我们用注解即可
43我们定义的注解如上,将类名和方法名作为其属性,然后给类注解
44我们给类注解的值就相当于做配置文件,其中传入属性值
45然后我们要获取当前类字节码,获取后字节码对象有获取注解的方法(参数为注解.class即注解的字节码,这里=右侧其实是获得注解对象的一个实现类),既然是实现类,就能调用方法(也是属性),这里其实就是把类开头的值获取出来,至于后面生成类对象调用方法就不重复了
案例就不额外说了,最后注解往往我们自定义的少,常常是拿来用就行,但是学会定义我们也知道这个注解是干嘛用的