• 【Java高级】一篇文章带你学会什么是注解


    目录

    注解概述

    | Java内置的三个注解

    @Deprecated

    @Override

    @SuppressWarnings

    | 元注解

    @Target

    @Retention(注解生命周期)

    @Documented

    @Inherited

    @Repeatable(JDK1.8)

    | 自定义注解

    自定义注解的概述

    自定义注解的步骤

    自定义注解应用示例


    本文参考了部分资料,在此声明,侵删。

    参考资料1

    参考资料2

    参考资料3

    注解概述

    什么是注解?

    • 注解也属于一种类型

    • 类上、属性上、方法上、变量上、注解类型上,均可以出现注解!

    • Java注解用于为Java代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。

    • Java注解通常用于以下目的:

      • 编译器指令:Java有3个内置的注解,你可以用它来发出指令到Java编译器。

      • 编译时指令:当创建建软件项目时,Java注释可以在编译时使用。构建过程包括生成的源代码,编译源文件,生成XML文件(例如部署描述符),包装编译代码和文件到一个JAR文件等。构建软件通常是通过像Apache Ant组织的Apache Maven的自动构建工具完成。构建工具可以扫描你的Java代码指定的注解并生成源代码或基于这些注解其他文件

      • 运行时指令:通常情况下,Java注解中不存在于编译后的Java代码。这是可能的,但是,定义自己的注解是在运行时可用。这些注解然后可以通过Java反射访问,并用于发出指令到程序,或者一些第三方API

    注解的作用

    • 生成文档的注解,如@author,@param。

    • 跟踪代码依赖性,实现替代配置文件功能,如spring mvc的注解。

    • 编译时进行格式检查,如@override。

    • 编译时进行代码生成补全,如lombok插件的@Data。

    注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

    注解有许多用处,主要如下:

    • 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息

    • 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。

    • 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取 值得注意的是,注解不是代码本身的一部分。


    | Java内置的三个注解

    @Deprecated

    • 用来注解过时的东西,如类或方法的注解; 如果使用的东西过时,编译器会通知应该使用的另一种方式。或IDE工具,像Eclipse,它也给显示出可视化通知

    1. package com.yiibai.tutorial.ann.builtin;
    2. import java.util.Date;
    3. public class DeprecatedMethodDemo {
    4. /**
    5. * @deprecated replaced by {@link #todo(String,Date)}
    6. */
    7. @Deprecated
    8. public void todoJob(String jobName) {
    9. System.out.println("Todo " + jobName);
    10. }
    11. public void todo(String jobName, Date atTime) {
    12. System.out.println("Todo " + jobName + " at " + atTime);
    13. }
    14. public void todoNothing() {
    15. System.out.println("Todo Nothing");
    16. }
    17. public static void main(String[] args) {
    18. DeprecatedMethodDemo obj = new DeprecatedMethodDemo();
    19. obj.todoJob("Java coding");
    20. obj.todoNothing();
    21. }
    22. }

     


    @Override

    • @Override注解来覆盖超类方法在方法使用。如果这个方法不超匹配的方法,编译器就会提示一个错误。

    • @Override注解不是必需的。如坚持使用它,也是个好主意。万一有人改变超类中的重写方法名称,子类的方法将不再覆盖它。可能不太好找到问题。

    • @Override注解,用于更好地查找在子类中的方法没有完全重写超类的方法

     


    @SuppressWarnings

    @SuppressWarnings注解使编译器抑制对某个方法的警告。例如,如果一个方法调用的方法已过时,或使一个不安全的类型转换,编译器可能会产生一个警告。可以通过包含使用@SuppressWarnings注解代码的方法标注抑制这些警告s

    示例1

    1. package com.yiibai.tutorial.ann.builtin;
    2. import java.util.Date;
    3. public class SuppressWarningsDemo {
    4. @SuppressWarnings("deprecation","unused")
    5. public Date getSomeDate() {
    6. Date date = new Date(2014, 9, 25);
    7. return date;
    8. }
    9. }

     

    示例2

    1. package com.yiibai.tutorial.ann.builtin;
    2. import java.util.ArrayList;
    3. import java.util.Date;
    4. import java.util.List;
    5. public class SuppressWarningsDemo2 {
    6. public List getDatas() {
    7. List list = new ArrayList();
    8. list.add("One");
    9. return list;
    10. }
    11. @SuppressWarnings({ "deprecation", "unused", "unchecked" })
    12. public void processDatas() {
    13. // You use deprecated Constructor
    14. // Variable 'date' was created, but not used
    15. Date date = new Date(2014, 9, 25);
    16. // Cast unsafe.
    17. // Variable 'datas' is created, but not used in the code.
    18. List datas= (List) this.getDatas();
    19. }
    20. }

     


    | 元注解

    • 元注解可以理解为 “用于修饰注解的注解”,指定注解可以修饰的位置

    @Target

    • 用于指定被修饰的注解可以放置的位置(如@Override注解被 @Target(ElementType.METHOD) 修饰,所以只能作用于方法)

    1. @Target(value={CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,PACAKGE,MODULE,PARAMATER,TYPE})
    2. @其他注解
    3. //也可以写成下面这种语法
    4. @Target({FIELD , METHOD})

    可以写的值有

    1. ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
    2. ElementType.CONSTRUCTOR 可以给构造方法进行注解
    3. ElementType.FIELD 可以给属性进行注解
    4. ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
    5. ElementType.METHOD 可以给方法进行注解
    6. ElementType.PACKAGE 可以给一个包进行注解
    7. ElementType.PARAMETER 可以给一个方法内的参数进行注解
    8. ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

    @Retention(注解生命周期)

    • 用于被修饰的注解会被保存在哪儿、能否被反射到

    1. @Retention(RetentionPolicy . SOURCE) //只被保存在java源文件(即源文件保留)
    2. @Retention(RetentionPolicy . CLASS) //被保存在class文件中(即class保留)
    3. @Retention(RetentionPolicy . RUNTIME) //被保存在class文件中,并能够被反射机制所读取 (即运行时保留)
    • RetentionPolicy.SOURCE : 仅存在于源代码中,编译阶段会被丢弃,不会包含于class字节码文件中。@Override, @SuppressWarnings都属于这类注解。

    • RetentionPolicy.CLASS : 默认策略,在class字节码文件中存在,在类加载的时被丢弃,运行时无法获取到。

    • RetentionPolicy.RUNTIME : 始终不会丢弃,可以使用反射获得该注解的信息。自定义的注解最常用的使用方式。


    @Documented

    能够将注解中的元素包含到 Javadoc 中去。


    @Inherited

    Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。


    @Repeatable(JDK1.8)

    注解的值可以同时取多个。举个例子,一个人他既是程序员又是产品经理,同时他还是个画家。

    1. @interface Persons {
    2. Person[] value();
    3. }
    4. @Repeatable(Persons.class)
    5. @interface Person{
    6. String role default "";
    7. }
    8. @Person(role="artist")
    9. @Person(role="coder")
    10. @Person(role="PM")
    11. public class SuperMan{
    12. }



    | 自定义注解

    自定义注解的概述

    • 注:注解只是一种标记,如果不解析,它是不会实现任何功能的

    • 自定义注解本质上是继承自Annotation的类

    • 自定义注解的作用:自定义注解就是在注解内添加一系列的属性,然后通过元注解限定注解的位置,

      接着在代码中通过反射(只有被元注解 @Retention(RetentionPolicy.RUNTIME) 修饰的注解才能被反射到),获取到注解内的值,完成一些业务逻辑代码


    自定义注解的步骤

    定义自定义注解使用 @interface 关键字

    @interface是声明一个注释的关键字,而这个注解非常相似于接口。注解可有或没有元素。注释元素的特点:

    • 元素可以有默认值;

    • 没有函数体;

    • 没有函数参数;

    • 返回的声明必须在一个特定的类型:

      • 基本类型 (boolean, int, float,…)

      • 枚举

      • 注解

      • 类(如:String.class)

    使用元注解来限定自定义注解

    • 使用元注解@Target来标记该注解的作用范围(空间范围,在哪些地方可以使用)

    • 使用元注解@Retention来标记该注解的作用时间范围(时间范围,在什么时候起作用)

    • 使用元注解@Inherited来标记该注解是否可以由子注解来继承

    • 使用元注解@Documented来标记该注解是否需要自动生成文档

    • 使用@interface关键词来定义注解

    创建自定义注解代码示例

    1. //Step1.根据语法搭建语法框架、根据需要添加元注解
    2. //Step2.给注解添加属性、【赋默认值】
    3. @元注解
    4. public @interface 自定义注解名MyAnnotation{
    5. //元素String name,默认值""
    6. String name() [default ""];
    7. int age();
    8. }

    使用自定义代码(给属性赋值)

    1. //给自定义注解的属性赋值:常规方法
    2. @MyAnnotation(age = 8 , name = "Klee");
    3. //给自定义注解的属性赋值:只有一个属性 且 属性名为value(此时可以省略value这个名字)
    4. //假设AnnotationValue中只有一个 int value(); 属性
    5. @AnnotationValue(100);
    6. //假如除了value属性还有别的属性,则只能按照常规方法老实赋值啦
    7. //给自定义注解的属性赋值:属性有数组(假设名字为array)
    8. @AnnotationArray(people = {"Klee","Sagiri"}); //数组有多个元素
    9. @AnnotationArray(people = "Klee"); //数组只有一个元素

    自定义注解应用示例

    • 下边定义一个注解@Range用来校验对象的字段长度是否符合自定义约束

    • 再次强调:自定义注解通常结合反射 + AOP来使用,比较高深,尽可能看懂。

    注解类 Range.class

    1. /**
    2. * @author sunhongmin
    3. * @Date 2020-10-21
    4. * @Describe 字段长度约束,默认0-255
    5. */
    6. @Target(ElementType.FIELD)
    7. @Retention(RetentionPolicy.RUNTIME) //只有被@Rention修饰的注解才能被反射到!
    8. public @interface Range {
    9. int min() default 0;
    10. int max() default 255;
    11. }

    测试类 RangeTestDemo.class

    1. package com.annotation;
    2. import java.lang.reflect.Field;
    3. class User {
    4. /**
    5. * 最大长度为10
    6. */
    7. @Range(max = 10)
    8. public String name;
    9. /**
    10. * 使用默认值255
    11. */
    12. @Range
    13. public String address;
    14. public User() {
    15. }
    16. public User(String name, String address) {
    17. this.name = name;
    18. this.address = address;
    19. }
    20. public String getName() {
    21. return name;
    22. }
    23. public void setName(String name) {
    24. this.name = name;
    25. }
    26. public String getAddress() {
    27. return address;
    28. }
    29. public void setAddress(String address) {
    30. this.address = address;
    31. }
    32. }
    33. public class RangeTestDemo {
    34. public static void checkRange(User user) throws IllegalAccessException {
    35. Field[] declaredFields = user.getClass().getDeclaredFields();
    36. for (Field field : declaredFields) {
    37. // 如果标注了Range注解
    38. if (field.isAnnotationPresent(Range.class)) {
    39. // 获取注解
    40. Range range = field.getAnnotation(Range.class);
    41. // 获取field值
    42. Object value = field.get(user);
    43. if (value instanceof String) {
    44. String str = (String) value;
    45. if (str.length() < range.min() || str.length() > range.max()) {
    46. throw new IllegalArgumentException("Invalid field:{" + field.getName() + "},value:{" + str + "},Limit range [" + range.min() + "," + range.max() + "]");
    47. }
    48. }
    49. }
    50. }
    51. }
    52. public static void main(String[] args) throws IllegalAccessException {
    53. User user = new User("张大彪啊啊啊啊啊啊啊asdasdasdasda", "地址在哪呢在哪在哪在哪在哪在哪在哪在哪在哪在哪在哪在哪在哪");
    54. RangeTestDemo.checkRange(user);
    55. }
    56. }

  • 相关阅读:
    DDIM代码详细解读(2):关键参数计算、损失函数设计、添加时间步长信息、归一化设计
    【附源码】Python计算机毕业设计某物业公司的信息管理系统
    我的奋斗:我在外企那些年(二)
    2022-31周 项目问题整理
    VSCode 居然是个娱乐软件?让你 high 到爆的几款插件
    Pytest自动化测试实战之执行参数
    HarmonyOS应用开发Web组件基本属性应用和事件
    元素的盒子模型+标签的尺寸大小和偏移量+获取页面滚动距离+JS直接修改标签样式 + 让页面滚动到指定位置
    人生第一个项目(学生管理系统)(进阶版)
    语法练习:front3
  • 原文地址:https://blog.csdn.net/m0_57265007/article/details/127949226