随笔 - 57,  文章 - 6,  评论 - 0,  阅读 - 29094

一、关于注释

从 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的标记。













posted on   山与月  阅读(46)  评论(0编辑  收藏  举报
编辑推荐:
· 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)
点击右上角即可分享
微信分享提示