作者:手机用户2502927925 | 来源:互联网 | 2023-09-16 13:33
如果您是Web或企业开发人员(大多数Java开发人员都是如此),则您始终会使用注释。无论是在Spring,JEE还是Struts中,您都会看到很多。如果您使用像JUnit这样的单元
如果您是Web或企业开发人员(大多数Java开发人员都是如此),则您始终会使用注释。 无论是在Spring,JEE还是Struts中,您都会看到很多。 如果您使用像JUnit这样的单元测试工具,则同样适用。 如果您进行胖客户端或Android开发,则在实际的生产代码中可能不会经常看到它,而您与它们的相遇可能是生产工具,构建工具和测试。 如果您使用上下文和依赖注入,那么注释就是您的主体。
当然,您要食用它们。 但是您知道如何做基本事情,例如如何定义一个吗? 也许你会。 但据我估计,在大多数情况下,如果您不开发API或部署工具,则可能很少需要定义注释。 我认为注释是Java语言元素,通常不完全理解。 也许我错了,但这只是我的意见。 我们为什么不从这里开始。
基本注释定义
请注意注释定义的语法,其中@interface
表示类型。 这就是编译器识别注释类型的方式。 之所以使用这种语法,是因为注释类型基于与接口后面相同的管道。 还要注意方法声明。 您不会像在普通界面中那样实现这些功能。 编译器可以。 另外,(对于应用程序开发人员来说,) methods()
都在使用时像字段一样对待。 通知Ln。 8.我们只说年龄= 33,就好像年龄是普通成员而不是方法。
尽管从源代码中看不出来,但是在Ln上使用了注释。 8保留程序不变。 回想一***释仅仅是元数据,它们不会改变程序的性质(至少不会直接改变)。 批注可以根据批注的类型向开发工具发出信号,以执行某些操作。 一个很好的例子就是Java EE的CDI引擎。 结果是,CDI运行时基于X注释提供了X,但是实际的源文件本身仍然没有改变。
还请参见:
注释类型的限制[1]
- 注释不能参与继承。
- 注释方法可能没有参数。
- 注释不能通用,也不能指定throws子句。
- 注释必须返回:枚举或原始类型或注释,String或Class对象。 他们还可以返回这些类型的数组。
默认注释
是在标准库和第三方提供程序中定义和使用的Java代码中使用的几个注释。
Java SE API [2]中定义的包java.lang
提供了6个标准注释。 由于被包含在java.lang
,它们会自动导入每个Java程序中。 他们是:
- @Deprecated —“注释为
@Deprecated
的程序元素是不鼓励程序员使用的元素。” [2]
- @FunctionalInterface —“一种有用的注释类型,用于指示接口类型声明旨在成为Java语言规范定义的功能接口。” [2]
- @Override —“表示方法声明旨在覆盖超类型中的方法声明。” [2]
- @SafeVarags —“程序员断言,带注释的方法或构造函数的主体不会对其varargs参数执行潜在的不安全操作。” [2]
- @SupressWarnings —“指示应在带注释的元素(以及带注释的元素中包含的所有程序元素)中禁止命名的编译器警告。” [2]
Java SE API [2]中定义的java.lang.annotation
包提供6个标准注释。 他们是:
- @Documented —“如果注释类型为A的声明中存在注释
@Documented
,则元素上的任何@A
注释都将被视为该元素的公共合同的一部分。” [2]用于标记另一个注释。
- @Inherited —“指示注释类型是自动继承的。” [2]用于标记另一个注释。
- @Native —“表示可以从本机代码中引用定义常量值的字段。” [2]
- @Repeatable —“注释类型
java.lang.annotation.Repeatable
用于指示其声明(元-)注释的注释类型是可重复的。” [2]用于标记另一个注释。
- @Retention —“指示带注释类型的注释将保留多长时间。” [2]用于标记另一个注释。
- @Target- “指示注释类型适用的上下文。” [2]用于标记另一个注释。
我将不深入介绍这些注释。 但是,它们是标准Java SE开发中常见的注释。
还请参见:
单成员注释,默认值
简而言之,单成员注释是仅包含一个成员的注释。 该单个成员必须命名为value()
。 注意,如何使用注释中的括号(类似于方法参数)来分配值。
使用默认值
注意两件事。 首先是因为occupation()
具有默认值,所以在第一个示例中,当使用@SampleAnnotation
批注时,我们无需为其指定值。 在第二个示例中,请注意,因为根本不需要指定值。
还请参见:
保留政策
保留策略是使用注释时要理解的关键概念。 共有三种保留策略:CLASS,RUNTIME和SOURCE。 [2]它们在java.lang.annotation.RetentionPolicy
中定义。 批注保留策略指定批注将保留多长时间。 它们使用@Retention
批注指定。 默认保留策略是CLASS。 它们如下:
- CLASS: “注释由编译器记录在类文件中,但VM在运行时无需保留。” [2]
- RUNTIME: “注释将由编译器记录在类文件中,并在运行时由VM保留,因此可以以反射方式读取它们。” [2]
- 消息来源: “注释将由编译器丢弃。” [2]
一个例子
一些读者可能会立即注意到,注释一直保留到运行时的能力允许使用Reflection使用数据。 这是注释有用的关键能力。 接下来,我们将简要介绍这一点。 另请注意,有关局部变量声明 (未初始化的变量,仅是声明)的注释未保留在Java字节码(.class文件)中。
还请参见:
运行时的注释信息-反射
正如我们刚刚谈到的那样,可以在运行时通过使用反射获得批注数据。 尽管我们不会讨论使用此功能的全部范围,但我将提供一个简短的示例来演示其用法。 尽管我们没有使用它,但AnnotatedElement
接口提供了一些有用的方法来在运行时对Annotations进行反射。 最后,我没有直接使用java.lang.reflect
AnnotatedElement
和Field
(Ln.7,8)。
编码
输出
还请参见:
我们所做的是:
- 我们做了三个注释。 SampleAnnotation以及名为SampleAnnotationContainer的容器注释(用于SampleAnnotation的重复实例)和SampleAnnotation2。 我们在SampleAnnotation的目标及其容器Annotation中指定了ElementType类型的值数组。 我们创建了RUNTIME批注。
- 我们创建了一个名为Annotate的类,并对该类以及该类的默认构造函数,main方法和该类中的字段进行了注释。 仅使用
main()
方法的SampleAnnotation2注释了一个元素。
- 在
main()
方法内部,我们实例化了封闭类的一个对象以用作反射的基础,并实例化了一个类型为Annotation的数组来保存注释。
- 我们进行了五次尝试以获得注释。 前四个通用步骤是使用Class API在特定元素上的注释实例化Annotation类型的数组。
- 有很多方法可以做到,但是我们做到了如下:
- 创建一个我们感兴趣的类的对象。
- 调用
Object.getClass()
获得Class <>对象,我们可以使用该对象执行反射。
- 调用
getConstructor()
, getMethod()
, getField()
,以获取对要用来执行反射的元素的更具体的引用。 第一次尝试反射的步骤已跳过,该步骤在类本身上。
- 在返回的对象上调用
Class.getAnnotations()
方法以获取该对象的注释,并将该值分配给Annotation []类型的变量。
- 如有必要,我们处理了异常。
- 我们进行了一些格式化,并遍历了注释类型的数组并打印了元素。
- 我们使用了注释类型本身的引用来进行反射。 这是对
SampleAnnotation2
的引用。 我们使用与上述相同的方法实例化SampleAnnotation2
的实例,除了我们调用getAnnotation(SampleAnnotation2.class)
而不是getAnnotations()
。 为了打印内容,我们调用了Annotation的方法id()
。 因为当我们在id()
上调用类型为SampleAnnotation2
的对象时使用注释时,我们没有为id()
指定值,所以它返回默认值,即字符串“ Default ID#43216”。
还请参见:
注释可以做的其他事情
注释还可以做其他事情,尽管我们在这里不进行深入讨论,但值得一提的是它们,以便您可以自己发现它们。
正如@Repeatable
注释的存在所暗示的@Repeatable
,可以重复注释。 例如:
这些步骤实质上是:
- 创建注释以重复。
- 使用
@Repeatable(x.class)
标记要重复的注释,其中x是一个容器,用于保存要重复的注释的实例。
- 创建用于保存可重复注释实例的容器。
- 创建要重复的批注的数组,并将其命名为
value()
。 它必须包含一个专门命名为value()
。
- 向您的心注解。
- 在容器注释上调用
getAnnotation()
以获取注释的实例,而不是在原始注释本身上调用getAnnotation()
。
如前所述,从JDK 8开始,可以注释类型。 此概念称为类型注释。
类型注释的定义中必须包含“ @Target (ElementType.TYPE_USE)
”,以便编译器将其理解为类型注释。 类型注释在将编译器插件用于源代码工具,构建工具和开发工具时非常有用。 我在下面讨论了更多有关此的内容,包括如何注释方法的接收者(隐含此参数)。
您还可以创建标记注释 。 标记注释只是没有任何成员的注释。 例如, @Override
是标记注释。 它只是让代码的其他用户知道您正在重写超类方法。 例如,在创建的类中重写Object.toString()
时。
API还有许多其他细微的功能和规则,例如您可能不会注释类型为void的方法返回类型。 我将它留给您进行发现。
已经知道所有这些东西了吗? 在下面的评论中告诉我您的经历:)
还请参见:
参考文献
[1] — Java:《完全参考》,第9版; 希尔德·希尔尔德·希尔尔德
[2] —Java®平台,标准版和Java开发套件版本9 API规范
This article was originally published on Noteworthy.
翻译自: https://jaxenter.com/understand-annotations-java-148001.html