JAVA的注解

注解

1、注解的作用

  • 可以用于编译器发现错误或者抑制异常
  • 编译时可以利用注解生成代码,部署时可以处理XML文件等。
  • 可以用于运行时检查

2、基础

一般格式如:@Entity,使用@开头,向编译器表示这是个注解.
注解可以包含参数:

@Author(
    name = "Benjamin Franklin",
       date = "3/27/2003"
)    
class MyClass {...}

或者:

@SupressWarnings(value = "unchecked")
void myMethod() {...}  

如果只有一个参数名为value, 则该参数名可以忽略不写。

@SuppressWarnings("unchecked")
void myMethod() { ... }

如果注解不包含参数,括号也可以忽略不写,而且多个注解可以叠加。

@Author(name = "Jane Doe")
@EBook
class MyClass { ... }

也可以同时存在多个相同类型的注解:

@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }

重复注解在Java SE 8被支持. 在前面的例子中,Override和SuppressWarnings是预定义注解,详见predefined Java annotations. 其他的如AuthorEbook为自定义注解.

3、注解的使用场景

注解可以应用于各种声明: 类、变量、方法,以及其他编程元素。当使用在声明时,注解一般出现在声明的同一行.

  • 创建类实例

    new @Interned MyObject();
    
  • 类型转换

    myString = (@NonNull String) str;
    
  • 实现接口

    class UnmodifiableList<T> implements
        @Readonly List<@Readonly T> { ... }
    
  • 抛出异常声明

    void monitorTemperature() throws
        @Critical TemperatureException { ... }
    

这种形式的注解被称为类型注解,详见Type Annotations and Pluggable Type Systems

4、声明注解类型##

许多注解可以在代码中替换注释。
一般在类开始的地方会加上一些注释,如:

public class Generation3List extends Generation2List {

   // Author: John Doe
   // Date: 3/17/2002
   // Current revision: 6
   // Last modified: 4/12/2004
   // By: Jane Doe
   // Reviewers: Alice, Bill, Cindy

   // class code goes here

}

如果需要用注解类型的元数据替换该注释,需要首先定义一个注解类型,如:

@interface ClassPreamble {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}

注解类型的定义与接口类型定义类似,只是需要在关键字interface前面加上 @ 符号。 注解类型是接口的一种形式。
定义完注解类型之后,就可以使用该注解,如:

