Java 注解

一、 简介

注解(Annotation),也叫元数据,是一种代码级别的说明。它是JDK1.5引入的一个新特性,与类、接口、枚举是在同一个层次。

注解可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明和注释。

注解都不会直接影响到程序的语义,也不会改变程序的编译方式,只是作为注解(标识)存在,相当于给它们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。

如果没有外部解析工具对其加以解析和处理,注解本身是不会对Java的源代码或class文件等产生任何影响,也不会对它们的执行产生任何影响。

注:在Java8中注解可以应用在任何类型上使用。

作用分类:

  • 编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
  • 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
  • 编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【例如Override】

注解是以 @注解名 在代码中存在的,还可以添加一些参数值,如:@SuppressWarnings(value="unchecked")

注解的定义:

  1. 定义新的注解(Annotation)类型时使用@interface关键字(在原有interface关键字前增加@符号)。
  2. 注解需要标明注解的生命周期,注解的修饰目标等信息,这些信息是通过元注解实现。

注解分类:

  • 标记注解:没有成员定义的注解类型被称为标记注解。
  • 单值注解:顾名思义注解仅包含一个成员变量,建议其取名为value。使用时可以直接填写成员变量值,而不必书写(成员变量名=)。
  • 完整注解:相对而言就是成员变量不止一个的注解。

二、 元注解

通俗来说是用来定义注解的注解,元注解是Java API提供的。元注解位于java.lang.annotation包中,在JDK5中定义了4个元注解,而在JDK8中又添加了两个元注解,并引入了类型注解。

2.1 @Inherited

作用:允许子类继承父类中的注解

如果某个类使用了@xxx注解(定义该注解时使用了@Inherited修饰)修饰,则其子类将自动被@xxx修饰 。

2.2 @Documented

作用:将此注解包含在Javadoc中,它代表着此注解会被Javadoc工具提取成文档

2.3 @Retention

作用:用于指定注解可以保留多长时间(生命周期)

@Retention包含一个名为value的成员变量,该value成员变量是RetentionPolicy枚举类型。使用@Retention时,必须为其value指定值,但是因为只有一个值,所以可以忽略成员名。

value成员变量的值只能是如下3个:

  • RetentionPolicy.SOURCE:注解只保留在源代码中,编译器编译时,直接丢弃这种注解,不记录在.class文件中。
  • RetentionPolicy.CLASS:编译器把注解记录在class文件中。当运行Java程序时,JVM中不可获取该注解信息。(默认值)
  • RetentionPolicy.RUNTIME:编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息,程序可以通过反射获取该注解的信息。

2.4 @Target

作用:指定注解用于修饰哪些程序元素

@Target也包含一个名为value的成员变量,该value成员变量类型为ElementType[]ElementType为枚举类型,因为是枚举数组,所以可以同时写入多个值。

value成员变量值有如下几个:

  • ElementType.ANNOTATION_TYPE can be applied to an annotation type.( 能修饰注解)
  • ElementType.CONSTRUCTOR can be applied to a constructor. (能修饰构造器)
  • ElementType.FIELD can be applied to a field or property. (能修饰成员变量)
  • ElementType.LOCAL_VARIABLE can be applied to a local variable. (能修饰局部变量)
  • ElementType.METHOD can be applied to a method-level annotation. (能修饰方法)
  • ElementType.PACKAGE can be applied to a package declaration. (能修饰包)
  • ElementType.PARAMETER can be applied to the parameters of a method. (能修饰参数)
  • ElementType.TYPE can be applied to any element of a class. (能修饰类的任意元素)
  • JDK1.8新增
    • ElementType.TYPE_PARAMETER:Type parameter declaration.(修饰类型变量的声明)
    • ElementType.TYPE_USE:Use of a type.(修饰类型)

注:使用@Target(ElementType.TYPE_USE)修饰注解定义,被修饰的这种注解被称为类型注解,可以用在任何使用到类型的地方。

2.5 @Repeatable

作用:允许在同一程序元素(类,属性,或方法)前多次使用同一个类型注解

在Java8以前,同一个程序元素前最多只能有一个相同类型的注解;如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。

JDK8之前的做法

