• Java之注解


    一、介绍:

    注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的方式,使我们可以在稍后的某个时刻更容易的使用这些数据。
    注解是 Java 5 所引入的众多语言变化之一。它们提供了 Java 无法表达的但是你需要完整表述程序所需的信息。因此,注解使得我们可以以编译器验证的格式存储程序的额外信息。注解可以生成描述符文件,甚至是新的类定义,并且有助于减轻编写“样板”代码的负担。通过使用注解,你可以将元数据保存在 Java 源代码中。并拥有如下优势:简单易读的代码,编译器类型检查,使用 annotation API 为自己的注解构造处理工具。即使 Java 定义了一些类型的元数据,但是一般来说注解类型的添加和如何使用完全取决于你。

    二、Jdk中常见的注解:

    • @Override: 表示当前的方法定义将覆盖基类的方法。如果你不小心拼写错误,或者方法签名被错误拼写的时候,编译器就会发出错误提示。
    • @Deprecated: 如果使用该注解的元素被调用,编译器就会发出警告信息。
    • @SuppressWarnings: 关闭不当的编译器警告信息。
    • @SafeVarargs: 在 Java 7 中加入用于禁止对具有泛型varargs参数的方法或构造函数的调用方
    • @FunctionalInterface: Java 8 中加入用于表示类型声明为函数式接口

    每当创建涉及重复工作的类或接口时,你通常可以使用注解来自动化和简化流程。例如在 Enterprise JavaBean(EJB)中的许多额外工作就是通过注解来消除的。

    三、元注解(本文并没有实操所有元注解,想学习可自查)

    Java 语言中目前有 5 种标准注解(前面介绍过),以及 5 种元注解。元注解用于注解其他的注解

    注解 解释
    @Target 表示注解可以用于哪些地方。可能的 ElementType 参数包括:
    CONSTRUCTOR:构造器的声明;
    FIELD:字段声明(包括 enum 实例);
    LOCAL_VARIABLE:局部变量声明;
    METHOD:方法声明;
    PACKAGE:包声明;
    PARAMETER:参数声明;
    TYPE:类、接口(包括注解类型)或者 enum 声明。
    @Retention 表示注解信息保存的时长。可选的 RetentionPolicy 参数包括:
    SOURCE:注解将被编译器丢弃;
    CLASS:注解在 class 文件中可用,但是会被 VM 丢弃;
    RUNTIME:VM 将在运行期也保留注解,因此可以通过反射机制读取注解的信息。
    @Documented 将此注解保存在 Javadoc 中
    @Inherited 允许子类继承父类的注解
    @Repeatable 允许一个注解可以被使用一次或者多次(Java 8)。

    四、定义注解:

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @Author: lblz
     * @Description: 注解定义
     * @Date: 2022/5/5 14:35
     */
    @Target({ElementType.METHOD,ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AnimalAnnotation {
    
        String value() default " "; //因为默认值不能用null,所以可以选用一些特殊符号作为判断标识
    
        int id() default -1;
    
        String hairColor() default " ";
    }
    

    除了 @ 符号之外, @AnimalAnnotation的定义看起来更像一个空接口。注解的定义也需要一些元注解(meta-annotation),比如 @Target@Retention@Target 定义你的注解可以应用在哪里(例如是方法还是字段)。@Retention 定义了注解在哪里可用,在源代码中(SOURCE),class文件(CLASS)中或者是在运行时(RUNTIME)。

    注解通常会包含一些表示特定值的元素。当分析处理注解的时候,程序或工具可以利用这些值。注解的元素看起来就像接口的方法,但是可以为其指定默认值。

    不包含任何元素的注解称为标记注解(marker annotation),例如上例中的 @AnimalAnnotation 就是标记注解。

    五、使用注解

    
    /**
     * @Author: lblz
     * @Description:
     * @Date: 2022/5/5 16:55
     */
    public class Dog {
        /**
         * 姓名
         */
        @AnimalAnnotation("小黄") //如果不加注解属性 则默认是value,
        private String name;
    
        /**
         * 颜色
         */
        @AnimalAnnotation(hairColor = "黄色")
        private String color;
    
        @AnimalAnnotation
        public void getDog(String dogData){
            System.out.println(dogData);
        }
    }
    

    六、处理器

    如果没有用于读取注解的工具,那么注解不会比注释更有用。使用注解中一个很重要的部分就是,创建与使用注解处理器。Java 拓展了反射机制的 API 用于帮助你创造这类工具。同时他还提供了 javac 编译器钩子在编译时使用注解。

    下面是一个非常简单的注解处理器,我们用它来读取被注解的 Dog 类,并且使用反射机制来寻找 @AnimalAnnotation标记。

    import java.lang.reflect.Field;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    
    /**
     1. @Author: lblz
     2. @Description: 处理器
     3. @Date: 2022/5/5 17:02
     */
    public class AnnotationProcessor {
        /**
         * @Date: 2022/5/5 17:35
         * @Description:
         * @param: cl:使用了注解的类对象
         * @Return: null
         */
        public static void operationAnnotation(Class<?> cl) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
            //获取类上私有字段;
            Field[] fields = cl.getDeclaredFields();
            StringBuffer stringBuffer = new StringBuffer();
            Arrays.stream(fields).forEach(p->{
                AnimalAnnotation annotation = p.getAnnotation(AnimalAnnotation.class);
                if(annotation.value().length()>0){
                    stringBuffer.append("我家小狗叫:"+annotation.value());
                }
                if(annotation.hairColor().length()>0){
                    stringBuffer.append("\t它的毛发是:"+annotation.hairColor());
                }
            });
            //获取方法
            Method method = cl.getDeclaredMethod("getDog", String.class);
            //获取方法上的注解
            AnimalAnnotation annotation = method.getAnnotation(AnimalAnnotation.class);
            if(annotation.id() != -1){
                stringBuffer.append("\t编号为:"+annotation.id());
            }
            //执行getDog方法
            method.invoke(cl.newInstance(),stringBuffer.toString());
        }
    
        public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
            operationAnnotation(Dog.class);
        }
    }
    

    处理器肯定能做很多事情、我只是想简单一点,或者说我了解的比较少,你可以对加了注解的方法加一个监听器,然后获取其参数去执行你想执行的方法,这个蛮有用的,其实注解本身并没有什么作用,它只是一个标识或描述,具体行为由你写的处理器去进行执行,我这个处理器是运行期间进行的,有的还可以在编译期间进行操作,然后对外进行输出文件,比如说mybatis-plus的生成工具或者类似的框架,哪些算是复杂的处理器了。

    参考资料

    1. 《On Java 8》中文版
  • 相关阅读:
    windows应用程序单实例
    11. 无数人难办事?
    递归、尾递归和使用Stream延迟计算优化尾递归
    吴裕雄--天生自然MySQL学习笔记:MySQL 函数
    吴裕雄--天生自然MySQL学习笔记:MySQL 导入数据
    吴裕雄--天生自然MySQL学习笔记:MySQL 导出数据
    吴裕雄--天生自然MySQL学习笔记:MySQL 及 SQL 注入
    吴裕雄--天生自然MySQL学习笔记:MySQL 处理重复数据
    吴裕雄--天生自然MySQL学习笔记:MySQL 序列使用
    吴裕雄--天生自然MySQL学习笔记:MySQL 元数据
  • 原文地址:https://www.cnblogs.com/joeyJss/p/16225790.html
Copyright © 2020-2023  润新知