java注解
注解概念与注解的作用
- 注解定义:注解是一种元数据,是在jdk1.5以及以后的版本引入的,与类,接口,枚举在同一层次的,它可以声明在一个类,方法,属性等前面,用来对这些元素进行说明,解释。
- 注解的作用:
- 编写文档:通过代码标识的注解,来生成文档(doc文档,api文档)
- 代码分析:通过代码标识的注解,来对代码进行分析(反射)
- 编译检查:通过代码标识的注解,来对代码进行编译前的检查(Override注解)
注解的本质
通过自定义一个注解AnnoDemo 里面含有一个name属性,然后对AnnoDemo的class文件进行反编译得到如下内容:
从上面结果看出,AnnoDemo注解本质是一个继承java.lang.annotation.Annotation接口的一个接口,注解AnnoDemo属性name,本质上是一个抽象方法。具体自定义注解规则在下面自定义注解再将。
自定义注解
- 自定义注解的语法:@interface 注 名{ 注解属性列表}
- 属性和属性取值类型:属性就是指接口中的抽象方法:属性取值类型可以是八大基本类型,String类型,枚举类型,注解,以及上面三种类型的数组
- 注解使用:如果注解定义了属性,在使用时需要给属性赋值操作,有以下注意事项:如果定义属性时,用default 给属性一个默认值,那么使用注解时,可以不给属性赋值操作,如果注解只定义一个属性时,并且属性名为value,使用注解时,对属性赋值,可以省去属性名=,可以直接定义值。数组属性赋值操作规则为{值1,值2 ...}
- 自定义注解一般需要用到元注解,元注解指的是描述注解的注解,在下面会介绍jdk的四大元注解:
jdk四大元注解
- @Target:注解作用的位置
- @Retention:注解的生命周期
- @Documented:注解是否应当被包含在 JavaDoc (api)文档中
- @Inherited:是否允许子类继承该注解
其中,@Target 指的是注解是作用在类上,还是方法,属性上。@Target注解定义如下:
通过ElementType 这个枚举类型的值,指定作用的位置,ElementType有以下一些值:
- ElementType.TYPE:允许被修饰的注解作用在类、接口和枚举上
- ElementType.FIELD:允许作用在属性字段上
- ElementType.METHOD:允许作用在方法上
- ElementType.PARAMETER:允许作用在方法参数上
- ElementType.CONSTRUCTOR:允许作用在构造器上
- ElementType.LOCAL_VARIABLE:允许作用在本地局部变量上
- ElementType.ANNOTATION_TYPE:允许作用在注解上
- ElementType.PACKAGE:允许作用在包上
@Retention 用于指明当前注解的生命周期,定义如下:
通过 RetentionPolicy 这个枚举类型的值,指定注解的生命周期,RetentionPolicy有以下取值:
- RetentionPolicy.SOURCE:当前注解编译期可见,不会写入 class 文件
- RetentionPolicy.CLASS:类加载阶段丢弃,会写入 class 文件
- RetentionPolicy.RUNTIME:永久保存,可以反射获取
@Retention 注解指定了被修饰的注解的生命周期,一种是只能在编译期可见,编译后会被丢弃,一种会被编译器编译进 class 文件中,无论是类或是方法,乃至字段,他们都是有属性表的,而 JAVA 虚拟机也定义了几种注解属性表用于存储注解信息,但是这种可见性不能带到方法区,类加载时会予以丢弃,最后一种则是永久存在的可见性。
剩下两种类型的注解我们日常用的不多,也比较简单,这里不再详细的进行介绍了,你只需要知道他们各自的作用即可。@Documented 注解修饰的注解,当我们执行 JavaDoc 文档打包时会被保存进 doc 文档,反之将在打包时丢弃。@Inherited 注解修饰的注解是具有可继承性的,也就说我们的注解修饰了一个类,而该类的子类将自动继承父类的该注解。
常用的java内置注解
- @Override: 该注解标注的方法必须继承父类(接口),否则编译不通过。该注解定义如下:
- @Deprecated: 该注解标注的代码(方法),表示这些代码已经过时了,该注解定义如下:
- @SuppressWarinings:该注解标注的代码,如果有警告,会压制警告。使用该注解时,一般给value属性赋值all,该注解定义如下:
注解案例
- 自定义一个StudentAnno注解代码如下:
//自定义枚举类型AnnoStudent @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface AnnoStudent { int age(); String name(); String [] course(); Target anno(); Grade grade(); } //枚举类Grade public enum Grade { SMALL,MID,HIGH; } //测试类 @AnnoStudent(age=15,name="jack",course={"Enlish","Math"},anno = @Target(ElementType.ANNOTATION_TYPE),grade=Grade.HIGH ) public class TestAnno { }
- 定义一个检测注解,检测一个Calculator类的所有方法是否正常运行,如果异常将其记录在一个log文件中,该过程结合了反射:具体代码如下:
//注解类 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CheckAnno { } //如果检测的类 /** * 模拟一个计算器类 */ public class Calculator { //加法 @CheckAnno public void add(){ System.out.println("1+0="+(1+0)); } //减法 @CheckAnno public void sub(){ System.out.println("1-0="+(1-0)); } //乘法 @CheckAnno public void multi(){ System.out.println("1*0="+(1*0)); } //除法 @CheckAnno public void div(){ System.out.println("1/0="+(1/0)); } } /** *检查计算器类方法是否正常运行,如果不正常运行的方法,将异常的方法,异常的名称,异常的原因记录在checkLog文件中 */ public class TestCheck { public static void main(String[] args )throws IOException { Calculator c=new Calculator(); //获取计算类的字节码 Class cla =c.getClass(); //获取计算器类的所有定义的方法 Method [] methodss= cla.getDeclaredMethods(); int num=0;//出现异常的方法数 BufferedWriter bw=new BufferedWriter(new FileWriter("checkLog.txt")); for(Method method:methodss){ //判断当前方法是否使用Check注解 if( method.isAnnotationPresent(CheckAnno.class)){ try{ //调用计算器的方法 method.invoke(c); } catch(Exception e) { //异常处理 bw.write("出现异常的方法:"+method.getName()); bw.newLine(); bw.write("异常的名称:"+e.getCause().getClass().getSimpleName()); bw.newLine(); bw.write("异常的原因:"+e.getCause().getMessage()); bw.newLine(); num++; } } } bw.write("调用该方法一共出了"+num+"次异常"); bw.close(); } }
- 注解与反射结合,获取任意类的对象,调用对象的任意方法,注解相当于配置文件的左右,代码如下:
//注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ReflectAnno { String className(); String classMethod(); } //测试注解与反射的类 /** * 注解与反射结合,获取任意类的对象,并调用该对象的任意方法 * 注解相当与配置文件 */ @ReflectAnno(className = "cn.ck.annotation.Student",classMethod = "show") public class ReflectTest { public static void main(String[] args) throws Exception { //获取相应的注解对象 ReflectAnno ra= ReflectTest.class.getAnnotation(ReflectAnno.class); //注解对象调用相应的抽象方法获取classname属性值,classMethod属性值 String classMethod= ra.classMethod(); String className=ra.className(); //将类加载进内存 Class cla= Class.forName(className); //创建对象 Object obj=cla.newInstance(); //通过classMethod值,获取相应的方法 Method method=cla.getDeclaredMethod(classMethod); //调用方法 method.invoke(obj); } }
Class 类中提供了以下一些方法用于反射注解。
- getAnnotation:返回指定的注解
- isAnnotationPresent:判定当前元素是否被指定注解修饰
- getAnnotations:返回所有的注解
- getDeclaredAnnotation:返回本元素的指定注解
- getDeclaredAnnotations:返回本元素的所有注解,不包含父类继承而来的