public @interface Authority {  
    String role();  
}  
public @interface Authorities {   
    // @Authorities注解作为可以存储多个@Authority注解的容器  
    Authority[] value();  
}  
public class RepeatAnnotationUseOldVersion {  
    @Authorities({ @Authority(role = "Admin"), @Authority(role = "Manager") })  
    public void doSomeThing() {  
    }  
}  

JDK8中的做法

import java.lang.annotation.Repeatable;  

@Repeatable(Authorities.class)  
public @interface Authority {  
    String role();  
}  
public @interface Authorities {   
    Authority[] value();  
}  
public class RepeatAnnotationUseOldVersion {  
    @Authority(role = "Admin")  
    @Authority(role = "Manager")  
    public void doSomeThing() {  
    }  
}  

不同的地方是,创建重复注解Authority时,加上@Repeatable,指向存储注解Authorities,在使用时候,直接可以重复使用Authority注解。从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点。但是,仍然需要定义容器注解。

两种方法获得的效果相同。重复注解知识一种简化写法,这种简化写法是一种假象:多个重复注解其实会被作为“容器”注解的value成员的数组元素处理。

2.6 @Native

作用:指定字段是一个常量,其值引用native code。

三、 JavaSE内置注解

预定义注解类型定义在java.lang包中,其包含 @Deprecated , @Override , 和 @SuppressWarnings (注:JDK1.7 新增 @SafeVarargs ,JDK1.8 新增 @FunctionalInterface)

3.1 @Deprecated

@Deprecated注解表示被标记的元素是被废弃的(deprecated),不应该再被使用。

  • 当代码使用被 @Deprecated 注解的方法,属性或者类的时候,编译器便会给出警告(warning)。
  • 当一个元素被废弃了,它将会在Javadoc文档里打上@deprecated标记(@deprecated tag)。

注意:Javadoc注释和注解都使用@开头,并不是巧合,他们在概念上是相关联的。当然,我们也会看到Javadoc tag是以小写字母d开头,而注解是以大写字母D开头。(Javadoc的@deprecated注解@Deprecated 的功效是一样的。)

3.2 @Override

@Override注解告诉编译器被注解注释的元素是重写(override)父类的。

虽然在重写方法时不需要使用此注解,但它有助于防止错误。如果一个方法被@Override标记,但未能正确重写它的父类方法,编译器便会报错。

3.3 @SuppressWarnings

@suppresswarning注解告诉编译器抑制特定的警告,否则就会产生警告。

每个编译期警告都属于一个类型。Java语言规范列出两类:deprecationunchecked。当接口是使用泛型前的遗留代码时,就会产生unchecked的警告。为了抑制多种类型的警告,可以使用下列语法:

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

3.4 @SafeVarargs

@SafeVarargs注解应用于一个方法或者构造函数,表示代码不会在可变参数(varargs)上进行不安全的操作。当@SafeVarargs注解被使用时,与可变参数相关(varargs)的unchecked警告都会被抑制。示例:

//可变长度的方法参数的实际值是通过数组来传递的,二数组中存储的是不可具体化的泛型类对象,自身存在类型安全问题。因此编译器会给出相应的警告消息。
@SafeVarargs
public static <T> T useVarargs(T... args) {
    return args.length > 0 ? args[0] : null;
}

@Test
public void testA() {
    System.out.println(useVarargs(new ArrayList<String>()));
}

3.5 @FunctionalInterface

@FunctionalInterface注解是Java 8新加入的一个注解,用于接口定义上表示其为函数式接口。主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。

注意:加不加@FunctionalInterface对于接口是不是函数式接口没有影响,该注解只是提醒编译器去检查该接口是否仅包含一个抽象方法 。

// 定义一个函数式接口
@FunctionalInterface
interface GreetingService{
   void sayMessage(String message);
}
// 那么就可以使用Lambda表达式来表示该接口的一个实现
GreetingService greetService1 = message -> System.out.println("Hello " + message);

四、 类型注解

在Java8以前,注解只能用在各种程序元素(定义类、定义接口、定义方法、定义成员变量)上,从Java8开始,类型注解可以用在任何使用到类型的地方。

类型注解是为了提高Java程序的强类型检查功能而诞生的。Java SE 8 没有提供一个类型检查框架(type checking framework),但却允许你自己写(或者下载)一个类型检查框架作为Java编译器的插件使用。

恰当的使用类型注解和类型检查插件,我们可以写出更健壮和不易出错的代码

