Java 基础语法:注解

2515 字
13 分钟
Java 基础语法:注解

学习目标:

  • 掌握注解的定义与使用

  • 熟悉2个元注解的作用

  • 掌握使用注解处理器获取注解信息

注解与注释#

注释

  • 单行注释//
  • 多行注释/* */
  • 文档注释/** */

注释的作用:

传递额外的信息进行解释说明, 给程序员看的

注释不参与编译

注释只有语法形式, 具体内容没有要求
// 年龄在18-25之间, [18,25], 18<=age<=25
boolean judegeAge(int age){
}

什么是注解

Annotation其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理

注解的作用

通过使用Annotation,程序开发人员可以在不改变原有逻辑的情况下,在源文件嵌入一些补充信息

Annotation就像修饰符一样被使用,可用于修饰类、构造器、方法、成员变量、参数…,这些信息被存储在Annotation的“属性名=属性值”对中

注解 VS 注释

  • 相同点
    • 都是用来传递额外信息的
  • 不同点
    • 注解可以参与编译,注释不行
    • 注解有使用范围,注释没有(想咋写咋写)
    • 注解作为一种数据类型,跟class interface具有同等地位

注解定义#

语法

权限修饰符 @interface 注解名字{
// 注解体定义
属性类型 属性名();
属性类型 属性名();
属性类型 属性名();
......
}
举例:
public @interface Override {
}
属性类型:
基本数据类型
String类型
Class类型
注解类型
枚举类型
以及以上类型的数组形式

注意:

注解不允许继承

注解和接口的关系:

注解和接口的定义都使用同一个关键字interface,而且注解体的定义也非常类似于接口中的抽象方法。

这当然不是巧合,而是因为注解本身就是一种特殊的接口。查看java.lang.annotation.Annotation接口的JDK文档,原文是:

所有注解类型都隐式扩展自该接口。但要注意,手动扩展该公共接口的接口不会定义为注解类型。还要注意此接口本身不是定义注解型。

也就是说:

  1. 当你使用”@interface”关键字定义一个注解类型时,它会自动实现java.lang.annotation.Annotation接口,即使你没有显式地声明这个继承关系。
  2. 如果使用”interface”关键字定义一个接口类型,并显式地让它继承java.lang.annotation.Annotation接口,那么这个新接口也不会被视为一个注解类型。
  3. java.lang.annotation.Annotation接口本身并不是一个注解类型,它只是一个普通的接口类型。
  4. 注解类型并不能显式地继承其他类或接口,虽然它确实隐式实现了接口java.lang.annotation.Annotation~

当然,以上概念了解即可。虽然注解和接口确实共享了同一个关键字,但它们在实际使用中具有不同的目的和功能,可谓千差万别

元注解#

元注解的概念来源于元数据,什么是元数据呢?

image-20230817175904439
image-20230817175904439

图中框起来的一列数据是什么意思呢?表头的学校就负责解释这一列数据,所以这一列数据都代表某个学生的学校信息。

像“学校”这样的,用于解释数据的数据,就是元数据 meta data

元注解:描述修饰注解的注解(注解的注解)

image-20230817180042910
image-20230817180042910

常用元注解:

@Retention元注解,来定义我们自己定义的注解的保留级别.

  • RetentionPolicy.RUNTIME
  • RetentionPolicy.CLASS 默认
  • RetentionPolicy.SOURCE

@Target元注解,注解可以作用的目标

对于注解而言,可以作用的目标:

  1. 整个类 ElementType.TYPE
  2. 成员变量 ElementType.FIELD
  3. 构造方法 ElementType.CONSTRUCTOR
  4. 成员方法 ElementType.METHOD

注解的使用(重点)#

类比类对象

User user = new User("zs",20);
User user2 = new User();

使用的时候注解需要通过@符号进行实例化, 对每个属性都要赋值

@注解名(属性1=属性值,属性2=属性值)

解释:

  1. ”@“可以认为相当于“new”关键字,必不可少。
  2. 注解相当于给Java代码打上一个标签,所以它必须要修饰Java代码的一个结构。比如修饰一整个类,一整个方法,一个成员变量等等。
  3. 实例化注解时,必须给注解的各个属性赋值,赋值方式是:属性名 = 属性值。如果是数组类型的注解属性,用”{}“赋值。如果有多个属性,赋值时用”,“隔开。

注意事项:

  • 每个属性都要赋值

  • 可以不赋值,但是要有默认值, default

  • 数组形式赋值 {}

  • 如果只有1个属性, 名字叫value, 可以简化赋值

  • 如果属性类型是引用类型, 不能是null

示例

@MyAnnotation
public class Test {
@MyAnnotation(666)
int num;
@MyAnnotation(value = 777, b = "777", c = {"777","aaa","bb"})
public void test() {
}
}
@interface MyAnnotation {
int value() default 123;
String b() default "abc";
String[] c() default {"123"};
}

注解处理器(难点)#

什么是注解处理器?

  • 获取注解信息, 根据注解信息进行处理

如何获取注解信息?

  • 通过反射获取注解信息

基本逻辑

  1. 获取注解所加的类的对应的Class对象
  2. 通过Class对象获取注解所加的类中的结构(Filed, Constructor, Method)对象
  3. 通过该结构对象判断是否使用了注解
  4. 通过该结构对象获取注解实例
  5. 通过注解实例获取注解信息
