热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

doesnotnameatype是什么意思_不吹牛逼,撸个注解有什么难的

注解是Java中非常重要的一部分,但经常被忽视也是真的。之所以这么说是因为我们更倾向成为一名注解的使用者而不是创建者。Override注解用过吧?Ser

注解是 Java 中非常重要的一部分,但经常被忽视也是真的。之所以这么说是因为我们更倾向成为一名注解的使用者而不是创建者。@Override 注解用过吧?@Service 注解用过吧?但你知道怎么自定义一个注解吗?

恐怕你会摇摇头,摆摆手,不好意思地承认自己的确没有自定义过。

ed605cc117eeff995d2d3ff763e516e3.png

01、注解是什么

注解(Annotation)是在 Java 1.5 时引入的概念,同 class 和 interface 一样,也属于一种类型。注解提供了一系列数据用来装饰程序代码(类、方法、字段等),但是注解并不是所装饰代码的一部分,它对代码的运行效果没有直接影响(这句话怎么理解呢?),由编译器决定该执行哪些操作。

来看一段代码,我随便写的,除了打印到控制台的那句宣传语,其他都不重要,嘻嘻。

public class AutowiredTest { @Autowired private String name; public static void main(String[] args) { System.out.println("王二,一枚有趣的程序员"); }}

注意到 @Autowired 这个注解了吧?它本来是为 Spring 容器注入 Bean 的,现在被我无情地扔在了成员变量 name 的身上,但这段代码所在的项目中并没有启用 Spring,意味着 @Autowired 注解此时只是一个摆设。

我之所以举这个无聊的例子就是为了证明一个观点:注解对代码的运行效果没有直接影响,明白我的用意了吧?

02、注解的生命周期

注解的生命周期有 3 种策略,定义在 RetentionPolicy 枚举中。

1)SOURCE:在源文件中有效,被编译器丢弃。

2)CLASS:在编译器生成的字节码文件中有效,但在运行时会被处理类文件的 JVM 丢弃。

3)RUNTIME:在运行时有效。这也是注解生命周期中最常用的一种策略,它允许程序通过反射的方式访问注解,并根据注解的定义执行相应的代码。

03、注解装饰的目标

注解的目标定义了注解将适用于哪一种级别的 Java 代码上,有些注解只适用于方法,有些只适用于成员变量,有些只适用于类,有些则都适用。

截止到 Java 9,注解的类型一共有 11 种,定义在 ElementType 枚举中。

1)TYPE:用于类、接口、注解、枚举

2)FIELD:用于字段(类的成员变量),或者枚举常量

3)METHOD:用于方法

4)PARAMETER:用于普通方法或者构造方法的参数

5)CONSTRUCTOR:用于构造方法

6)LOCAL_VARIABLE:用于变量

7)ANNOTATION_TYPE:用于注解

8)PACKAGE:用于包

9)TYPE_PARAMETER:用于泛型参数

10)TYPE_USE:用于声明语句、泛型或者强制转换语句中的类型

11)MODULE:用于模块

04、开始撸注解

说再多,都不如撸个注解来得让人心动。撸个什么样的注解呢?一个字段注解吧,它用来标记对象在序列化成 JSON 的时候要不要包含这个字段。

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface JsonField { public String value() default "";}

1)JsonField 注解的生命周期是 RUNTIME,也就是运行时有效。

2)JsonField 注解装饰的目标是 FIELD,也就是针对字段的。

3)创建注解需要用到 @interface 关键字。

4)JsonField 注解有一个参数,名字为 value,类型为 String,默认值为一个空字符串。

为什么参数名要为 value 呢?有什么特殊的含义吗?

当然是有的,value 允许注解的使用者提供一个无需指定名字的参数。举个例子,我们可以在一个字段上使用 @JsonField(value = "沉默王二"),也可以把 value = 省略,变成 @JsonField("沉默王二")。

那 default "" 有什么特殊含义吗?

当然也是有的,它允许我们在一个字段上直接使用 @JsonField,而无需指定参数的名和值。

05、使用注解

是骡子是马拉出来遛遛,对吧?现在 @JsonField 注解已经撸好了,接下来就到了怎么使用它的环节。

假设有一个作者类,他有 3 个字段,分别是 age、name 和 bookName,后 2 个是必须序列化的字段。

public class Writer { private int age; @JsonField("writerName") private String name; @JsonField private String bookName; public Writer(int age, String name, String bookName) { this.age = age; this.name = name; this.bookName = bookName; } // getter / setter @Override public String toString() { return "Writer{" + "age=" + age + ", name='" + name + ''' + ", bookName='" + bookName + ''' + '}'; }}

1)name 上的 @JsonField 注解提供了显式的字符串值。

2)bookName 上的 @JsonField 注解使用了缺省项。

接下来,我们来编写序列化类 JsonSerializer,内容如下:

