《疯狂java讲义》第14章 注解(Annotation)

第14章 注解(Annotation)

  • 注解,其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,程序开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
  • 注解是一个接口,程序可以通过反射来获取指定程序元素的java.lang.annotation.
    Annotation对象,然后通过该对象来取得注解里的元数据。

14.1 基本注解

  • 使用注解时要在其前面增加@符号,并把该注解当成一个修饰符使用,用于修饰它支持的程序元素。

@Override
@Deprecated
@SuppressWarnings
@SafeVarargs
@FunctionalInterface

14.1.1 限定重写父类方法:@Override

  • @Override 就是用来指定方法覆载的,它可以强制一个子类必须覆盖父类的方法
    (编译程序,看不出注解@Override的作用,它告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则编译出错)

14.1.2 java 9 增强的@Deprecated

  • @Deprecated用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的类、方法时,编译器将会给出警告。
//since属性指定从哪个版本开始,forRmoval指定该API将来会被删除
@Deprecated(since="9", forRemoval=true)

14.1.3 抑制编译器警告:@SuppressWarnings

  • @SuppressWarnings指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告。
@SuppressWarnings(value="unchecked")

14.1.4 "堆污染"警告与java 9 增强的@SafeVarargs

  • “堆污染”(Heap pollution),当把一个不带泛型的对象赋给一个带泛型的变量时,往往就会发生这种“堆污染”。
  • 但在有些时候,开发者不希望看到这个警告,则可以使用如下三种方式来“抑制”这个警告。(一二常用)

(1)使用@SafeVarargs修饰引发该警告的方法或构造器。Java9增强了该注解,允许使用该注解修饰私有实例方法。
(2)使用@SuppressWarnings(“unchecked”)修饰。
(3)编译时使用-Xlint:varargs选项。

14.1.5 java 8 的函数式接口与@FunctionlInterface

  • @Functionallnterface就是用来指定某个接口必须是函数式接口(如果接口中只有一个抽象方法,可以包含多个默认方法或多个static方法)。
  • 函数式接口就是为Java8的Lambda表达式准备的。
  • @FunInterface只能修饰接口,不能修饰其他程序元素。

14.2 JDK的元注解

JDK除在java.lang下提供了5个基本的注解之外,还在java.lang.annotation包下提供了6个Meta注解(元注解),其中有5个元注解都用于修饰其他的注解定义。

14.2.1 使用@Retention

  • @Retention 只能用于修饰注解定义,用于指定被修饰的注解可以保留多长时间,包含一个RetentionPolicy 类型的value成员变量,value成员变量的值只能是如下三个。

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

//定义下面的注解保留到运行时
@Retention(value = RetentionPolicy.RUNTIME)

//定义下面的主机将被编译器直接丢弃
@Retention(RententionPolicy.SOURCE)

14.2.2 使用@Target

  • @Target 也只能修饰注解定义,指定被修饰的注解能用于修饰哪些程序单元(目标)。也包含一个名为value的成员变量

ElementType.ANNOTATION TYPE:指定该策略的注解只能修饰注解
ElementType.CONSTRUCTOR:指定该策略的注解只能修饰构造器
ElementType.FIELD:指定该策略的注解只能修饰成员变量
ElementType.LOCAL VARIABLE:指定该策略的注解只能修饰局部变量
ElementType.METHOD:指定该策略的注解只能修饰方法定义。>ElementType.PACKAGE:指定该策略的注解只能修饰包定义。>ElementType.PARAMETER:指定该策略的注解可以修饰参数
ElementType.TYPE:指定该策略的注解可以修饰类、接口(包括注解类型)或枚举定义

//指定注解只能修饰成员变量
@Target(ElementType.FIELD)

14.2.3 使用@Documented

  • @Documented用于指定被该元注解修饰的注解类将被 javadoc工具提取成文档,使用了@Documented修饰,则所有使用该注解修饰的程序元素的API文档中将会包含该注解说明。

14.2.4 使用@Inherited

  • @Inherited元注解指定被它修饰的注解将具有继承性。如果某个类使用了@Xxx注解(定义该注解时使用了@Inherited修饰)修饰,则其子类将自动被@Xxx修饰。

14.3 自定义注解

14.3.1 定义注解

  • 使用@interface关键字(有点像接口定义),可以修饰类、接口、方法等。
  • 还可以带成员变量,以无形参的方法形式来声明。
public @interface MyTag{
	//定义带成员变量的注解
	//成员变量以无形参的方法形式来声明。
	String name();
	int age();
}
  • 注解里面定义了成员变量之后,为其成员变量指定值。
public class Test{
	//带成员变量的注解,需为其成员变量赋值
	@MyTag(name = "xx", age = 6)
	public void info(){...}
}
  • 也可以定义注解的成员变量时指定初始值(默认值),使用default关键字。
