简介

Java 1.5 引入了注解,现在它在 Java EE 框架(如 HibernateJerseySpring )中被大量使用。Java 注释是该语言的一个强大特性,用于向 Java 代码中添加元数据。它们不直接影响程序逻辑,但可以由工具、库或框架处理,以执行特定的任务(例如代码生成、验证或自定义处理)。

元注解

目前有五种类型的元注解

@Documented

指示使用此注解的元素应该由 javadoc 和类似的工具记录。该类型应该用于注解类型的声明,这些类型的注解会影响其客户端对已注解元素的使用。如果一个类型声明用 Documented 进行了注解,那么它的注解将成为被注解元素的公共API的一部分。

@Target

表示注解类型适用的程序元素种类。一些可能的值是 TYPE、METHOD、CONSTRUCTOR、FIELD 等。如果不存在目标元注解,则注解可用于任何程序元素。

元素类型对应可以应用的位置:

  • TYPE:类、接口、枚举、记录

  • FIELD:字段、枚举常量

  • METHOD:方法

  • CONSTRUCTOR:构造器

  • LOCAL_VARIABLE:本地变量

  • ANNOTATION_TYPE:注解接口声明

  • PARAMETER:参数声明

  • PACKAGE:包声明

  • TYPE_PARAMETER:类型参数声明

  • TYPE_USE:类型的使用

  • MODULE:模块声明

  • RECORD_COMPONENT:记录组件声明

用法示例

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@interface MyAnnotation{
int value1();
String value2();
}

@Inherited

表示自动继承注解类型。如果用户查询类声明上的注解类型,而类声明没有此类型的注解,则将自动查询该类的超类以获取注解类型。这个过程将重复进行,直到找到该类型的注解,或者到达类层次结构的顶端(Object)

@Retention

表示注解类型的注解要保留多长时间。它采用 RetentionPolicy 枚举中的参数,其可能值为 SOURCE、CLASS 和 RUNTIME

保留策略可用的值:

  • RetentionPolicy.SOURCE:源代码可用,在编译期间被丢弃,它在编译后的类中不可用。

  • RetentionPolicy.CLASSjava 编译器可以访问,但 JVM 无法访问,它包含在 class 文件中。

  • RetentionPolicy.RUNTIME:运行时可用,可供 Java 编译器和 JVM 使用。

@Repeatable

用于指示其注解的类型声明是可重复的。

内建的注解

  • @Override

当想要重写一个 Superclass 的方法时,应该使用这个注解来通知编译器我们正在重写一个方法。因此,当父类方法被删除或更改时,编译器将显示错误消息。

  • @Deprecated

当我们想让编译器知道某个方法已被弃用时,我们应该使用此注释。

  • @SuppressWarnings

这只是为了告诉编译器忽略它们产生的特定警告,例如在 Java 泛型中使用原始类型。它的保留策略是 SOURCE,它会被编译器丢弃。

  • @FunctionalInterface

此注释是在 Java 8 中引入的,用于表明该接口旨在成为一个功能接口。

  • @SafeVarargs

程序员断言,注解方法或构造函数的主体不会对其 varargs 参数执行潜在的不安全操作。

使用元注解和内建注解示例

public class AnnotationExample {
public static void main(String[] args) {
} @Override
@MethodInfo(author = "Pankaj", comments = "Main method", date = "Nov 17 2012", revision = 1)
public String toString() {
return "Overriden toString method";
} @Deprecated
@MethodInfo(comments = "deprecated method", date = "Nov 17 2012")
public static void oldMethod() {
System.out.println("old method, don't use it.");
} @SuppressWarnings({ "unchecked", "deprecation" })
@MethodInfo(author = "Pankaj", comments = "Main method", date = "Nov 17 2012", revision = 10)
public static void genericsTest() throws FileNotFoundException {
List l = new ArrayList();
l.add("abc");
oldMethod();
}
}

注解的类型

  • 标记注释

没有方法的注解称为标记注解,例如:@Override 和 @Deprecated 是标记注释

