• 注解及其使用


    概念 

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

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

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

             * 概念描述:

                       * JDK1.5之后的新特性

                       * 说明程序的

                       * 使用注解:@注解名称                     

             * 作用分类:

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

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

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

    注解就是为了简化开发,避免写过多的代码,不利于程序的扩展以及维护。 

    @Override

        限定重写父类方法。对于子类中被@Override 修饰的方法,如果存在对应的被重写的父类方法,则正确;如果不存在,则报错。@Override 只能作用于方法,不能作用于其他程序元素。

    @Deprecated

        用于表示某个程序元素(类、方法等)已过时。如果使用了被@Deprecated修饰的类或方法等,编译器会发出警告。

    @SuppressWarnings

        抑制编译器警告。指示被@SuppressWarnings修饰的程序元素(以及该程序元素中的所有子元素,例如类以及该类中的方法.....)取消显示指定的编译器警告。例如,常见的@SuppressWarnings(value="unchecked")

        SuppressWarnings注解的常见参数值的简单说明:

            1.deprecation:使用了不赞成使用的类或方法时的警告(使用@Deprecated使得编译器产生的警告);

       2.unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 关闭编译器警告

      3.fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;

      4.path:在类路径、源文件路径等中有不存在的路径时的警告;

      5.serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;

      6.finally:任何 finally 子句不能正常完成时的警告;

      7.all:关于以上所有情况的警告。

     练习

    1. package com.annotation;
    2. import java.util.Date;
    3. /*
    4. * @author liuyaxi
    5. * @version 1.0
    6. * @since 1.5
    7. * @Override :检测被该注解标注的方法是否是继承自父类(接口)的
    8. * @Deprecated:该注解标注的内容,表示已过时
    9. * @SuppressWarnings:压制警告
    10. * 一般传递参数all @SuppressWarnings("all")
    11. * */
    12. @SuppressWarnings("all")
    13. public class annoDemo {
    14. @Override
    15. public String toString() {
    16. return super.toString();
    17. }
    18. @Deprecated
    19. public void show1(){
    20. //有缺陷
    21. }
    22. @MyAnno
    23. public void show2(){
    24. //替代show1
    25. }
    26. public void demo(){
    27. show1();
    28. Date n = new Date();
    29. }
    30. }

    自定义注解

    格式:

               元注解

               public @interface 注解名称{

                  属性列表;

               }

    本质:

    注解本质上就是一个接口,该接口默认继承Annotation接口

              * public interface MyAnno extends java.lang.annotation.Annotation {}

     自定义注解本质是一个接口类。它的实现类在jvm运行时会自动帮我们创建它的实现类。

    属性:接口中的抽象方法

    要求:
    1. 属性的返回值类型有下列取值
            * 基本数据类型
            * String
            * 枚举
            * 注解
            * 以上类型的数组

    2. 定义了属性,在使用时需要给属性赋值
            1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
             2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
             3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略

    练习 

    1. package com.annotation;
    2. import com.PerEnum;
    3. public @interface MyAnno {
    4. int value = 0;
    5. String name();
    6. int age() default 12;
    7. PerEnum per();
    8. String[] str();
    9. MyAnno1 anno1();
    10. }
    11. package com.annotation;
    12. import com.PerEnum;
    13. import java.util.Date;
    14. /*
    15. * @author liuyaxi
    16. * @version 1.0
    17. * @since 1.5
    18. * @Override:检测被该注解标注的方法是否是继承自父类(接口)的
    19. * @Deprecated:该注解标注的内容,表示已过时
    20. * @SuppressWarnings:压制警告
    21. * 一般传递参数all @SuppressWarnings("all")
    22. * */
    23. @SuppressWarnings("all")
    24. public class annoDemo {
    25. @Override
    26. public String toString() {
    27. return super.toString();
    28. }
    29. @Deprecated
    30. public void show1(){
    31. //有缺陷
    32. }
    33. @MyAnno(name = "zhangsan", anno1 = @MyAnno1, per = PerEnum.p1, str = {"abc", "def"})
    34. public void show2(){
    35. //替代show1
    36. }
    37. public void demo(){
    38. show1();
    39. Date n = new Date();
    40. }
    41. }

    元注解 

       元注解:用于描述注解的注解
       * @Target:描述注解能够作用的位置
          * ElementType取值:
               * TYPE:可以作用于类上
               * METHOD:可以作用于方法上
               * FIELD:可以作用于成员变量上
       * @Retention:描述注解被保留的阶段
             * @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
       * @Documented:描述注解是否被抽取到api文档中
       * @Inherited:描述注解是否被子类继承

    1. package com.annotation;
    2. import java.lang.annotation.*;
    3. @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})//只有value要赋值,所以可以省略value直接赋值,表示注解可以作用到类,方法,变量中
    4. @Retention(RetentionPolicy.RUNTIME)
    5. @Documented
    6. public @interface MyAnno1 {
    7. }
    1. package com.annotation;
    2. /**
    3. * 注解javadoc演示
    4. * @author liuyaxi
    5. * @version 1.0
    6. * @since 1.5
    7. */
    8. @MyAnno1
    9. public class annoDemo0 {
    10. /**
    11. * 计算两数之和
    12. * @param a 整数
    13. * @param b 整数
    14. * @return 两数的和
    15. */
    16. String str = "name";
    17. @MyAnno1
    18. public int add(int a, int b){
    19. return a + b;
    20. }
    21. }

    1.@Retention: 定义注解的保留策略

    @Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含

    @Retention(RetentionPolicy.CLASS)     //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,

    @Retention(RetentionPolicy.RUNTIME)  //注解会在class字节码文件中存在,在运行时可以通过反射获取到

    首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。

    2.@Target:定义注解的作用目标

    源码为:

    @Documented 

    @Retention(RetentionPolicy.RUNTIME) 

    @Target(ElementType.ANNOTATION_TYPE) 

    public @interface Target { 

        ElementType[] value(); 

    @Target(ElementType.TYPE)   //接口、类、枚举、注解

    @Target(ElementType.FIELD) //字段、枚举的常量

    @Target(ElementType.METHOD) //方法

    @Target(ElementType.PARAMETER) //方法参数

    @Target(ElementType.CONSTRUCTOR)  //构造函数

    @Target(ElementType.LOCAL_VARIABLE)//局部变量

    @Target(ElementType.ANNOTATION_TYPE)//注解

    @Target(ElementType.PACKAGE) ///包   

    3.@Document:说明该注解将被包含在javadoc中

    4.@Inherited:说明子类可以继承父类中的该注解

    解析注解

        * 在程序使用(解析)注解:获取注解中定义的属性值
            1. 获取注解定义的位置的对象  (Class,Method,Field)
            2. 获取指定的注解
                * getAnnotation(Class)
                //其实就是在内存中生成了一个该注解接口的子类实现对象

                        public class ProImpl implements Pro{
                            public String className(){
                                return "cn.itcast.annotation.Demo1";
                            }
                            public String methodName(){
                                return "show";
                            }
                        }
            3. 调用注解中的抽象方法获取配置的属性值

    1. package com.annotation;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. /**
    7. * 描述需要执行的类名和方法名
    8. */
    9. @Target({ElementType.TYPE, ElementType.METHOD})
    10. @Retention(RetentionPolicy.RUNTIME)
    11. public @interface Pro {
    12. String className();
    13. String funcName();
    14. }
    1. package com.annotation;
    2. import java.io.InputStream;
    3. import java.lang.reflect.Constructor;
    4. import java.lang.reflect.Method;
    5. import java.util.Properties;
    6. /**
    7. * 框架类
    8. */
    9. @Pro(className = "com.annotation.Student",funcName = "study")
    10. public class reflectTest_properties {
    11. public static void main(String[] args) throws Exception {
    12. /*
    13. * 前提不能改变该类的任何代码,可以创建任意类的对象,可以执行任意方法
    14. * */
    15. //1.解析注解
    16. //1.1获取该类的字节码文件对象
    17. Class<reflectTest_properties> reflectTest_pro = reflectTest_properties.class;
    18. //2.获取上边的注解对象
    19. //其实就是在内存中生成了一个该注解接口的子类实现对象
    20. /*
    21. public class ProImpl implements Pro{
    22. public String className(){
    23. return "com.annotation.Student";
    24. }
    25. public String funcName(){
    26. return "study";
    27. }
    28. * */
    29. Pro an = reflectTest_pro.getAnnotation(Pro.class);
    30. //3.调用注解对象中定义的抽象方法,获取返回值
    31. String className = an.className();
    32. String funcName = an.funcName();
    33. System.out.println(funcName);
    34. //4.加载该类进内存
    35. Class<?> c = Class.forName(className);
    36. //5.创建对象
    37. Constructor<?> cons = c.getConstructor();
    38. Object obj = cons.newInstance();
    39. //6.获取成员方法对象
    40. Method method = c.getMethod(funcName);
    41. // Pro an1 = method.getAnnotation(Pro.class);
    42. //7.执行方法
    43. method.invoke(obj);
    44. }
    45. }

     首先要获取注解中属性的值,再根据获取到的值通过反射来创建对象,并调用对象的方法来实现功能。

    简单测试框架 

    1. package com.annotation.demo;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. @Target(ElementType.METHOD)
    7. @Retention(RetentionPolicy.RUNTIME)
    8. public @interface Check {
    9. }
    1. package com.annotation.demo;
    2. /**
    3. * 小明定义的计算器类
    4. */
    5. public class Calculator {
    6. //加法
    7. @Check
    8. public void add(){
    9. String str = null;
    10. str.toString();
    11. System.out.println("1 + 0 =" + (1 + 0));
    12. }
    13. //减法
    14. @Check
    15. public void sub(){
    16. System.out.println("1 - 0 =" + (1 - 0));
    17. }
    18. //乘法
    19. @Check
    20. public void mul(){
    21. System.out.println("1 * 0 =" + (1 * 0));
    22. }
    23. //除法
    24. @Check
    25. public void div(){
    26. System.out.println("1 / 0 =" + (1 / 0));
    27. }
    28. public void show(){
    29. System.out.println("永无bug...");
    30. }
    31. }
    1. package com.annotation.demo;
    2. import java.io.BufferedWriter;
    3. import java.io.FileWriter;
    4. import java.io.IOException;
    5. import java.lang.reflect.InvocationTargetException;
    6. import java.lang.reflect.Method;
    7. /**
    8. * 简单的测试框架
    9. * 当主方法执行后,会自动自行检测的所有方法(加了check注解的方法),判断方法是否有异常,记录到文件中
    10. */
    11. public class TestCheck {
    12. public static void main(String[] args) throws IOException {
    13. //1.创建计算器对象
    14. Calculator c = new Calculator();
    15. //2.获取字节码文件对象
    16. Class cClass = c.getClass();
    17. //3.获取所有方法
    18. Method[] methods = cClass.getMethods();
    19. int number = 0;//出现异常的次数
    20. BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
    21. for(Method method : methods){
    22. //4.判断方法上是否有check注解
    23. if(method.isAnnotationPresent(Check.class)){
    24. //5.有,执行
    25. try {
    26. method.invoke(c);
    27. } catch (Exception e) {
    28. //6.捕获异常
    29. //记录到文件中
    30. number++;
    31. bw.write(method.getName() + "方法出现异常");
    32. bw.newLine();
    33. bw.write("异常的名称:" + e.getCause().getClass().getSimpleName());
    34. bw.newLine();
    35. bw.write("异常的原因:" + e.getCause().getMessage());
    36. bw.newLine();
    37. bw.write("______________________");
    38. bw.newLine();
    39. }
    40. }
    41. }
    42. bw.write("本次出现" + number + "次异常");
    43. bw.flush();
    44. bw.close();
    45. }
    46. }

  • 相关阅读:
    c#中单例模式详解
    代码随想录算法训练营 单调栈part03
    207.课程表
    【Hello Go】Go语言文本文件处理
    使用Python 创建 AI Voice Cover
    sqrt函数的实现
    web网页设计期末课程大作业:美食餐饮文化主题网站设计——中华美德6页面HTML+CSS+JavaScript
    miniprogram-ci 使用说明
    Android切换主题生命周期流程与onSaveInstanceState和onRestoreInstanceState,Kotlin
    PostgreSQL Extension 开发环境搭建
  • 原文地址:https://blog.csdn.net/weixin_42107106/article/details/125543459