public class JsonSerializer { public static String serialize(Object object) throws IllegalAccessException { Class> objectClass &#61; object.getClass(); Map jsonElements &#61; new HashMap<>(); for (Field field : objectClass.getDeclaredFields()) { field.setAccessible(true); if (field.isAnnotationPresent(JsonField.class)) { jsonElements.put(getSerializedKey(field), (String) field.get(object)); } } return toJsonString(jsonElements); } private static String getSerializedKey(Field field) { String annotationValue &#61; field.getAnnotation(JsonField.class).value(); if (annotationValue.isEmpty()) { return field.getName(); } else { return annotationValue; } } private static String toJsonString(Map jsonMap) { String elementsString &#61; jsonMap.entrySet() .stream() .map(entry -> """ &#43; entry.getKey() &#43; "":"" &#43; entry.getValue() &#43; """) .collect(Collectors.joining(",")); return "{" &#43; elementsString &#43; "}"; }}

JsonSerializer 类的内容看起来似乎有点多&#xff0c;但不要怕&#xff0c;我一点点来解释&#xff0c;直到你搞明白为止。

1)serialize() 方法是用来序列化对象的&#xff0c;它接收一个 Object 类型的参数。objectClass.getDeclaredFields() 通过反射的方式获取对象声明的所有字段&#xff0c;然后进行 for 循环遍历。在 for 循环中&#xff0c;先通过 field.setAccessible(true) 将反射对象的可访问性设置为 true&#xff0c;供序列化使用(如果没有这个步骤的话&#xff0c;private 字段是无法获取的&#xff0c;会抛出 IllegalAccessException 异常)&#xff1b;再通过 isAnnotationPresent() 判断字段是否装饰了 JsonField 注解&#xff0c;如果是的话&#xff0c;调用 getSerializedKey() 方法&#xff0c;以及获取该对象上由此字段表示的值&#xff0c;并放入 jsonElements 中。

2)getSerializedKey() 方法用来获取字段上注解的值&#xff0c;如果注解的值是空的&#xff0c;则返回字段名。

3)toJsonString() 方法借助 Stream 流的方式返回格式化后的 JSON 字符串。

看完我的解释&#xff0c;是不是豁然开朗了&#xff1f;

接下来&#xff0c;我们来写一个测试类 JsonFieldTest&#xff0c;内容如下&#xff1a;

public class JsonFieldTest { public static void main(String[] args) throws IllegalAccessException { Writer cmower &#61; new Writer(18,"王二","Web全栈开发进阶之路"); System.out.println(JsonSerializer.serialize(cmower)); }}

程序输出结果如下&#xff1a;

{"bookName":"Web全栈开发进阶之路","writerName":"王二"}

从结果上来看&#xff1a;

1)Writer 类的 age 字段没有装饰 &#64;JsonField 注解&#xff0c;所以没有序列化。

2)Writer 类的 name 字段装饰了 &#64;JsonField 注解&#xff0c;并且显示指定了字符串“writerName”&#xff0c;所以序列化后变成了 writerName。

3)Writer 类的 bookName 字段装饰了 &#64;JsonField 注解&#xff0c;但没有显示指定值&#xff0c;所以序列化后仍然是 bookName。

作者&#xff1a;沉默王二

原文链接&#xff1a;https://juejin.im/post/5e911eca6fb9a03c485777d6




推荐阅读
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • Spring – Bean Life Cycle
    Spring – Bean Life Cycle ... [详细]
  • 本文介绍了在 Java 编程中遇到的一个常见错误:对象无法转换为 long 类型,并提供了详细的解决方案。 ... [详细]
  • 本教程详细介绍了如何使用 Spring Boot 创建一个简单的 Hello World 应用程序。适合初学者快速上手。 ... [详细]
  • 本文主要探讨了Java中处理ActionEvent事件的接口,以及一些常见的编程问题和解决方案,包括方法重载、成员变量访问、镜片质量检测等。 ... [详细]
  • 深入理解Java中的多态性概念及其应用
    多态是面向对象编程中的三大核心特性之一,与封装和继承共同构成了面向对象的基础。多态使得代码更加灵活和可扩展,封装和继承则为其提供了必要的支持。本文将深入探讨多态的概念及其在Java中的具体应用,帮助读者全面理解和掌握这一关键知识点。 ... [详细]
  • 在Java Web服务开发中,Apache CXF 和 Axis2 是两个广泛使用的框架。CXF 由于其与 Spring 框架的无缝集成能力,以及更简便的部署方式,成为了许多开发者的首选。本文将详细介绍如何使用 CXF 框架进行 Web 服务的开发,包括环境搭建、服务发布和客户端调用等关键步骤,为开发者提供一个全面的实践指南。 ... [详细]
  • DAO(Data Access Object)模式是一种用于抽象和封装所有对数据库或其他持久化机制访问的方法,它通过提供一个统一的接口来隐藏底层数据访问的复杂性。 ... [详细]
  • 本文探讨了如何在 Java 中将多参数方法通过 Lambda 表达式传递给一个接受 List 的 Function。具体分析了 `OrderUtil` 类中的 `runInBatches` 方法及其使用场景。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统
    技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统 ... [详细]
  • 深入解析Struts、Spring与Hibernate三大框架的面试要点与技巧 ... [详细]
  • 如何撰写适应变化的高效代码:策略与实践
    编写高质量且适应变化的代码是每位程序员的追求。优质代码的关键在于其可维护性和可扩展性。本文将从面向对象编程的角度出发,探讨实现这一目标的具体策略与实践方法,帮助开发者提升代码效率和灵活性。 ... [详细]
author-avatar
海豚青春_407
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有