Java注解Annotation与自定义注解详解

Java注解Annotation与自定义注解详解

概述

Java注解是在Java5中加入的新特性,是代码中的特殊标记,用于给类、方法、变量等元素添加附加信息,这些信息在编译、运行时处理或者是在代码分析的时候会被读取。注解可以看作是一种高级的Java注释,它与代码有紧密的联系。

Java注解可以分为三类:

  1. 预定义注解:JDK提供的注解,例如@Override@Deprecated@SuppressWarnings
  2. 元注解:用于自定义注解的注解,例如@Retention@Target@Documented
  3. 自定义注解:用户自己定义的注解,可以用@interface关键字定义。

预定义注解

@Override

指明某一个方法覆盖了父类的方法,如果父类中没有该方法,使用该注解会导致编译错误。

@override
public void foo() {
    // some code here
}

@Deprecated

标记一个方法或类已经弃用,不再推荐使用。

@Deprecated
public class OldClass {
    // some code here
}

@SuppressWarnings

指定一种或多种不显示任何编译器警告,这样可以防止出现不必要的编译警告。

@SuppressWarnings("unchecked")
List<String> list = new ArrayList();

元注解

@Retention

指定注解运行时生命周期。取值为RetentionPolicy中的枚举值,有三种可选:SOURCECLASSRUNTIME

  • SOURCE:只在源代码中存在,编译时就会被忽略。
  • CLASS:编译时被存储在class文件中,但是在运行时不会被JVM保留。
  • RUNTIME:运行时存在,可以通过反射获取注解信息。
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    // some code here
}

@Target

指定注解应用的目标类型,取值为ElementType中的枚举值,可以单独使用也可以组合使用。

@Target(ElementType.TYPE)
public @interface MyAnnotation {
    // some code here
}

@Documented

将注解包含在JavaDoc中。

@Documented
public @interface MyAnnotation {
    // some code here
}

自定义注解

自定义注解是使用@interface关键字定义的,它和定义Java类很像,可以定义元素,方法,抛出的异常等等。在使用自定义注解时,需要给注解元素设置值。下面给出一个简单的例子:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "";
    int id();
    String[] names();
}

该注解有三个元素:valueidnames,对应不同的数据类型,其中valueid均为必填,value默认值为""。下面给出一个使用该注解的例子:

@MyAnnotation(id=1, names={"abc", "def"})
public class Test {
    @MyAnnotation(id=2)
    public void foo() {
        // some code here
    }
}

示例说明

示例1:用自定义注解实现权限控制

定义一个注解@Permission,用于标注有权限限制的方法。同时编写一个PermissionHandler类,用于获取当前用户权限,并检查访问该方法所需的权限是否满足。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {
    String[] value();
}

public class PermissionHandler {

    public static boolean hasPermission(String[] permissions) {
        // some code here
        return true;
    }

    public static boolean checkPermission(Method method) {
        boolean hasPermission = true;
        if (method.isAnnotationPresent(Permission.class)) {
            Permission permission = method.getAnnotation(Permission.class);
            hasPermission = hasPermission(permission.value());
        }
        return hasPermission;
    }
}

下面定义一个测试类,并在其中使用@Permission注解:

public class Test {

    @Permission({"read", "write"})
    public void foo() {
        System.out.println("do foo");
    }

    public void bar() {
        System.out.println("do bar");
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Test test = new Test();
        Method fooMethod = test.getClass().getMethod("foo");
        if (PermissionHandler.checkPermission(fooMethod)) {
            test.foo();
        }
        Method barMethod = test.getClass().getMethod("bar");
        if (PermissionHandler.checkPermission(barMethod)) {
            test.bar();
        }
    }
}

运行上述测试代码,当用户具备readwrite权限时,执行foo方法,否则不执行。

示例2:用注解生成文档

定义一个注解@Doc,用于标注需要生成文档的类和方法,同时编写一个DocGenerator类,用于生成文档。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Doc {
    String desc();
    String author();
    String date();
}

public class DocGenerator {

    public static void generate(Class<?> clazz) {
        if (clazz.isAnnotationPresent(Doc.class)) {
            Doc classDoc = clazz.getAnnotation(Doc.class);
            System.out.println("Class: " + clazz.getSimpleName() + ":\n   " + classDoc.desc() +
                " By " + classDoc.author() + " On " + classDoc.date());
        }
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Doc.class)) {
                Doc methodDoc = method.getAnnotation(Doc.class);
                System.out.println("Method: " + method.getName() + ":\n   " + methodDoc.desc() +
                    " By " + methodDoc.author() + " On " + methodDoc.date());
            }
        }
    }
}

下面定义一个测试类,并在其中使用@Doc注解:

