java注解

注解概念与注解的作用

  1. 注解定义:注解是一种元数据,是在jdk1.5以及以后的版本引入的,与类,接口,枚举在同一层次的,它可以声明在一个类,方法,属性等前面,用来对这些元素进行说明,解释。
  2. 注解的作用:
    • 编写文档:通过代码标识的注解,来生成文档(doc文档,api文档)
    • 代码分析:通过代码标识的注解,来对代码进行分析(反射)
    • 编译检查:通过代码标识的注解,来对代码进行编译前的检查(Override注解)

 注解的本质

  通过自定义一个注解AnnoDemo 里面含有一个name属性,然后对AnnoDemo的class文件进行反编译得到如下内容:

     

  从上面结果看出,AnnoDemo注解本质是一个继承java.lang.annotation.Annotation接口的一个接口,注解AnnoDemo属性name,本质上是一个抽象方法。具体自定义注解规则在下面自定义注解再将。

自定义注解

  1. 自定义注解的语法:@interface 注 名{ 注解属性列表} 
  2. 属性和属性取值类型:属性就是指接口中的抽象方法:属性取值类型可以是八大基本类型,String类型,枚举类型,注解,以及上面三种类型的数组
  3. 注解使用:如果注解定义了属性,在使用时需要给属性赋值操作,有以下注意事项:如果定义属性时,用default 给属性一个默认值,那么使用注解时,可以不给属性赋值操作,如果注解只定义一个属性时,并且属性名为value,使用注解时,对属性赋值,可以省去属性名=,可以直接定义值。数组属性赋值操作规则为{值1,值2 ...}
  4. 自定义注解一般需要用到元注解,元注解指的是描述注解的注解,在下面会介绍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:返回本元素的所有注解,不包含父类继承而来的
原文地址:https://www.cnblogs.com/shareAndStudy/p/12730723.html