Java 注解
上次修改时间:2024 年 7 月 4 日
在本文中,我们讨论 Java 注解。
注解是一种特殊的代码元素,它提供关于程序的附加信息。 它们使用 @ 符号附加到类、方法、字段和其他程序元素。
注解本身并不直接影响代码的运行方式,而是提供可供各种工具使用的元数据
- 编译器:编译器可以使用注解来检查错误或在编译期间抑制警告。 例如,@Override 注解告诉编译器验证方法是否确实覆盖了超类中的方法。
- 处理工具:注解可以在编译或部署期间由各种工具处理。 这些工具可以使用注解中的信息来生成代码、配置文件或执行其他任务。 例如,框架可能会使用注解来配置类如何映射到数据库表。
- 运行时:一些注解可在运行时使用反射进行内省。 这允许程序检查类和方法上的注解并采取相应的措施。
- 它们是元数据:注解提供关于程序的补充信息,而不是核心功能。
- 自定义与预定义:您可以定义自己的注解,也可以使用作为 Java 平台或第三方库一部分的预定义注解。
- 作用域:注解可以应用于各种程序元素,如类、方法、字段等。
- 保留策略:注解可以有不同的生命周期。 有些仅在编译期间保留,而另一些则可以通过反射在运行时使用。
@Override 注解
Java 中的 @Override
注解专门用于继承中的方法重写。 它是一个标记注解,用于提高代码清晰度并在编译期间捕获错误。
@Override
的作用- 表明意图:通过将
@Override
放置在子类中的方法声明之前,您明确告诉编译器您打算覆盖超类中的方法。 - 编译器检查:编译器验证带有
@Override
注解的方法是否确实覆盖了直接超类中具有相同名称、参数列表和返回类型的方法。 如果不是,则会抛出编译错误。 这有助于防止方法名称中的错别字或意外重载(具有相同名称但参数列表不同的方法)等错误。 - 提高可读性:使用
@Override
可以突出显示旨在覆盖父类功能的的方法,从而使您的代码更清晰。
@Override
对于重写方法不是强制性的。 编译器通常可以根据签名判断一个方法是否打算覆盖。 但是,为了更好的代码清晰度和及早发现潜在错误,通常认为使用 @Override 是一种好的做法。
@Override
不用于实现接口中的方法。 接口只声明方法,实现接口的子类必须提供自己的实现。 编译器本质上会检查这种关系。
class User { private final String firstName; private final String lastName; private final String occupation; public User(String firstName, String lastName, String occupation) { this.firstName = firstName; this.lastName = lastName; this.occupation = occupation; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public String getOccupation() { return occupation; } @Override public String toString() { final StringBuilder sb = new StringBuilder("User{"); sb.append("firstName='").append(firstName).append('\''); sb.append(", lastName='").append(lastName).append('\''); sb.append(", occupation='").append(occupation).append('\''); sb.append('}'); return sb.toString(); } } void main() { User[] users = { new User("John", "Doe", "gardener"), new User("Roger", "Roe", "driver"), new User("Paul", "Smith", "teacher"), }; for (User user : users) { System.out.println(user); } }
在示例中,我们重写了 Object
的 toString
方法。 该方法用 @Override
注释。 如果该函数与父函数不匹配(例如,我们使用 private
而不是 public
),我们会收到警告:无法降低从 Object 继承的方法的可见性。
@Deprecated 注解
Java 中的 @Deprecated
注解用于标记不再推荐使用的类、方法、字段或构造函数。 它可以作为对开发人员的警告,他们应该避免使用这些元素并考虑替代方案。
@Deprecated
注解的关键特性
- 信号意图:通过使用
@Deprecated
注释一个程序元素,您可以表明不鼓励在新代码中使用它。 可能有更好的替代方案可用,或者该元素可能计划在未来版本中删除。 - 编译器警告:当编译器遇到标记为
@Deprecated
的代码元素时,它会生成一条警告消息。 这有助于开发人员识别潜在问题,并鼓励他们迁移到建议的替代方案。 - 重构:
@Deprecated
注解通常在 API 的演化过程中使用。 随着库和框架的成熟,某些功能可能会过时或被更有效的方法取代。 弃用允许逐步过渡,而不会破坏依赖旧元素的现有代码。
@Deprecated
注解的其他特性(Java 9 及更高版本)
- since(可选):指定元素被弃用的版本。 这为开发人员提供了关于弃用历史的上下文。
- forRemoval(可选):指示该元素计划在未来的主要版本中删除。 这种更强的警告鼓励开发人员尽快找到替代方案。
class PassWordGenerator { @Deprecated public String generatePassword() { return "generated password"; } public String generateSecurePassword() { return "a secure password"; } } // @SuppressWarnings("deprecation") void main() { var pgen = new PassWordGenerator(); System.out.println(pgen.generateSecurePassword()); System.out.println(pgen.generatePassword()); }
在示例中,我们用 @Deprecated
注释标记 generatePassword
,因为它将被更安全的替代方案取代。
可以使用 @SuppressWarnings("deprecation")
注解来抑制 @Deprecated
注解。
自定义注解
我们可以在 Java 中创建自己的注解。 下表总结了自定义 Java 注解的关键要素
元素 | 描述 |
---|---|
@interface | 创建自定义注解的声明。 |
@Target | 指定注解可以应用的位置(字段、类、方法等)。 |
@Retention | 确定注解信息保留的时间(编译时或运行时)。 |
元素(方法) | 定义注解的属性或参数(带有数据类型和默认值)。 |
文档注释 | 用于记录注解及其用法的可选注释。 |
下一个示例创建一个自定义注解。
package com.zetcode; 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 ClassDescription { String description(); }
我们有一个 ClassDescription
,它以类为目标。
package com.zetcode; import java.util.List; import java.util.Optional; public class AnnotationProcessor { public void process(List<Object> objects) { objects.forEach(obj -> { Class<?> clazz = obj.getClass(); ClassDescription annotation = clazz.getAnnotation(ClassDescription.class); // if (annotation != null) { // System.out.println(annotation.annotationType().getName()); // System.out.println(annotation.description()); // } Optional.ofNullable(annotation) .ifPresentOrElse( ann -> { System.out.println(ann.annotationType().getName()); System.out.println(ann.description()); }, () -> System.out.println("No annotation found") ); }); } }
AnnotationProcessor
将处理注解。
package com.zetcode; import java.util.List; @ClassDescription(description = "this is a User class") class User { } @ClassDescription(description = "this is a Test class") class Test { } class Hello { } public class CustomAnnotation { private String field; public static void main(String[] args) { List<Object> objects = List.of(new User(), new Test(), new Hello()); var processor = new AnnotationProcessor(); processor.process(objects); } }
我们有两个类用 @ClassDescription
装饰。 我们使用 AnnotationProcessor
处理实例对象,它将打印装饰类的描述。
来源
在本文中,我们介绍了 Java 中的注解。 我们展示了如何使用现有注解以及如何创建一个自定义注解。
作者
列出所有Java教程。