“核心技术Ⅱ:脚本、编译、注解处理:注解”的版本间差异
跳到导航
跳到搜索
(→使用注解) |
|||
(未显示同一用户的1个中间版本) | |||
第227行: | 第227行: | ||
== 标准注解 == | == 标准注解 == | ||
Java SE 在“java.lang”、“java.lang.annotation”和“javax.annotation”包中定义了大员的注解接口。 | |||
*(其中四个是元注解,用于描述注解接口的行为属性,其他的三个是规则接口,可以用它们来注解你的源代码中的项) | |||
[[File:标准注解.png|800px]] | |||
=== 用于编译的注解 === | === 用于编译的注解 === | ||
# “'''@Deprecated'''”:可以被添加到任何不再鼓励使用的项上。 | |||
#:(与Javadoc标签“@deprecated”具有同等功效) | |||
# “'''@SuppressWarnings'''”:注解会告知编译器阻止特定类型的警告信息。 | |||
#: <syntaxhighlight lang="java"> | |||
@SuppressWarnings("unchecked") | |||
</syntaxhighlight> | |||
# “'''@Override'''”:(只能应用到方法上)编译器会检查具有这种注解的方法是否真正覆盖了一个来自于超类的方法。 | |||
# “'''@Generated'''”:注解的目的是供代码生成工具来使用。 | |||
#:(任何生成的源代码都可以被注解,从而与程序员提供的代码区分开) | |||
#: <syntaxhighlight lang="java"> | |||
@Generated("com.horstmann.beanproperty", "2008-01-04T12:08:56.235-0700"); | |||
</syntaxhighlight> | |||
=== 用于管理资源的注解 === | === 用于管理资源的注解 === | ||
# “'''@PostConstruct'''”、“'''@PreDestroy'''”:用于控制对象生命周期的环境。 | |||
#:(标记了这些注解的方法应该在对象被构建之后,或者在对象被移除之前,紧接着调用) | |||
# “'''@Resource'''”:注解用于资源注入。 | |||
=== 元注解 === | === 元注解 === | ||
# “'''@Target'''”:元注解可以应用于一个注解,以限制该注解可以应用到哪些项上。 | |||
#:(没有 @Target 限制的注解可以应用于任何项上) | |||
#: <syntaxhighlight lang="java"> | |||
@Target({ElementType.TYPE, ElementType.METHOD}) | |||
public @interface BugReport | |||
</syntaxhighlight> | |||
#: [[File:@Target注解的元素类型.png|800px]] | |||
# “'''@Retention'''”:指定一条注解应该保留多长时间。 | |||
#:(默认值是“RetentionPolicy.CLASS”) | |||
#: [[File:@Retention注解的保留策略.png|800px]] | |||
# “'''@Documented'''”:为像“Javadoc”这样的归档工具提供了一些提示。 | |||
#* 将一个注解应用到它自身上是合法的。例如,“@Documented”注解被它自身注解为“@Documented”。 | |||
# “'''@Inherited'''”:(只能应用于对类的注解)如果一个类具有继承注解,那么它的所有子类都自动具有同样的注解。 | |||
#:(这使得创建一个与“Serializable”这样的标记接口具有相同运行方式的注解变得很容易) | |||
#:(“@Serializable”注解应该比没有任何方法的“Serializable”标记接口更适用。) | |||
# “'''@Repeatable'''”:用于标记可重复的注解。 | |||
* 对于 JavaSE 8 来说,将同种类型的注解多次应用于某一项是合法的。为了向后兼容,可重复注解的实现者需要提供一个容器注解,它可以将这些重复注解存储到一个数组中: | |||
*: <syntaxhighlight lang="java"> | |||
@Repeatable (TestCases.c1ass) | |||
@interface TestCase | |||
{ | |||
String params(); | |||
String expected(); | |||
} | |||
@interface TestCases | |||
{ | |||
TestCase[] value(); | |||
} | |||
</syntaxhighlight> | |||
*: 无论何时,只要用户提供了两个或更多个“@TestCase”注解, 那么它们就会自动地被包装到一个“@TestCases”注解中。 | |||
* 在处理可重复注解时:如果调用“getAnnotation”来查找某个可重复注解,而该注解又确实重复了,那么就会得到null。(因为重复注解被包装到了容器注解中) | |||
*: 此时,应该调用“getAnnotationsByType”:会“遍历”容器,并给出一个重复注解的数组。如果只有一条注解,那么该数组的长度就为1。 | |||
== | == 源码级注解处理【???】 == | ||
=== 注解处理 === | === 注解处理 === | ||
=== 语言模型API === | === 语言模型API === | ||
=== 使用注解来生成源码 === | === 使用注解来生成源码 === |
2020年12月20日 (日) 02:10的最新版本
关于注解
注解是那些插入到源代码中使用其他工具可以对其进行处理的标签。 这些工具可以在源码层次上进行操作, 或者可以处理编译器在其中放置了注解的类文件。 注解不会改变程序的编译方式。 Java编译器对于包含注解和不包含注解的代码会生成相同的虚拟机指令。 为了能够受益于注解, 需要选择一个处理工具, 然后向你的处理工具具可以理解的代码中插入注解, 之后运用该处理工具处理代码。 注解的使用范围: 附属文件的自动生成, 例如部署描述符或者 bean信息类。 测试、 日志、 事务语义等代码的自动生成。
- 在Java中,注解是当作一个修饰符来使用的,是代码的一部分;
- 注解自身并不会做任何事情,它需要工具支持才会有用;
- (如:JUnit4 测试工具可能会调用所有标识为“@Test”的方法)
- 注解可以定义成包含元素的形式;
- (如:“@Test(ti111eout="10000")”)
- 每个注解都必须通过一个注解接口进行定义;
- (“@interface”声明创建了一个真正的Java接口)
使用注解
注解接口(@interface)
注解是由注解接口来定义的:
modifiers @interface AnnotafionName
{
elementDeclaration1
elementDeclaration2
...
}
每个元素声明都具有下面这种形式:
type elementName();
// 或
type elementName() default value;
- 所有的注解接口都隐式地扩展自“java.lang.annotation.Annotation”接口。
- (这是一个常规接口,不是一个注解接口)
- 无法扩展注解接口。
- (即:所有的注解接口都直接扩展自“java.lang.annotation.Annotation”)
- 不用提供那些实现了注解接口的类。
注解元素的类型:
- 基本类型(int、short,、long、byte、char、double、float或者boolean);
- String;
- Class;(具有一个可选的类型参数, 例如“Class <? extends MyClass>”)
- enum 类型;
- 注解类型;
- 由以上类型组成的数组;(由数组组成的数组不是合法的元素类型)
示例:
public @interface BugReport
{
enum Status { UNCONFIRMED, CONFIRMED, FIXED, NOTABUG };
boolean showStopper() default false;
String assignedTo() default "[none]";
Class<?> testCase() default Void.class;
Status status() default Status.UNCONFIRMED;
Reference ref() default @Reference();// an annotation type
String[] reportedBy();
}
注解语法
每个注解都具有下面这种格式:
@AnnotationName(elementName1=value1, elementName2=value2,...)
- 元素的顺序无关紧要。
- 如果某个元素的值并未指定,那么就使用声明的默认值。
- (默认值并不是和注解存储在一起的;相反地,它们是动态计算而来的)
- 两种简化注解:
- “标记注解”:注解中没有任何元素,或者所有元索都使用默认值;
@BugReport
- “单值注解”:有且仅有一个名为“value”的元素;
@ActionlistenerFor("ye11owButton")
- “标记注解”:注解中没有任何元素,或者所有元索都使用默认值;
- 一个项可以有多个注解。
- 如果注解的作者将其声明为可重复的(“@Repeatable”),那么可以多次重复使用同一个注解。
@BugReport(showStopper=true, reportedBy="Joe") @BugReport(reportedBy={"Harry", "Carl"}) public void checkRandomlnsertions()
- 如果元素值是一个数组,则值用括号括起来:
@BugReport(..., reportedBy={"Harry", "Carl"})
- 如果该元素具有单值,则可以忽略这些括号:
@BugReport(..., reportedBy="Joe") // same as {"Joe"}
- 一个注解元素可以是另一个注解,那么就可以创建出任意复杂的注解:
@BugReport(ref=@Reference(id="3352627"), ...)
- 因为注解是由编译器计算而来的,因此,所有元素值必须是编译期常量。
- 注解元素永远不能设置为“null”,甚至不允许其默认值为“null”。
- (可以使用其他的默认值,例如“""”或者“Void.class”)
- 不能在注解中引入循环依赖。
- (例如,因为“BugReport”具有一个注解类型为“Reference”的元素,那么“Reference”就不能再拥有一个类型为“BugReport”的元素)
注解用法
注解可以出现在许多地方,可以分为两类:“声明”和“类型”用法。
- 习惯(但不是必需)的做法,是将“声明”注解放牲到其他修饰符的前面,将“类型”用法注解放置到其他修饰符的后面。
private @NonNull String text; // Annotates the type use @Id private String userId; // Annotates the variable
- 注解的作者需要指定特定的注解可以出现在哪里。
- 如果一个注鲜可以同时应用于变量和类型用法,并且它确实被应用到了某个变量声明上,那么该变量和类型用法就都被注解了:
public User getUser(@NonNull String userId) // 如果 @NonNull 可以同时应用于参数和类型用法,那么 userId 参数就被注解了,而其参数类型是 @NonNull String
用法:声明注解
“声明”用法可以出现在,以下声明处:
- 包
- 类(包括“enum”)
- 接口(包括注解接口)
- 方法
- 构造器
- 实例域(包含“enum”常量)
- 局部变量
- 参数变扯
- 类型参数
- 对于类和接口,需要将注解放置在“class”和“interface”关键词的前面:
@Entity public class User{. . . }
- 对于变量, 需要将它们放置在类型的前面:
@SuppressWarnings("unchecked") List<User> users =. . .; public User getUser(@Param("id") String userld)
- 泛化类或方法中的类型参数,如下注解:
public class Cache<@Immutab1e V> { . . . }
- 包是在文件“package-info.java”中注解的:
/** Package-level Javadoc */ @GPL(version="3") package com.horstmann.corejava; import org.gnu.GPL;
- 对局部变责的注解只能在源码级别上进行处理。类文件并不描述局部变量。因此,所有的局部变量注解在编译完一个类的时候就会被遗弃掉。同样地,对包的注解不能在源码级别之外存在。
用法:类型用法
“类型”用法可以出现在,以下声明处:
- 与泛化类型引元一起使用:
List<@NonNul1 String> Comparator.<@NonNul1 String> reverseOrder()。
- 数组中的任何位置:
@NonNull String[][] words // words[i][j] 不为null String @NonNull [][] words // words 不为null String[] @NonNull [] words // words[i] 不为null
- 与超类和实现接口一起使用:
class Warning extends @Localized Message
- 与构造器调用一起使用:
new @Localized String( . . . )
- 与强制转型和“instanceof”检查一起使用:
- (只供外部工具使用,对强制转型和instanceof检查不会产生任何影响)
(@Localized String) text if (text instanceof @Localized String)
- 与异常规约一起使用:
public String read() throws @Localized IOException
- 与通配符和类型边界一起使用:
List<@Localized ? extends Message> List<? extends @Localized Message>
- 与方法和构造器引用一起使用:
@Localized Message::getText
有多种类型位笐是不能被注解的:
@NonNull String.class // ERROR: Cannot annotate class literal import java.lang.@NonNull String; // ERROR: Cannot annotate import
注解this
【???】
public class Point { pub1ic boo1ean equa1s (@ReadOn1y Point this, @ReadOn1y Object other){ . . . } }
第一个参数被称为接收器参数,它必须被命名为“this”,而它的类型就是要构建的类(“Point”)。
- 只能为方法而不能为构造器提供接收器参数。
标准注解
Java SE 在“java.lang”、“java.lang.annotation”和“javax.annotation”包中定义了大员的注解接口。
- (其中四个是元注解,用于描述注解接口的行为属性,其他的三个是规则接口,可以用它们来注解你的源代码中的项)
用于编译的注解
- “@Deprecated”:可以被添加到任何不再鼓励使用的项上。
- (与Javadoc标签“@deprecated”具有同等功效)
- “@SuppressWarnings”:注解会告知编译器阻止特定类型的警告信息。
@SuppressWarnings("unchecked")
- “@Override”:(只能应用到方法上)编译器会检查具有这种注解的方法是否真正覆盖了一个来自于超类的方法。
- “@Generated”:注解的目的是供代码生成工具来使用。
- (任何生成的源代码都可以被注解,从而与程序员提供的代码区分开)
@Generated("com.horstmann.beanproperty", "2008-01-04T12:08:56.235-0700");
用于管理资源的注解
- “@PostConstruct”、“@PreDestroy”:用于控制对象生命周期的环境。
- (标记了这些注解的方法应该在对象被构建之后,或者在对象被移除之前,紧接着调用)
- “@Resource”:注解用于资源注入。
元注解
- “@Target”:元注解可以应用于一个注解,以限制该注解可以应用到哪些项上。
- “@Retention”:指定一条注解应该保留多长时间。
- “@Documented”:为像“Javadoc”这样的归档工具提供了一些提示。
- 将一个注解应用到它自身上是合法的。例如,“@Documented”注解被它自身注解为“@Documented”。
- “@Inherited”:(只能应用于对类的注解)如果一个类具有继承注解,那么它的所有子类都自动具有同样的注解。
- (这使得创建一个与“Serializable”这样的标记接口具有相同运行方式的注解变得很容易)
- (“@Serializable”注解应该比没有任何方法的“Serializable”标记接口更适用。)
- “@Repeatable”:用于标记可重复的注解。
- 对于 JavaSE 8 来说,将同种类型的注解多次应用于某一项是合法的。为了向后兼容,可重复注解的实现者需要提供一个容器注解,它可以将这些重复注解存储到一个数组中:
@Repeatable (TestCases.c1ass) @interface TestCase { String params(); String expected(); } @interface TestCases { TestCase[] value(); }
- 无论何时,只要用户提供了两个或更多个“@TestCase”注解, 那么它们就会自动地被包装到一个“@TestCases”注解中。
- 在处理可重复注解时:如果调用“getAnnotation”来查找某个可重复注解,而该注解又确实重复了,那么就会得到null。(因为重复注解被包装到了容器注解中)
- 此时,应该调用“getAnnotationsByType”:会“遍历”容器,并给出一个重复注解的数组。如果只有一条注解,那么该数组的长度就为1。