public @interface MyTag{
	//定义带成员变量的注解
	//使用default为成员变量指定初始值
	String name() default "yeeku";
	int age() default 22;
}
  • 为注解的成员变量指定了默认值后,使用注解时可以不用为这些成员变量指定值;当然也可以指定值,指定值之后,默认值不会起作用。
  • 根据注解是否可以包含成员变量,可以把注解分为如下两类:

标记注解没有定义成员变量的注解类型被称为标记。这种注解仅利用自身的存在与否来提供信息,如前面介绍的@Override、@Test 等注解。
元数据注解包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。

14.3.2 提取注解信息

  • java.lang.reflect 包下主要包含一些实现反射功能的工具类,从Java5开始,java.lang.reflect包所提供的反射API增加了读取运行时注解的能力。只有当定义注解时使用了@Retention(RetentionPolicy.RUNTIME)修饰,该注解才会在运行时可见,JVM才会在装载*.class文件时读取保存在class文件中的注解信息。
  • AnnotatedElement 接口是所有程序元素(如Class、Method、Constructor等)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象(如Class、Method、Constructor等)之后,程序就可以调用该对象的方法来访问注解信息(感觉不重要)。

<A extends Annotation>A getAnnotation(Class<A>annotationClass):返回该程序元素上存在的、指定类型的注解,如果该类型的注解不存在,则返回null。
<A extends Annotation>A getDeclaredAnnotation(Class<A>annotationClass):这是Java8新增的方法,该方法尝试获取直接修饰该程序元素、指定类型的注解。如果该类型的注解不存在,则返
回null。
Annotation[]getAnnotations():返回该程序元素上存在的所有注解。
Annotation[]getDeclaredAnnotations():返回直接修饰该程序元素的所有注解。
boolean isAnnotationPresent(Class<?extends Annotation>annotationClass):判断该程序元素上是否存在指定类型的注解,如果存在则返回true,否则返回false。
<A extends Annotation>A[]getAnnotationsByType(Class<A>annotationClass):该方法的功能与前面介绍的getAnnotation)方法基本相似。但由于Java8增加了重复注解功能,因此需要使用该方法获取修饰该程序元素、指定类型的多个注解。
<A extends Annotation>A[]getDeclaredAnnotationsBy Type(Class<A>annotationClass):该方法的功能与前面介绍的getDeclaredAnnotations)方法基本相似。但由于Java8增加了重复注解功能,因此需要使用该方法获取直接修饰该程序元素、指定类型的多个注解。

//获取Test类的info方法的所有注解
Annotation[] aArray = Class.forName("Test").getMethod("info").getAnnotations();
//遍历所有注解
for(Annotation an : aArray){
	System.out.println(an);
}
  • 注解,是对源代码增加的一些特殊标记,这些特殊标记可通过反射获取,当程序获取这些特殊标记后,程序可以做出相应的处理(当然也可以完全忽略这些注解)。

14.3.4 java 8 新增的重复注解

  • Java8允许使用多个相同类型的注解来修饰同一个类。
@Result(name="failure", location="failed.jsp")
@Result(name="success", location="succ.jsp")
public Acton FooAction{...}
  • 重复注解,需要使用@Repeatable修饰该注解,使用@Repeatable 时必须为value成员变量指定值,该成员变量的值应该是一个“容器”注解——该“容器”注解可包含多个@FkTag,因此还需要定义如下的“容器”注解。
//指定该注解信息会保留到运行时
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FkTags{
	//定义value成员变量,该成员变量可接受多个QFkTag 注解
	FkTag[] value();
}

//接下来程序可在定义@FkTag注解时添加如下修饰代码:
@Repeatable(FkTags.class)

//传统代码注解
@FkTags({@FkTag(age=5),
	@FkTag(name="疯狂Java",age=9)})
//重复注解
@FkTag(age=5)
@FkTag(name="疯狂Java",age=9)
  • 第二种用法只是一个简化写法,系统依然将两个@FkTag注解作为@FkTags的value成员变量的数组元素。如下程序演示了重复注解的本质。

14.3.5 java 8 新增的类型注解

  • Java 8为ElementType枚举增加了TYPE_PARAMETER和TYPE_USE两个枚举值。这样就允许定义注解时使用@Target(ElementType.TYPE_USE)修饰,这种注解就是类型注解。
  • 类型注解可以在任何使用类型的地方使用

创建对象
类型转换
使用implements实现接口
使用throws 声明抛出异常

// 定义类型注解
@Target(ElementType.TYPE_USE)
public @interface NotNull {}

14.4 编译时处理注解

  • APT(Annotation Processing Tool)是一种注解处理工具,它对源代码文件进行检测,并找出源文件所包含的注解信息,然后针对注解信息进行额外的处理。
  • 使用APT可以代替传统的对代码信息和附属文件的维护工作。因为APT可以在编译程序源代码的同时生成一些附属文件(比如源文件、类文件、程序发布描述文件等),这些附属文件的内容也都与源代码相关。
  • 通过注解可以在Java源文件中放置一些注解,然后使用APT工具就可以根据该注解生成另一份XML文件,这就是注解的作用。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值