注解概念与注解的作用
注解定义:注解是一种元数据,是在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)
@Documentedpublic @interfaceAnnoStudent {intage();
String name();
String [] course();
Target anno();
Grade grade();
}//枚举类Grade
public enumGrade {
SMALL,MID,HIGH;
}//测试类
@AnnoStudent(age=15,name="jack",course={"Enlish","Math"},anno = @Target(ElementType.ANNOTATION_TYPE),grade=Grade.HIGH
)public classTestAnno {
}
定义一个检测注解,检测一个Calculator类的所有方法是否正常运行,如果异常将其记录在一个log文件中,该过程结合了反射:具体代码如下:
//注解类
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documentedpublic @interfaceCheckAnno {
}//如果检测的类/*** 模拟一个计算器类*/
public classCalculator {//加法
@CheckAnnopublic voidadd(){
System.out.println("1+0="+(1+0));
}//减法
@CheckAnnopublic voidsub(){
System.out.println("1-0="+(1-0));
}//乘法
@CheckAnnopublic voidmulti(){
System.out.println("1*0="+(1*0));
}//除法
@CheckAnnopublic voiddiv(){
System.out.println("1/0="+(1/0));
}
}/***检查计算器类方法是否正常运行,如果不正常运行的方法,将异常的方法,异常的名称,异常的原因记录在checkLog文件中*/
public classTestCheck {public static void main(String[] args )throwsIOException {
Calculator c=newCalculator();//获取计算类的字节码
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)
@Documentedpublic @interfaceReflectAnno {
String className();
String classMethod();
}//测试注解与反射的类/*** 注解与反射结合,获取任意类的对象,并调用该对象的任意方法
* 注解相当与配置文件*/@ReflectAnno(className= "cn.ck.annotation.Student",classMethod = "show")public classReflectTest {public static void main(String[] args) throwsException {//获取相应的注解对象
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:返回本元素的所有注解,不包含父类继承而来的