@interface MyAnnotation{}
  • 单值注解

只有一个方法的注解称为单值注解

@interface MyAnnotation{
int value() default 0;
} // 应用注解
@MyAnnotation(value=10)
  • 多值注解

具有多个方法的注释称为多值注释

@interface MyAnnotation{
int value1() default 1;
String value2() default "";
String value3() default "abc";
} // 应用注解
@MyAnnotation(value1=10,value2="Arun Kumar",value3="Ghaziabad")

自定义注解示例

先定义三个注解

  • JsonSerializable
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface JsonSerializable {
}
  • JsonElement
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonElement {
String key() default "";
}
  • Init
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Init {
}

接着定义Person实体

import com.redmine.annotationexample.annotation.Init;
import com.redmine.annotationexample.annotation.JsonElement;
import com.redmine.annotationexample.annotation.JsonSerializable; @JsonSerializable
public class Person { @JsonElement
private String firstName; @JsonElement
private String lastName; @JsonElement(key = "personAge")
private int age; private String address; @Init
private void initNames() {
this.firstName = this.firstName.substring(0, 1).toUpperCase() + this.firstName.substring(1);
this.lastName = this.lastName.substring(0, 1).toUpperCase() + this.lastName.substring(1);
} public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
}

定义一个异常类

public class JsonSerializationException extends RuntimeException {
public JsonSerializationException(String message) {
super(message);
}
}

编写注解处理器

import com.redmine.annotationexample.exception.JsonSerializationException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; public class ObjectToJsonConverter { public String convertObjectToJson(Object obj) throws JsonSerializationException {
try {
checkIfSerializable(obj);
initializeObject(obj);
return getJsonString(obj);
} catch (Exception e) {
throw new JsonSerializationException(e.getMessage());
}
} private void checkIfSerializable(Object obj) {
if (Objects.isNull(obj)) {
throw new JsonSerializationException("Object is null");
} Class<?> clazz = obj.getClass();
if (!clazz.isAnnotationPresent(JsonSerializable.class)) {
throw new JsonSerializationException("The class" + clazz.getName() + " is not annotated with JsonSerializable");
}
} private void initializeObject(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(Init.class)) {
method.setAccessible(true);
method.invoke(obj);
}
}
} private String getJsonString(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
Map<String, String> jsonElementsMap = new HashMap<>();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
if (field.isAnnotationPresent(JsonElement.class)) {
JsonElement annotation = field.getAnnotation(JsonElement.class);
String mapKey = !Objects.equals(annotation.key(), "") ? annotation.key() : field.getName();
jsonElementsMap.put(mapKey, field.get(obj).toString());
}
} return jsonElementsMap.entrySet()
.stream()
.map(entry -> "\"" + entry.getKey() + "\":\"" + entry.getValue() + "\"")
.collect(Collectors.joining(",", "{", "}"));
}
}

单元测试运行试下

@SpringBootTest
class AnnotationExampleApplicationTests { @Test
void contextLoads() {
} @Test
void givenObjectSerializedTheTrueReturned() throws JsonSerializationException {
Person person = new Person("soufiane", "cheouati", 34);
ObjectToJsonConverter serializer = new ObjectToJsonConverter();
String jsonString = serializer.convertObjectToJson(person);
Assertions.assertEquals("{\"personAge\":\"34\",\"firstName\":\"Soufiane\",\"lastName\":\"Cheouati\"}", jsonString);
}
}

JDK 注解定义

