java注解(通俗易懂)

java注解

  • 概念:说明程序的,是给计算机看的。

  • 注释:用文字描述程序的,给程序员看的。

  • 定义:注解(Annotation),也叫做元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次【因为其声明是public @interface xxx{}】。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面用来对这些元素进行说明、注释。

  • 概念描述:

    1. JDK1.5以后的新特性
    2. 用来说明程序的
    3. 使用注解的格式:@注解名称
  • 作用分类

    1. 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】

    2. 代码分析:通过代码里标识的注解对代码进行分析【使用反射】

    3. 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【例:@Override】

  • JDK中预定义的一些注解

    1. @Override:检测被该注解标注的方法是否是继承父类(接口)的

    2. @Deprecated:该注解标注的内容,表示已经过时

    3. @SuppressWarnings:压制警告

      • 一般传递参数all : @SuppressWarnings(“all”)代表压制.java文件中所有警告信息
      • idea中java文件警告展现方式:

在这里插入图片描述

package annocation;


@SuppressWarnings("all")
//压制所有警告,主要是右侧的黄色警告全部消失
public class AnnoDemo2 {
    @Override
    public String toString() {
        return super.toString();
    }

    @Deprecated
    public void show1(){
        //有缺陷
    }

    public void show2(){
        //替换show1方法
    }