package _24annotation.com.cskaoyan._04handle;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
/**
* @description:
* @author: 景天
* @date: 2022/10/24 17:41
**/
public class Demo {
public static void main(String[] args) throws Exception{
// 获取字节码文件对象
Class<?> c = Class.forName("_24annotation.com.cskaoyan._04handle.Demo");
// 拿到方法对象
Method loginMethod = c.getDeclaredMethod("login");
// 判断方法上是否使用了注解
boolean annotationPresent = loginMethod.isAnnotationPresent(Login.class);
if (annotationPresent) {
// 获取注解实例
Login loginAnnotation = loginMethod.getAnnotation(Login.class);
// 获取属性值
String password = loginAnnotation.password();
String username = lo
ginAnnotation.username();
// 打印
System.out.println(password);
System.out.println(username);
}else {
System.out.println("没有使用注解");
}
}
@Login
public static void login(){
}
}
// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Login{
// 属性
String username() default "admin";
String password() default "123456";
}

练习:

通过创建自定义注解和注解处理器来实现对数据有效性的校验。

我们将创建一个名为 ValidateData 的注解,用于指定成员变量数据的最小长度、最大长度以及不满足条件时的报错信息。

然后,我们将编写一个注解处理器来实际执行这些校验。

步骤 1: 创建 Validated 注解

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 Validated {
int min() default 0; // 最小长度,默认为 0
int max() default Integer.MAX_VALUE; // 最大长度,默认为 Integer 的最大值
String message() default ""; // 校验失败时的报错信息,默认为空
}

步骤2: 将注解加在目标成员变量

public class User {
@Validated(min = 2,max = 5,message = "名字长度必须在2-5之间")
private String name;
@Validated(min = 18,max = 25,message = "年龄必须在18-25之间")
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}

步骤 3: 创建注解处理器

注解处理器将读取 ValidateData 注解的属性,并根据这些属性执行校验逻辑。

import java.lang.reflect.Field;
/**
* @description:
* @author: 景天
* @date: 2023/11/14 11:44
**/
public class ValidatedHandler {
public static void validate(Object obj) throws IllegalAccessException {
// 获取校验对象的Class对象
Class<?> clazz = obj.getClass();
// 获取所有成员变量并遍历 检查是否需要数据验证
for (Field field : clazz.getDeclaredFields()) {
// 是否使用了注解
if (field.isAnnotationPresent(Validated.class)) {
field.setAccessible(true);
// 获取注解实例
Validated validate = field.getAnnotation(Validated.class);
// 获取成员变量取值
Object value = field.get(obj);
// 成员变量类型不确定 做个分类处理
if (value instanceof String) {
// 对String类型的处理
checkStringLength((String) value, validate);
} else if (value instanceof Object[]) {
// 对数组的处理
checkArrayLength((Object[]) value, validate);
}else {
// 对数值类型的处理
checkIntRange(((int) value),validate);
}
// 可以添加对更多类型的支持
// .....
}
}
}
private static void checkIntRange(int value, Validated validate) {
if (value < validate.min() || value > validate.max()) {
throw new IllegalArgumentException(validate.message());
}
}
private static void checkStringLength(String value, Validated validate) {
if (value.length() < validate.min() || value.length() > validate.max()) {
throw new IllegalArgumentException(validate.message());
}
}
private static void checkArrayLength(Object[] value, Validated validate) {
if (value.length < validate.min() || value.length > validate.max()) {
throw new IllegalArgumentException(validate.message());
}
}
}

步骤 4: 使用注解和处理器进行测试

/**
* @description:
* @author: 景天
* @date: 2023/11/14 11:55
**/
public class ValidatedTest {
public static void main(String[] args) {
User user = new User("zsss", 20);
try {
ValidatedHandler.validate(user);
System.out.println("校验通过");
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}

注解的使用场景#

在 Java 中,注解被广泛应用于多种场景,提供了一种用于添加元数据到代码中的方式。注解不会直接影响代码的操作,但可以被工具、库和框架用于生成代码、进行编译时检查或在运行时处理。以下是一些常见的 Java 注解使用场景:

  1. 编译时检查
    • 用于提高代码质量,如 @Override 确保方法覆盖,@Deprecated 表示方法过时,@SuppressWarnings 抑制编译器警告。
  2. 框架和库
    • Web 框架如 Spring 使用注解来定义路由、请求方法、依赖注入等,例如 @Controller, @RequestMapping, @Autowired
  3. 测试代码
    • 单元测试框架如 JUnit 使用注解定义测试方法、测试前后的操作,例如 @Test
  4. 代码生成
    • 自动生成代码,如 Lombok 库使用 @Getter, @Setter, @AllArgsConstructor 等注解来自动生成 getter、setter 方法和构造函数。
  5. 配置和设置
    • 在 Spring 和其他依赖注入框架中,用于配置和设置应用程序,如 @Configuration, @Bean
  6. 拦截器和过滤器
    • 在 AOP(面向切面编程)中,用于定义切点和通知,如 @Aspect, @Before, @After
  7. 自定义业务逻辑
    • 创建自定义注解来实现特定的业务逻辑,如验证、日志记录等。

Java 注解提供了一种强大的机制来简化代码、提高可读性和维护性,并支持诸如配置管理、数据校验和框架开发等高级功能。通过注解,开发者可以将关键信息嵌入到代码的声明性部分,而不是执行性代码中。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Java 基础语法:注解
https://firefly-mu-weld.vercel.app/posts/20-注解/
作者
Daisy
发布于
2026-06-10
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Daisy
Hello, I'm Daisy.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签

文章目录