自JDK1.5引入注解后,他就成为了Java编程语言重要的组成部分,在开发过程当中,咱们也时经常使用到@Override、@ToString等这样的注解。在这篇文章中,将介绍什么是注解、为何引用注解、它是如何工做的、如何编写自定义注解、以及如何测试自定义注解。
1、什么是注解?
用一个词就能够描述注解,那就是元数据,即一种描述数据的数据,能够理解为描述数据的一种标记。如如下代码:
@Override
public void doSomething() { System.out.println("复写了父类doSomething()!)"); }
总的来讲,Annotation(注解)是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符;Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的做用。包含在 java.lang.annotation 包中;它是一种由JSR-175标准选择用来描述元数据的一种工具。
2、为何引入注解?
使用Annotation注解以前,XML被普遍的应用于描述元数据。随着项目的开发与维护,XML的表现愈来愈糟糕,此时,人们但愿在一些场合使用紧耦合的方式进行代码描述。固然,并非说XML方式很差,二者各有优点:
假如你想为应用设置不少的常量或参数,这种状况下,XML是一个很好的选择,由于它不会同特定的代码相连;若是你想对代码进行配置说明、编译提示等时,那么使用Annotation注解会更好一些。
注解的用处:
一、生成文档。这是最多见的,也是java 最先提供的注解。经常使用的有@param @return 等
二、跟踪代码依赖性,实现替代配置文件功能。好比Dagger 2依赖注入,将来java开发,将大量注解配置,具备很大用处,好比如今的Springboot框架;
三、在编译时进行格式检查。如@override 放在方法前,若是你这个方法并非覆盖了超类方法,则编译时就能检查出。
3、注解是如何工做的?
注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。而咱们经过反射获取注解时,返回的是Java运行时生成的动态代理对象$Proxy。经过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。
4、如何编写自定义注解
编写自定义的注解以前,首先得了解注解相关的基本概念——元注解。
元注解,专门注解其余的注解(在自定义注解的时候,须要使用到元注解),起到指定注解(自定义注解)的做用范围、生命周期等做用。java.lang.annotation提供了四种元注解:
1)@Documented——注解是否将包含在JavaDoc中,一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中
2)@Retention ——何时使用该注解,定义该注解的生命周期
3)@Target? ——注解用于什么地方。若是不明确指出,该注解能够放在任何地方。如下是一些可用的参数。须要说明的是:属性的注解是兼容的,若是你想给7个属性都添加注解,仅仅排除一个属性,那么你须要在定义target包含全部的属性。
ElementType.TYPE:用于描述类、接口或enum声明
ElementType.FIELD:用于描述实例变量
ElementType.METHOD:用于描述实例方法
ElementType.PARAMETER:用于描述实例方法参数
ElementType.CONSTRUCTOR:用于描述实例的构造函数
ElementType.LOCAL_VARIABLE:用于描述局部变量
ElementType.ANNOTATION_TYPE 另外一个注释
ElementType.PACKAGE 用于记录java文件的package信息
4)@Inherited —— 是否容许子类继承该注解
可是从JDK1.8开始,又新增长了两个元注解:
@Native——指示可从本机代码引用定义常量值的字段。
@Repeatable——用于指示其声明(元)注释的注释类型是可重复的。
下面咱们开始编写自定义注解。语法以下,使用@interface来代表声明的是一个注解
public @interface 注解名称 {
//String weather() default "";//注解中能够没有属性,而且在注解中属性是以方法的形式存在
}
可是,编写注解的时候仅仅作到如上代码的样子还远远不够,一般,还须要为他们指定注解的做用范围、生命周期等,这时候就须要用到元注解对咱们定义的注解进行注解(标识说明)了。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface WeatherAnnotation { String weather() default ""; }
以上注解经过ElementType.FIELD限制了该注解只能标识属性,经过RetentionPolicy.RUNTIME指定了注解的生命周期——代码运行时生效,自定义注解基本上都是使用该枚举字段声明其生命周期。接下来,开始编写测试注解的代码:javascript
package com.wyfx.nio.annotation;
public class Today { @WeatherAnnotation(weather="hello,今天是晴天") private String dayWeather; public String getDayWeather() { return dayWeather; } public void setDayWeather(String dayWeather) { this.dayWeather = dayWeather; } @Override public String toString() { return "Today{" + "dayWeather='" + dayWeather + '\'' + '}'; } }
import java.lang.reflect.Field;
public class Test { public static void main(String[] args){ /*Annotation annotations=new Today().getClass().getAnnotation(WeatherAnnotation.class);*/ try { Class aClass=Class.forName("com.wyfx.nio.annotation.Today"); Field[] fields= aClass.getDeclaredFields(); String weather=""; for (Field field : fields) { if(field.isAnnotationPresent(WeatherAnnotation.class)){ WeatherAnnotation weatherAnnotation=field.getAnnotation(WeatherAnnotation.class); weather= weatherAnnotation.weather(); } } System.out.println("--annotation---:"+weather); }catch (Exception e){ e.printStackTrace(); } } }
以上代码顺利打印出“hello,今天是晴天”,说明自定义注解成功了,值得注意的是,注解的做用域是属性,因此在经过反射进行测试的时候,必须在Field的基础上去判断是不是Annotation接口的子类(field.isAnnotationPresent(),而后获取注解子类对象。java