    public void demo(){
        show1();//虽然过时但是仍然可以使用
    }
}

  • 自定义注解

    • 格式:

      ​ 元注解

      ​ public @interface 注解名称{}

    • 本质:本质上是一个接口,这个接口继承下面的Annocation接口,故接口中可以定义什么,注解中就可以定义什么。

      public interface MyAnno extends java.lang.annotation.Annotation {
      }
      
      1. 将一个自定义注解编译成字节码文件(.class文件)。

        javac MyAnno.java
        

        在这里插入图片描述

      2. 将字节码文件反编译成.java文件。

        javap MyAnno.class
        

    在这里插入图片描述

    • 属性:接口中的抽象方法

      1. 属性的返回值类型有下列取值:

        • 基本数据类型
        • String
        • 枚举
        • 注解
        • 以上数据类型的数组
        package annocation;
        public @interface MyAnno {
        
            int age();
            String name() default "皮皮虾";
            Person per();
            MyAnno2 anno2();
            String[] str();
        
        }
        
        
      2. 定义了属性,在使用注解的时候要为注解中的属性赋值。

        1. 如果在定义属性的时候,使用default关键字给属性默认初始化值,则使用注解的时候,可以不进行属性的赋值。
        2. 如果只有一个属性的时候,并且属性的名称为value则value可以省了,直接定义值即可。
        3. 数组赋值的时候,值使用{}包裹,如果数组中的值只有一项则可以省了{}。
        package annocation;
        @MyAnno(age = 12,name = "张三",per = Person.MAN,anno2 = @MyAnno2,str = {"abc","sdsd"})
        public class Worker {
        }
        
        
    • 元注解:用于描述注解的注解

      • @Target:描述注解能够作用的位置【常用】

        • ElementType(@Target注解的枚举属性)取值
          1. TYPE:可以作用于类上
          2. METHOD:可以作用于方法上
          3. FIELD:可以作用于成员变量上
        # 自定义注解@MyAnno3
        package annocation;
        import java.lang.annotation.ElementType;
        import java.lang.annotation.Target;
        
        @Target(value = {ElementType.TYPE,ElementType.METHOD})
        public @interface MyAnno3 {
        }
        
        
        #测试自定义注解
        package annocation;
        @MyAnno3
        public class Worker {
        
            @MyAnno3//下划线警告:因为该注解中未描述其可以作用于成员变量上
            public String name = "aaa";
        
            @MyAnno3
            public void show(){
        
            }
        }
        
      • @Retention:描述注解被保留的阶段(java代码的三个阶段)【常用】

        • RetentionPolicy(@Retention注解的枚举属性)取值
          1. **SOURCE:**当前被描述的注解不会保留到class字节码文件中
          2. **CLASS:**当前被描述的注解会被保留到class字节码文件中不会被JVM读取到
          3. RUNTIME:当前被描述的注解会被保留到class字节码文件中,并被JVM读取到【常用】
      • @Documented:描述注解是否被抽取到api文档中

        # 该注解没有属性
        @Documented
        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.ANNOTATION_TYPE)
        public @interface Documented {
        }
        
        

        将java文件生成为javadoc文档:由于Worker.java文件中的成员变量未带有自定义的注解,所以在javadoc文档中不会保留注解样式。

      在这里插入图片描述

      在这里插入图片描述

      • @Inherited:描述注解是否被子类继承
  • 在程序中使用(解析)注解:获取注解中定义的属性值

    • 案例01:创建框架类,要求不能改变该类的任何代码,可以创建任意类的对象,可以执行任意方法。

      1. 获取注解定义位置的对象 (Class、Method、Field)
      2. 获取指定的注解
        • getAnnotation(Class)
      3. 调用注解中抽象方法获取配置的属性值
      #  框架类java文件
      package annocation;
      
      import java.io.InputStream;
      import java.lang.reflect.Method;
      import java.sql.Ref;
      import java.util.Properties;
      
      /**
       * @ClassName ReflectUtil
       * @Description TODO 创建任意类的对象,可以执行任意方法
       * @Author ROG
       * @Date 2019/10/24 16:00
       * @Version 1.0
       */
      @Pro(ClassName = "annocation.Demo",MethodName = "test")
      public class ReflectUtil {
      
          public static void main(String[] args) throws Exception {
      
      //       1. 解析注解
      //        1.1获取该类的字节码文件对象
              Class<ReflectUtil> reflectUtilClass = ReflectUtil.class;
      
      //        2.获取上边Pro的注解对象,实际上就是在内存中生成了该注解接口的子类实现对象(类似于内部类的用法)
              Pro annotation = reflectUtilClass.getAnnotation(Pro.class);
      
      //        调用注解对象中定义的抽象方法,获得返回值
              String className = annotation.ClassName();
              String methodName = annotation.MethodName();
      
              System.out.println(className + " " + methodName);
      
      //        根据全类名获得字节码文件对象
              Class aClass = Class.forName(className);
      //        创建实际Runtime时对象
              Object o = aClass.newInstance();
              System.out.println(o);
      
      //        获取字节码文件对象中的Method对象
              Method method = aClass.getMethod(methodName);
      //        执行方法
              method.invoke(o);
      
          }
      }
      
      
      #  注解@Pro文件
      package annocation;
      //描述需要执行的类名、方法名
      
      import java.lang.annotation.ElementType;
      import java.lang.annotation.Retention;
      import java.lang.annotation.RetentionPolicy;
      import java.lang.annotation.Target;
      
      @Target(value = ElementType.TYPE)
      @Retention(RetentionPolicy.RUNTIME)
      public @interface Pro {
      
          String ClassName();
          String MethodName();
      }
      
      
      #  实体类Demo.java文件
      package annocation;
      
      
      public class Demo {
          public String name = "我是个好人";
      
          @Override
          public String toString() {
              return "Demo{" +
                      "name='" + name + '\'' +
                      '}';
          }
      
          public void test(){
              System.out.println("我爱你");
          }
      }
      
      
    • 案例02:检测一个封装类中的部分方法是否出现异常,如果出现异常将这些异常信息记录到bug.txt文件中。

      • 思路:

        1. 创建受检测封装类的对象。
        2. 获取封装类的字节码对象。
        3. 根据字节码对象获取封装对象的方法对象列表(使用反射)。
        4. 检测方法是否带有注解。
        5. 如果方法带有注解,执行方法。
        6. 如果方法执行之后出现异常,进行异常捕获,并把异常信息记录到bug.txt文件中。
      • 代码:

        #  @Check注解
        package annocation.demo;
        
        
        import java.lang.annotation.ElementType;
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        import java.lang.annotation.Target;
        
        @Target(value = ElementType.METHOD)
        @Retention(RetentionPolicy.RUNTIME)
        public @interface Check {
        }
        
        
        #  封装类
        package annocation.demo;
        
        /**
         * @ClassName Calculateor
         * @Description TODO 自定义的计算器
         * @Author ROG
         * @Date 2019/10/25 13:57
         * @Version 1.0
         */
        public class Calculateor {
        
            @Check
            public void add(){
                String str = null;
                str.toString();
                System.out.println("1 + 0 = " + (1 + 0));
            }
        
            @Check
            public void sub(){
                System.out.println("1 - 0 = " + (1 - 0));
            }
        
            @Check
            public void mul(){
                System.out.println("1 * 0 = " + (1 * 0));
            }
        
            @Check
            public void div(){
                System.out.println("1 / 0 = " + (1 / 0));
            }
        
            public void show(){
                System.out.println("永不报错");
            }
        }
        
        
        
        #  测试类
        package annocation.demo;
        
        import java.io.BufferedWriter;
        import java.io.FileWriter;
        import java.io.IOException;
        import java.lang.reflect.InvocationTargetException;
        import java.lang.reflect.Method;
        
        /**
         * @ClassName CheckTest
         * @Description TODO
         * @Author ROG
         * @Date 2019/10/25 14:02
         * @Version 1.0
         */
        public class CheckTest {
            public static void main(String[] args) throws IOException {
                //1.创建Calcuator对象
                Calculateor calculateor = new Calculateor();
        
                //2.获取其字节码文件对象
                Class calculateorClass = Calculateor.class;
        
                //3.获取其所有方法
        
                Method[] methods = calculateorClass.getMethods();
        
                int number = 0;
                BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
        
                //4.对方法进行检测,查看方法是否被@Check注解修饰
        
                for (Method method : methods) {
                    //5.如果方法被注解修饰,执行
                    if(method.isAnnotationPresent(Check.class)){
                        try {
                            method.invoke(calculateor);
                        } catch (Exception e) {
                            //6.方法报错,捕获异常,将异常信息打印在txt文件中
                            number ++;
        
                            bw.write( method.getName() + "方法出现异常了");
                            bw.newLine();
                            bw.write("异常的名称是:" + e.getCause().getClass().getSimpleName());
                            bw.newLine();
                            bw.write("异常的原因是:" + e.getCause().getMessage());
                            bw.newLine();
                            bw.write("------------------");
                            bw.newLine();
        
                        }
                    }
                }
        
        
                bw.write("本次测试一共出现" + number + "次异常");
                bw.flush();
                bw.close();
        
            }
        }
        
        
        #  bug.txt内容
        add方法出现异常了
        异常的名称是:NullPointerException
        异常的原因是:null
        ------------------
        div方法出现异常了
        异常的名称是:ArithmeticException
        异常的原因是:/ by zero
        ------------------
        本次测试一共出现2次异常
        
    • 小结

      1. 以后大多数的时候我们会使用注解,很少自定义注解。
      2. 注解给谁用?
        • 给编译器用:编译器识别注解,检测编译看有没有问题,有问题就在代码中会有提示,一般为红色下划曲线。
        • 给解析程序用(上述两个案例中的ReflectUtil.java 、CheckTest.java)
      3. 注解不是程序的一部分,可以理解为注解是一个标签,它不会影响程序代码的执行逻辑。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值