一、关于注释
从 JDK5之后开始支持注释(Annotation),可以在原始码中使用注释,对编译程序提供额外编译提示,或提供应用程序执行时期可读取的组态信息。注释可以仅用于原始码,编译后留在.class文档仅供编译程序读取或开放执行时期读取。
(1)常用标准注释
Java提供了一些标准注释,前面经常看到的@Override就是标准注释,它在原始码中提供编译程序的信息是,被注释的方法必须是父类或接口中已定义的方法,请编译程序协助是否真的为重新定义方法。
如果某个方法原先存在于 API 中,后来不建议再使用,可以在该方法上注释@Deprecated。例如:
public class Some { @Deprecated public void doSome() { ... } }
编译后的.class会存储这个信息,若有用户后续又想调用或重新定义这个方法,编译程序会提出警告(IDE通常会在方法上加删除线:
(2)在 JDK5之后加入泛型支持,对于支持泛型的 API,建议明确指定泛型真正类型,如果没有指定的话,编译程序会提出警告。例如程序代码若含有以下片段:
public void doSome(){ List list = new ArrayList(); list.add("Some"); )
由于List与 ArrayList支持泛型,但这里没有指定泛型真正类型,编译时会出现以下信息:
Note: xxx.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
(3)如果不想看到这个警告,可以使用@SuppressWarnings 指定抑制unchecked的警告产生:
@SuppressWarnings(value={ "unchecked"}) public void doSome(){ List list = new ArrayList(); list.add("Some"); )
SuppressWarnings的value可以指定要抑制的警告种类。例如,你真的想调用@Deprecated标示过的方法,又不想看到警告,可以这样:
@SuppressWarnings(value={ "deprecation"})也可以一次指定抑制多项警告:
@SuppressWarnings (value={"unchecked","deprecation"})
(4)heap pollution:也就是编译程序无法检查执行时期的类型错误,
在使用泛型定义不定长度自变量时,编译程序会提示开发人员,有没有注意到 heap pollution问题,这个问题是指执行时期无法具体确认(Reified)自变量类型(参数实际参考的对象类型)。如果开发人员确定避免了这个问题,则可以使用@SafeVarargs加以注释。例如:
public class Util{ @SafeVarargs public static <T>void doSome(List<String>... varargs){ ...略 } }
二、自定义注释类型
每个注释都会有个注释类型(Annotation Type),所有注释类型其实都是java.lang.annotation.Annotation子接口,@Override的注释类型为java.lang.Override,@Deprecated的
注释类型为java.lang.Deprecated等。之前介绍的标准注释类型,都位于java.lang包中。
(1)可以自定义注释。先来看看如何定义标示注释(Marker Annotation),也就是注释名称本身就是信息,对编译程序或应用程序来说,主要是检查是否有注释出现,并做出对应的动作。例如,@Override的作用就是标示注释。要定义一个注释可以使用@interface。例如:
public @interface Debug{}
编译完成后,就可以在程序代码中使用@Test注释了。例如:
public class SomeTestCase { @Test public void testDoSome() { ... } }
(2)如果注释名称本身无法提供足够信息,可进一步设定单值注释(Single-valueAnnotation)。例如;
public @interface Test2{ int timeout(); }
这表示注释将会有个timeout属性可以设定int值。例如:
@Test2 (timeout =10) public void testDoSome2 (){ ... }
注释属性也可以用数组形式指定。例如这样定义注释的话:
public @interface Test3{ String[] args(); }
就可以用数组形式指定属性
@Test3(args = { "arg1","arg2"}) public void testDoSome3 (){ ... }
在定义注释属性时,如果属性名称为value,则可以省略属性名称,直接指定值。例如:
public @interface Ignore { String value(); }
这个注释可以使用@Ignore(value = "message")指定,也可以使用@IIgnore ("message")指定,而以下这个注释:
public @interface TestClass { Class[] value(); }
可以使用@TestClass (value = {Some.class,0ther.class})指定,也可以使用@Testclass ({Some.class,Other.class})指定。
也可以对成员设定默认值,使用default关键字即可。例如:
public @interface Test4{ int timeout() default 0; String message default ""; }
这样一来,如果设定为@Test4,则 timeout属性默认值就是0,message默认就是空字符串。如果设定@Test4(timeout = 10,message = "逾时10秒"),则timeout属性的值就是10, message就是"逾时10秒"。如果是Class设定的属性比较特别,default之后不能接上null,会发生编译错误,必须自定义一个类作为默认值。例如:
public @interface Test5{ Class expected() default Default.class; class Default {} }
如果要设定数组默认值,可以在 default之后加上。例如;
public @interface Test6{ String[] args() default {}; }
必要时{}中可放置元素值。例如:
public @interface Test6{ String[] args() default {"args1","args2"}; }
(3)在定义注释时,可使用java.lang.annotation.Target限定注释使用位置,限定时可指定java.lang.annotation.ElementType的枚举值:
public enum ElementType{ TYPE, //可标注于类别、接口、列举等 FIELD, //可标注于数据成员 METHOD, //可标注于方法 PARAMETER, //可标注于方法上的参数 CONSTRUCTOR, //可标注于构造函数 LOCAL_VARIABLE, //可标注于局部变量 ANNOTATION_TYPE, //可标注于标注类型 PACKAGE //可标注于包 TYPE_PARAMETER, //可标注于类型参数,JDK8 新增 TYPE_USE //可标注于各式类型,JDK8新增 }
例如想将@Test8限定只能用于方法:
import java.lang.annotation.Target; import java.lang.annotation. ElementType; @Target({ElementType.METHOD}) public @interface Test8{}
尝试在方法以外的地方加上@Test8就会发生编译错误。
(4)在制作 JavaDoc文件时,默认并不会将注释数据加入文件中,如果想要将注释数据加入文件,可以使用java.lang.annotation.Documented。例如:
import java.lang.annotation.Documented; @Documented public @interface Test9 {}
如果在文件中使用到eTest9,则产生JavaDoc后,文件中就会包括@Test9的信息。
(5)在定义注释类型并使用于程序代码时,默认父类设定的注释,不会被继承至子类。在定义注释时设定java.lang.annotation.Inherited注释,就可以让注释被子类继承。例如:
import java.lang.annotation. Inherited; @Inherited public @interface Test10{}
三、JDK8标注增强功能
(1)JDK8的ElementType多了两个枚举成员TYPE_PARAMETER、TYPE_USE,它们是用来限定哪个类型可以进行标注。举例来说,如果想要对泛型的类型参数(Type parameter)进行标注:
public class MailBox<@Email T> { ... }
那么,你在定义@Email时,必须在@Target设定ElementType.TYPE PARAMETER,表示这个标注可用来标注型态参数。例如:
import java.lang.annotation. Target; import java. lang.annotation.ElementType; @Target (ElementType.TYPE_PARAMETER) public @interface Email{}
ElementType.TYPE_USE可用于标注在各式类型,因此上面的范例也可以将ElementType.TYPE_PARAMETER 改为 ElementType.TYPE_USE,一个标注如果被设定为ElementType.TYPE_USE,
只要是类型名称,都可以进行标注。
(2)JDK8除了ElementType多了两个枚举成员TYPE_PARAMETER、TYPE_USE之外,还新增了一个 @Repeatable,可以让你在同一个位置重复相同标注。举例来说你也许本来定义了以下的@Filter标注:
public @interface Filter { String[] value(); }
这可以让你如下进行标注:
@Filter({" /admin","/manager"}) public interface SecurityFilter { ... }
四、执行时期读取注释信息
程序代码中若使用了自定义注释,默认会将注释信息存储于.class文档,可被编译程序或位码分析工具读取,但执行时期无法读取注释信息。如果希望在执行时期读取注释信息,可以在自定义注释时使用java.lang.annotation.Retention搭配java.lang.annotation,RetentionPolicy枚举指定:
public enum RetentionPolicy { SOURCE //注释信息留在原始码(不会存储至.class文档) CLASS //注释信息会存储至.class 文档,但执行时期无法读取 RUNTIME //注释信息会存储至.class文档,但执行时期可以读取 }
JDK8新增了getDeclaredAnnotation()、getDeclaredAnnotationsByType()、getAnnotationsByType()三个方法,getDeclaredAnnotation ()可以让你取回指定的标注,至于getDeclaredAnnotationsByType()与getAnnotationsByType(),在指定@Repeatable 的标注时,会找寻收集重复标注的容器,相对来说,getDeclaredAnnotation()与getAnnotation()就不会处理@Repeatable的标记。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)