在很多情况下,我们都不用自己写类型检查模块。很多第三方都已经为我们写好了这些。例如,你可以使用华盛顿大学(University of Washington)的the Checker Framework

示例程序:

<1> 定义

import java.lang.annotation.*;  

@Target(ElementType.TYPE_USE)  
public @interface NotNull {  
}

<2> 使用

import java.io.FileNotFoundException;  
import java.io.Serializable;  
import java.util.List;  
import javax.swing.JFrame;  

//定义类时使用  
@NotNull  
public class TypeAnnotationTest implements @NotNull Serializable // 在implements时使用  
{  
    private static final long serialVersionUID = 5280557188557866484L;  

    // 在方法形参中使用  
    public static void main(@NotNull String[] args) throws @NotNull FileNotFoundException // 在throws时使用  
    {  
        Object obj = "fkjava.org";  
        // 使用强制类型转换时使用  
        String str = (@NotNull String) obj;  
        // 创建对象时使用  
        Object win = new @NotNull JFrame("疯狂软件");  
    }  

    // 泛型中使用  
    public void foo(List<@NotNull String> info) {  
    }  
}  

需要指出的是,上面程序虽然使用了大量@NotNull注解,但是这些注解暂时不会起任何作用,原因是没有为这些注解提供处理工具,Java8本身并没有提供,要想这些注解发挥作用,需要开发者自己实现,或者使用第三方提供的工具。

五、Common Annotations

Common annotations原本是Java EE 5.0(JSR 244)规范的一部分,现在SUN将其一部分放到了Java SE 6.0中。随着Annotation元数据功能(JSR 175)加入到Java SE 5.0里面,很多Java 技术(比如EJB,Web Services)都会用Annotation代替XML文件来配置运行参数(或者说是支持声明式编程,如EJB的声明式事务), 如果这些技术都单独定义了自己的Annotations,显然有点重复了,所以为其他相关的Java技术定义一套公共的Annotation是有价值的,可以避免重复建设的同时,也保证Java SE和Java EE 各种技术的一致性。

JSR的管理机制还没有弄明白,但是在Java EE 8的时候是Common Annotations for the Java Platform 1.3(JSR 250)。

下面列举出Common Annotations 1.0里面的10个Annotations :

AnnotationRetentionTargetDescription
GeneratedSourceANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE用于标注生成的源代码
ResourceRuntimeTYPE, METHOD, FIELD用于标注所依赖的资源,容器据此注入外部资源依赖,有基于字段的注入和基于setter方法的注入两种方式
ResourcesRuntimeTYPE同时标注多个外部依赖,容器会把所有这些外部依赖注入
PostConstructRuntimeMETHOD标注当容器注入所有依赖之后运行的方法,用来进行依赖注入后的初始化工作,只有一个方法可以标注为PostConstruct
PreDestroyRuntimeMETHOD当对象实例将要被从容器当中删掉之前,要执行的回调方法要标注为PreDestroy
RunAsRuntimeTYPE用于标注用什么安全角色来执行被标注类的方法,这个安全角色必须和Container 的Security角色一致的
RolesAllowedRuntimeTYPE, METHOD用于标注允许执行被标注类或方法的安全角色,这个安全角色必须和Container 的Security角色一致的
PermitAllRuntimeTYPE, METHOD允许所有角色执行被标注的类或方法
DenyAllRuntimeTYPE, METHOD不允许任何角色执行被标注的类或方法,表明该类或方法不能在Java EE容器里面运行
DeclareRolesRuntimeTYPE用来定义可以被应用程序检验的安全角色,通常用isUserInRole来检验安全角色

注意:

  1. RolesAllowed,PermitAll,DenyAll不能同时应用到一个类或方法上
  2. 标注在方法上的RolesAllowed,PermitAll,DenyAll会覆盖标注在类上的RolesAllowed,PermitAll,DenyAll
  3. RunAs,RolesAllowed,PermitAll,DenyAll和DeclareRoles还没有加到Java SE 6.0上来
  4. 处理以上Annotations的工作是由Java EE容器来做,Java SE 6.0只是包含了上面表格的前五种Annotations的定义类,并没有包含处理这些Annotations的引擎,这个工作可以由Pluggable Annotation Processing API(JSR 269)来做

参考资料:

赞赏码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值