@Doc(desc="This is a test class", author="John", date="2021-08-01")
public class Test {

    @Doc(desc="This is a test method", author="John", date="2021-08-02")
    public void foo() {
        System.out.println("do foo");
    }

    public static void main(String[] args) {
        DocGenerator.generate(Test.class);
    }
}

运行上述测试代码,将会生成如下的文档:

Class: Test:
   This is a test class By John On 2021-08-01
Method: foo:
   This is a test method By John On 2021-08-02

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java注解Annotation与自定义注解详解 - Python技术站

(0)
上一篇 2023年6月25日
下一篇 2023年6月25日

相关文章

  • vue cli3 配置 stylus全局变量的使用方式

    Vue CLI3 配置 Stylus 全局变量的使用方式攻略 在 Vue CLI3 中,可以使用 Stylus 预处理器来编写样式。为了方便管理和使用全局变量,我们可以配置 Stylus,使其支持全局变量的定义和使用。下面是详细的攻略: 步骤一:安装依赖 首先,确保已经安装了 Vue CLI3。然后,在项目根目录下打开终端,执行以下命令安装 stylus 和…

    other 2023年7月29日
    00
  • Python编程-封装,继承与多态

    Python编程-封装、继承与多态 在面向对象的编程语言中,封装、继承和多态是三个重要的概念,Python作为一种流行的编程语言也不例外。在本文中,我们将详细讲解Python中封装、继承和多态的概念以及如何应用到实际的面向对象编程中。 封装 封装是面向对象编程的核心概念之一,指的是将数据和方法封装到一个抽象的类中,从而保证数据的安全性和方法的可控性。在Pyt…

    other 2023年6月25日
    00
  • postgresql 修改字段长度的操作

    要修改 PostgreSQL 数据库中的字段长度,需要执行以下几个步骤: 创建一个备份在进行任何修改操作之前,务必创建一个数据库备份。这样,如果出现问题,可以轻松地恢复原始数据库。 修改表结构在修改字段长度之前,需要先对表结构进行修改,这可以通过以下命令实现: ALTER TABLE table_name ALTER COLUMN column_name T…

    other 2023年6月25日
    00
  • 批处理BAT脚本中set命令的使用详解(批处理之家Batcher)

    批处理BAT脚本中set命令的使用详解 在批处理BAT脚本中,set命令是一个非常有用的命令,用于设置和显示环境变量。它可以用于存储和检索各种类型的数据,包括字符串、数字和文件路径等。本攻略将详细介绍set命令的使用方法和示例。 设置环境变量 set命令可以用于设置环境变量,语法如下: set 变量名=值 其中,变量名是要设置的环境变量的名称,值是要为该环境…

    other 2023年8月15日
    00
  • jenkins运行python脚本

    Jenkins运行Python脚本 Jenkins是一款流行的持续集成和持续部署工具,可以自动构建、测试和部署你的应用程序。它支持多种编程语言和技术,并且扩展性非常强,可以通过插件来适应不同的场景和需求。在本文中,我们将介绍如何使用Jenkins来运行Python脚本。 准备工作 在开始之前,需要准备以下工具和环境: 安装Jenkins服务器; 安装Pyth…

    其他 2023年3月28日
    00
  • TreeSet详解和使用示例_动力节点Java学院整理

    TreeSet详解和使用示例 概述 TreeSet是基于TreeMap实现的一种具有排序功能的集合,可以自动对集合中的元素进行排序,也可以自行指定排序规则。TreeSet中不允许插入重复元素,而且TreeSet中的元素一定是按照某种排序规则排序的,这也是TreeSet的最大特点。本文将详细介绍TreeSet的使用方法和注意事项。 TreeSet的特点 Tre…

    other 2023年6月26日
    00
  • C语言实现静态链表

    C语言实现静态链表 什么是静态链表 静态链表是一种数组表示链表结构的方法。它本质上是一个数组,但数组的每个元素都拥有两个属性:data 和 next。其中 data 属性保存了该节点的数据,next 属性则保存了指向下一个节点在数组中的下标。 如何实现静态链表 静态链表的实现步骤如下: 创建一个数组作为静态链表的容器 定义一个变量 head 作为链表的头节点…

    other 2023年6月27日
    00
  • Vue.js自定义指令的用法与实例解析

    下面是Vue.js自定义指令的用法与实例解析的完整攻略。 自定义指令的概念 在Vue.js中,我们可以通过自定义指令来扩展Vue.js的功能。自定义指令实际上就是一个指令函数,它可以接收三个参数:el, binding, vnode。 其中,el表示指令所绑定的元素,binding是一个对象,包含指令的相关信息,vnode表示Vue编译生成的虚拟节点。 自定…

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部