@ClassPreamble (
   author = "John Doe",
   date = "3/17/2002",
   currentRevision = 6,
   lastModified = "4/12/2004",
   lastModifiedBy = "Jane Doe",
   // Note array notation
   reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {

// class code goes here

}

Note: 如果需要 @ClassPreamble 中的信息能出现在Javadoc生成的文档中,需要加上 @Documented 注解.

// import this to use @Documented
import java.lang.annotation.*;

@Documented
@interface ClassPreamble {

   // Annotation element definitions

}

4、预定义注解类型

Java SE中定义了一系列注解类型。有些用于Java编译器,有些用于其他的注解。

Java语言中使用的注解类型

在java.lang中定义的注解类型有@Deprecated, @Override 和 @SuppressWarnings.

@Deprecated @Deprecated注解表示所标记的元素是退化的并且在未来不再使用。当在程序中使用@Deprecated注解标记方法、类或者变量时,编译器会产生警告。当一个元素退化时,也应该使用Javadoc的@deprecated标签,如下例所示。在Javadoc和注解中都使用 @ 符号并不是一个巧合: 他们存在概念上的关联。 同时请注意:注解使用大写的D,Javadoc使用小写的d开头。

 // Javadoc comment follows
/**
 * @deprecated
 * explanation of why it was deprecated
 */
@Deprecated
static void deprecatedMethod() { }

@Override@Override注解告诉编译器该方法会覆写超类的方法。覆写将在interfaces and Inheritance中讨论。

 // mark method as a superclass method
 // that has been overridden
 @Override 
 int overriddenMethod() { }

当覆写一个方法时,该注解并不是必须的,它只是帮助预防错误。如果用@Override标记的方法不能正确覆写超类方法,则编译器会报错。

@SuppressWarnings@SuppressWarnings该注解告诉编译器抑制特定警告的产生。 如下例所示,退化函数的使用,编译器会产生警告,在这种情况下,注解会抑制警告的产生。

 // use a deprecated method and tell 
   // compiler not to generate a warning
   @SuppressWarnings("deprecation")
    void useDeprecatedMethod() {
    // deprecation warning
    // - suppressed
    objectOne.deprecatedMethod();
    }

每个编译器警告都属于一个类别。Java语言说明书列了两个类别:deprecation和unchecked。unchecked异常一般出现在泛型产生之前的遗留代码中。为抑制多个类别的警告,可以使用:

@SuppressWarnings({"unchecked", "deprecation"})

@SafeVargs@SafeVarargs注解,当应用在方法或构造函数中,会假定对于varargs参数,代码不会执行潜在的不安全的操作,该注解会抑制跟varargs有关的unchecked警告。

FunctionallInteface@FunctionallInteface,出现在Java SE 8中,表示该注解声明的类型是一个函数接口。

应用在注解上的注解

应用在其他注解上的注解被称为 meta-annotations,(元注解?). meta-annotations一般定义在java.lang.annotation.

@Retention@Retention指定被标注的注解如何存储:

  • RetentionPolicy.SOURCE - 表示被标注的注解只保持在源代码层面,会被编译器忽略。

  • RetentionPolicy.CLASS - 表示被标注的注解会在编译时被编译器保持,但是会被JVM忽略。

  • RetentionPolicy.RUNTIME - 表示被标注的注解会被JVM保持,所以可以在运行时使用。

@Documented@Documented表示被标注的注解无论用在什么元素上,该注解都会被Javadoc工具生成在文档中。(默认情况下,注解不会被包括在Javadoc中)。详细说明请参考Javadoc tools page

@Target@Target表示被标注的注解会被限制在那种Java元素上使用。 目标注解类型如下:

  • ElementType.ANNOTATION_TYPE 表示可以应用在注解类型上。

  • ElementType.CONSTRUCTOR 表示可以应用在构造函数上。

  • ElementType.FIELD 表示可以应用在变量或属性上。

  • ElementType.LOCAL_VARIABLE 表示可以应用在本地变量上。

  • ElementType.METHOD 表示可以应用在函数上。

  • ElmentType.PACKAGE 表示可以应用在包声明上。

  • ElementType.PARAMETER 表示可以应用在方法参数上。

  • ElementType.TYPE 表示可以应用在类的任何元素上。

Inherited@Inherited 表示该注解类型可以从超类继承。(默认是否)。当用户查找注解类型,但是类中并没有应用该注解,则该类的超类也会被查询。该注解只能应用在类声明上。

@Repeatable@Repeatable在Java SE 8中引入,表示该注解可以在同一个声明或类型上多次使用。详细说明见Repeating Annotations

类型注解和可插拔类型系统

在Java SE 8发布之前,注解只能用于声明。 之后,注解可以用于任何类型使用。这意味着注解可以在你使用类型的任何地方使用。一些简单的例子,如类实例创建(new), casts, implements,以及 throw。 这些形式的注解被称为类型注解。

类型注解支持提高了Java程序的强类型检查的分析能力。Java SE 8并没有提供类型检查框架,但是它允许你实现或者下载类型检查框架,作为一个或多个可插拔模块,用于与Java编译器的连接。

比如,你想确保在你的程序中一个特定的变量永远不会被设为null; 你想避免触发NullPointerException。 你可以写一个自定义插件来检查这个变量。你可以用注解标记该变量,表示它不能被赋值为null。这个变量声明类似于:

@NonNull String str;

当你编译该代码,并在命令行中引入NotNull模块时,如果编译器检查到潜在的错误时,编译器就会打印警告,提醒你修改代码避免错误。当做完修改移除警告时,当程序运行该特定错误就不会发生。

可以使用多个类型检查模块,每个模块检查不同的错误。用这种方式,你可以在Java类型系统之上构建,当你想需要的时候可以使用它们进行特殊类型检查。

在很多情况下,你把需要编写自己的类型检查模块。有许多三方为你做了这些工作。比如,你可以使用华盛顿大学的Checker Framework。该框架包括NotNull模块,以及一些正则表达式模块。详见Checker Framework

5、重复注解##

在一些情况下,你需要在一个声明或类型上应用同一个注解。在Java SE 8发布之后,
reapeating annotations可以让你实现这些功能。

比如,你想使用定时服务以在给定的时间或时间表执行某个函数,类似于UNIX的cron。现在你可以使用一个定时器去执行函数doPeriodicCleanup, 在每个月的最后一天和每个周五的晚上11点执行。为了执行这个定时器,创建一个@Schedure注解,并且在doPeriodicCleanup函数上应用两次。第一次指定每个月的最后一天,第二次指定每周五的晚上11点,示例如下:

@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }

你也可以在任意使用注解的地方使用重复注解。比如,你想在一个类上处理未授权访问异常。你可以使用@Alert注解标注该类:

@Alert(role="Manager")
@Alert(role="Administrator")
public class UnauthorizedAccessException extends SecurityException { ... }

兼容性考虑,重复注解一般会存储在由编译器生成的容器注解中(container annotation)。 为了让编译器作者,需要加两个声明.

步骤1: 声明一个重复注解类型

注解类型必须被@Repeatable元注解标注。 下面的示例定义了自定义注解@Schedule为重复注解类型:

import java.lang.annotation.Repeatable;

@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  String dayOfWeek() default "Mon";
  int hour() default 12;
}

@Repeatable元注解的值,是编译器生成的容器注解,用于存储重复注解。在这个例子中,容器注解类型为 Schedules, 所以 @Schedule 注解存储在 @Schedules 中。

步骤2: 声明一个容器注解类型

容器注解类型必须包含一个value属性,并且是数组。 数组的类型必须为可重复的注解类型。声明 Schedules 容器注解类型如下:

public @interface Schedules {
    Schedule[] value();
}

查找注解

在反射接口中有许多方法用于查找注解。有个返回单个注解的方法,如 AnnotatedElement.getAnnotation(Class), 如果有一个需要查找的类型存在,就返回单个注解。 如果有多个注解出现,你可以通过容器注解类型间接获取它们。 在这种方式下,老的代码也可以正常工作。 在Java SE 8中也存在其他的方法可以一次返回多个注解,如 AnnotatedElement.getAnnotationsByType(Class).更详细的说明见AnnotatedElement.

设计考虑

当设计一个注解类型时,你必须考虑那个类型的基准注解。 现在可以使用一个注解0次,一次,或者,如果一个注解被标注为 @Repeatable,多次。 你可以使用 @Target 元注解限制该注解类型的使用。比如,你可以创建一个重复注解只能使用再方法和域变量上。 非常重要的一点,你必须尽可能的使注解在使用时使编码更灵活和更强大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值