Java 注解使用教程的更多相关文章

  1. Java 注解指导手册 – 终极向导

    原文链接 原文作者:Dani Buiza 译者:Toien Liu  校对:深海 编者的话:注解是java的一个主要特性且每个java开发者都应该知道如何使用它. 我们已经在Java Code Gee ...

  2. Java 注解指导手册(下)

    9. 自定义注解   正如我们之前多次提及的,可以定义和实现自定义注解.本章我们即将探讨. 首先,定义一个注解:   public @interface CustomAnnotationClass   ...

  3. Java注解教程:自定义注解示例,利用反射进行解析

    Java注解能够提供代码的相关信息,同时对于所注解的代码结构又没有直接影响.在这篇教程中,我们将学习Java注解,如何编写自定义注解,注解的使用,以及如何使用反射解析注解. 注解是Java 1.5引入 ...

  4. Java注解教程及自定义注解

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...

  5. 160621、Java注解教程及自定义注解

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...

  6. JAVA 注解的几大作用及使用方法详解

    JAVA 注解的几大作用及使用方法详解 (2013-01-22 15:13:04) 转载▼ 标签: java 注解 杂谈 分类: Java java 注解,从名字上看是注释,解释.但功能却不仅仅是注释 ...

  7. JAVA 注解的几大作用及使用方法详解【转】

    java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解 ...

  8. Java注解知识点摘抄

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...

  9. Java注解(一):介绍,作用,思想及优点

    “注解优先于命令模式”-出自<Effective Java> Java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供 ...

  10. Java注解处理器

    Java注解处理器 2015/03/03 | 分类: 基础技术 | 0 条评论 | 标签: 注解 分享到:1 译文出处: race604.com   原文出处:Hannes Dorfmann Java ...

随机推荐

  1. Spark - [01] 概述

    一.Spark是什么 Spark 是一种基于内存的快速.通用.可扩展的大数据分析引擎. Apache Spark is a unified analytics engine for large-sca ...

  2. 单线程的Redis速度为什么快?

    博客:https://www.emanjusaka.com 博客园:https://www.cnblogs.com/emanjusaka 公众号:emanjusaka的编程栈 by emanjusak ...

  3. Windows编程----进程:命令行参数

    什么是进程的命令行参数 每个进程在启动(双击exe启动.cmd命令行启动或者由其他程序通过CreateProcess启动)的时候,都会有一个命令行参数给它.命令行的参数以空格区分.这个命令行总是不为空 ...

  4. 【P0】Logisim部件级实验/有限状态机

    课上 过得十分狼狈.经鉴定孩子可能脑子拗 T1 投票决议 组内投票,赞成>反对,则通过:组长拥有一票否决权. 信号名 方向 描述 [1:0] s Input 2'b00 赞成2'b01 反对2' ...

  5. Visio绘制时间轴安排图的方法

      本文介绍基于Visio软件绘制时间轴.日程安排图.时间进度图等的方法.   在很多学习.工作场合中,我们往往需要绘制如下所示的一些带有具体时间进度的日程安排.工作流程.项目进展等可视化图表.    ...

  6. tsconfig.json报错问题

    tsconfig.json报错问题 前言 创建 tsconfig.json 配置文件时,VS Code 会自动检测当前项目当中是否有ts文件,若没有则报错,提示用户需要创建一个ts文件后,再去使用ty ...

  7. oracle清除日志

    近日发现oracle占用的空间很大,经查,发现是 /u01/app/oracle/diag/rdbms/orcl/orcl/alert 警告日志 /u01/app/oracle/diag/rdbms/ ...

  8. 项目实战 TS

    项目实战 TS 通用技巧 新手先 any 再填坑,老手先定义数据结构写逻辑 遇到新场景,没把握快速,先用 any 再填坑,填坑的过程也是 TS 技能满满提升的过程. TS 发现潜在问题 1)复杂逻辑, ...

  9. 【JDBC第7章】DAO及相关实现类

    第7章:DAO及相关实现类 DAO:Data Access Object访问数据信息的类和接口,包括了对数据的CRUD(Create.Retrival.Update.Delete),而不包含任何业务相 ...

  10. 【Linux】3.8 Linux磁盘分区、挂载

    Linux磁盘分区.挂载 1. 分区方式 mbr分区 最多支持四个主分区 系统只能安装在主分区 扩展分区要占一个主分区 MBR最大只支持2TB,但拥有最好的兼容性 gpt分区 支持无限多个主分区(但操 ...