注解
注解概述
- Java注释(Annotation) 又称 Java 注释是JDK5.0引入的注释机制
- Java语言中的类、构造器、方法、成员变量、参数等都可以标记
- 注释不是程序的一部分,可以理解为注释是标签
- 大多数时候,我们会使用注释,而不是自定义注释
JDK中预定义的一些注释
- @Override:检测标注的方法是否继承了自父类(接口)
- @Deprecated:标注的内容表示已过时
- @Suppresswarnings: 压制警告[一般传输参数”all把它放在类上,如:@suppresswarnings("all")】
注解的作用
- 标记Java中类、方法和成员变量,然后进行特殊处理。什么样的处理取决于业务需求
- 例如,在JUnit框架中标记注释 @Test方法可以作为测试方法执行,没有标记的不能作为测试方法执行
注释给谁用?
- 1、编译器
- 2、使用解析程序
自定义注解
定制注释是指使用自己的注释
格式
(public为默认值,可省略)
注释本质上是一个接口,默认继承Annotation接口(注释反编译如下)
- public interface 注解名 extends java,lang.annotation.Annotation {}
属性: 接口中的抽象方法要求:
属性的返回值类型包括以下值(不包括类)
- 基本数据类型
- String
- 枚举(类型为枚举类型,调用时为:枚举名.常量)
- 注释(类型注释名,调用时为:定义时的变量名=@注释名)
- 上述类型的数组
定义属性,使用时需要赋值属性
- 如果在定义属性时使用default关键字默认初始化属性值,则在使用注释时不能赋予属性值
- 如果只有一个属性需要赋值,属性的名称是value,value可以省略并直接定义值
- 当数组赋值时,值使用 “{}” 包裹。若数组中只有一个值,则包裹。 “{}” 省略
我们先定义一个注释
public @interface AnnotationDemo { String name()default "单身狗";}
我们已经写了默认值,这样在其他地方使用注释就不需要赋值了。如果我们想改名,也可以
@AnnotationDemopublic class ObjectDemo { @AnnotationDemo(name = “单身狗1” public static void main( @AnnotationDemo String[] args) { @AnnotationDemo String name="dog"; }}
我们对此时的注释没有约束,所以几乎可以在任何地方使用
但是,如果我们没有给出初始值,则每次使用注释时都需要输入值(只要符合定义的数据类型)
@AnnotationDemo(name = “单身狗”)public class ObjectDemo { @AnnotationDemo(name = “单身狗”) public static void main( @AnnotationDemo(name = “单身狗”) String[] args) { @AnnotationDemo(name = “单身狗”) String name="dog"; }}
特殊属性
- value属性,如果只有一个value属性,在使用value属性时可以省略value名称
- 但是,如果有多个属性,且多个属性没有默认值,则value名称不能省略
如下
public @interface AnnotationDemo { String name()default "单身狗"; String value();}
然后,使用它的地方
@AnnotationDemo(“你好”)public class ObjectDemo { @AnnotationDemo(“你好”) public static void main( @AnnotationDemo(“你好”) String[] args) { @AnnotationDemo(“你好”) String name="dog"; }}
我们可以发现括号不需要写 "value=" 是的,但是当我们删除name的默认值时
public @interface AnnotationDemo { String name(); String value();}
是必须要写 "value=" 的
@AnnotationDemo(value = "你好",name =“单身狗”)public class ObjectDemo { @AnnotationDemo(value = "你好",name =“单身狗”) public static void main( @AnnotationDemo(value = "你好",name =“单身狗”) String[] args) { @AnnotationDemo(value = "你好",name =“单身狗”) String name="dog"; }}
元注解
元注释是注释(动词)注释(名词)注释(名词)
常用元注:
- @Target: 约束自定义注释可以标记的范围(即自定义注释可以在哪里使用)
- @Retention: 用于约束自定义注释的生存范围(生命周期)(如:@Retention(RetentionPolicy.RUNTIME): 目前描述的注释将保留在class字节码文件中,并由JVM读取)
- @Documented:描述注释是否被提取到api文档中(我们可以在文件所在的文档中启动终端,然后输入 javadoc API文档将生成相应的class文件)
- @Inherited: 描述注解是否被子类继承(注解标记后,注解的子类继承也包括注解)
简单展示一下
@Target({ElementType.METHOD})//元注解,括号为限制范围(此限制注解仅用于方法)public @interface AnnotationDemo {}
@Target中可用的值定义Elementtype枚举常用值如下
值
范围
TYPE
类,接口
FIELD
成员变量
METHOD
成员方法
PARAMETER
方法参数
CONSTRUCTOR
构造器
LOCAL_VARIABLE
局部变量
@Retention中可用的值定义RetentionPolicy枚举常用值如下
值
范围
SOURCE
注释只作用于源代码阶段,生成的字节码文件中不存在
CLASS
注释在源代码阶段、字节码文件阶段、运行阶段不存在,默认值
RUNTIME
注释作用在源代码阶段、字节码文件阶段、运行阶段(常用开发)
两者结合格式
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface AnnotationDemo {}
注解的解析
在注释操作中,经常需要进行分析。注释的分析是判断是否有注释。如果有注释,则分析内容
与注释分析相关的界面
- Annotation:顶部接口的注释,Annotation类型的注释对象
- AnnotatedElement:该界面定义了与注释分析相关的分析方法
方法
说明
Annotation[ ] getDeclaredAnnotations()
返回当前对象上使用的所有注释数组
T getDeclaredAnnotation(Class< T > annotationClass)
根据注解类型获得相应的注解对象 (实际上是在内存中生成注解接口的子类实现对象)(上述获取数组的相同性)
boolean isAnnotationPresent(Class< Annotation > annotationClass)
判断当前对象是否使用了指定的注释,如果使用,则返回true,否则false
Class,所有类成分,Method,Field,Constructor,都实现了Annnotatedelement接口,它们都有能力分析注释
分析注释的技巧:注释在哪个成分上,我们先拿哪个成分对象
- 例如,注解作用成员方法,需要获得与成员方法对应的Method对象,然后取上述注解
- 例如,如果注释作用于类别,则需要此类Class对象,然后取上述注释
- 例如,如果注释作用于成员变量,则应获得成员变量对应的Field对象,然后取上述注释
我们用一个案例来理解这些技能和方法的使用
需求:注释分析案例
步骤分析
①定义注意Book,要求如下:
- 包含属性: String value() 书名
- 包含属性: double price() 价格,默认值为 100
- 包含属性: String[ ] authors()多位作者
- 限制注释的位置:类别和成员方法
- 注释的有效范围:RUNTIME
② 定义BookStore类别,Book注释用于类和成员的方法
③定义AnnotationDemoBook注解上的数据获取测试类
代码展示
注释Book代码
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE,ElementType.METHOD})public @interface Book { String value(); double price()default 100; String[] authors();}
BookStore代码代码
@Book(value = “我真的不想重生”,price = 30,authors = {"柳暗花又明"}public class BookStore { @Book(value = 漫画版《我真的不想重生》,price = 30,authors = {“噼啪”} public static void buyBook(){ System.out.println(恭喜你,和渣男陈汉升约会吧); }}
Annotationdemo测试代码
public class AnnotationDemo { @Test public void testBookStore() throws Exception { ///分析类注释 Class c = BookStore.class; if (c.isAnnotationPresent(Book.class)) { Book book = (Book) c.getDeclaredAnnotation(Book.class); System.out.println(book.value()); System.out.println(book.price()); System.out.println(Arrays.toString(book.authors())); } //解析方法上的注释 Method m = c.getDeclaredMethod("buyBook"); if (m.isAnnotationPresent(Book.class)) { Book book = m.getDeclaredAnnotation(Book.class); System.out.println(book.value()); System.out.println(book.price()); System.out.println(Arrays.toString(book.authors())); } }}
模拟Junit框架
需求:定义几种方法,只要添加Mytest注释,启动时就可以触发执行
步骤分析
- ①定义自定义注解Mytest,只能注解方法,生存范围一直在
- 定义几种方法,只要有@Mytest注释法,启动时就可以触发执行,没有这种注释法就不能执行
因为我们没有模拟运行键,所以我们用main模拟
代码展示
Mytest注解代码代码
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface MyTest {}
测试代码
public class Test { @MyTest public void test(){ System.out.println("====test====="); } public void test1(){ System.out.println(===test1=======)); } public void test2(){ System.out.println(===test2=======)); } @MyTest public static void main(String[] args) throws Exception { Test test=new Test(); Class c= Test.class; Method[] methods=c.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(MyTest.class)) { method.invoke(test); } } }}
