Annotation注释 (一)——java学习笔记

来源:互联网 发布:淘宝商品api 编辑:程序博客网 时间:2024/06/06 13:57

以下内容大部分来自于疯狂java一书和廖雪峰老师的教程,如需转载请注明这两个出处,本文仅供自身学习,查缺补漏之用。


初识注解

从JDK5开始,java正价了对元数据(metadata)的支持,也就是annotation(即朱姐,也被翻译为注释),这种annotation与之前所说的注释有一定的区别。本章所介绍的annotation,其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用朱姐,程序开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或进行部署。

注解是什么??

  • 注解(Annotation)是放在Java源码的类、方法、字段、参数前的一种标签。

注解的作用:

  • 注解本身对代码逻辑没有任何影响
  • 如何使用注解由工具决定。

编译器就是一种工具

编译器可以使用的注解:

  • @Override:让编译器检查该方法是否正确实现了覆写
  • @Deprecated:告诉编译器该方法已经被标记为“作废”,在其他地方引用将会出现编译警告
  • @SuppressWarnings如果编译器发现在以下的代码中出现了警告,那么请编译器忽略这些警告。

annotation提供了一种为程序元素设置元数据的方法,从某些方面来看,annotation就像修饰符一样,可以终于修饰包、类、构造器、方法、成员变量、参数、局部变量的生命,这些信息被储存在annotation的“name = value”对中。

annotation是一个接口,程序可以通过反射来获取指定程序元素的annotation对象,然后通过annotation对象来去的注解里的元数据,大家需要注意本文中使用annotation的地方,有的annotation指的是java.lang.annotaion接口,有的指注解本身

annotation能被用来作为程序员苏(类、方法、成员变量等)设置元数据,值得提出的是,annotation不影响程序代码的执行,无论增加、删除annotation,代码都始终如一的执行,如果希望让程序中的annotation在运行时起一定的作用,,只有通过某种配套的工具对annotation中的信息进行访问和处理,访问和处理annotation的工具统称APT(Annotation Processing Tool),不是ATP。=。=

注解可以定义配置参数:

  • 配置参数由注解类型定义
  • 配置参数可以包括:
    • 所有基本类型
    • String
    • 枚举类型
    • 数组
  • 配置参数必须是常量

关于配置参数的部分注意事项:

  • 缺少某个配置参数将使用默认值(如何设置默认值下文会有详细说明)
  • 如果只写常量,相当于省略了value参数
  • 如果只写注解,相当于全部使用默认值

基本annotation

Annotation必须使用工具来处理,工具负责提取annotation里包含的元数据,工具还会根据这些元数据增加额外的功能。在系统学习心得annotation语法之前,我们先看一下java提供的5个基本annotation的用法——使用annotation时要在其前面加@符号,并把该annotation当成一个修饰符使用,用于修饰它支持的程序元素。

5个基本的annotation如下:

  • @Override
  • @Dprecated
  • @SuppressWarnings
  • @SafeVarargs
  • @FunctionalInterface

上面5个基本annotation中的@SafeVarargs是java 7 新增的、 @FunctionalInterface是java 8 新增的,这5个基本的annotation都定义在java.lang包下,我们可以通过查看他们的api文档来了解他们的具体细节。下面进行简单的介绍。

限定重写父类方法:@Override

这个可能是我们学习java的时候最先接触的注解,当然那时我们可能不清楚它是什么。
@Override就是用来指定方法覆载的,它可以强制一个子类必须覆盖父类的方法。如下程序中使用@Override指定子类Apple的info()方法必须重写父类方法。

这里写图片描述

它的主要作用就是帮助程序员避免一些低级错误,例如把上面apple类中的info方法不小心写成了inf0。这一点有时候很有必要。

另外还有一点要提出的就是 apple的红线是因为此工程中还有同名的类名,和本程序的对错无关,所以大家忽略掉这些就可以了。

标记已过世:@Deprecated

@Deprecated用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的类、方法时,编译器将会发出警告。如下程序制定apple类中的info()方法已过世,其他程序中使用apple类的info()方法时编译器将会发出警告。

这里写图片描述

注意:
@Deprecated的作用与文档注释中的@deprecated标记的作用基本相同,但它们的用法不同,前者是JDK5 才支持的注解,无须放在文档注释语法(/* …*/部分)中,而是直接用于修饰程序中的程序单元,如方法、类、接口等。

抑制编译器警告:@SuppressWarnings

@SuppressWarnings指示该被Annotation修饰的程序员苏(以及该程序元素中的所有子元素)取消显示指定的编译器警告。@SuppressWarnings会一直作用域该程序元素的所有子元素,例如,使用@SuppressWarnings修饰某个类取消显示某个编译器警告,同时又修饰该类里的某个方法取消显示另一个编译警告,那么方法将会同时取消显示这两个编译器警告。

在通常情况下,如果程序中使用没有泛型限制的集合将会引起编译器警告,为了避免这种编译器警告,可以使用@SuppressWarnings修饰,下面程序就取消了没有使用泛型的编译器警告。

这里写图片描述

①如果删除注释,这个地方就会有编译器的警告。这个成员变量的设置,可以参考下一篇注释的文章。

java 7 的“堆污染”警告与@SafeVarargs

List list = new ArrayList<Integer>();list.add(20);//添加元素时引发unchecked异常//下面代码引起“未经检查的转换”的警告,编译、运行时完全正常List<String> ls = list; //①//但只要访问ls里的元素,如下面代码就会引起运行时异常System.out.println(ls.get(0));

Java把引发这种错误的原因成为“堆污染”,当吧一个不带泛型的对象赋给一个带泛型的变量时,往往就会发生这种堆污染,如①所表示

对于形参个数可变的方法,该形参的类型又是泛型,这将更容易导致“堆污染”,如下

这里写图片描述

List ls = list;已经发生了堆污染,由于该方法有个形参是List类型,个数可变的形参相当于数组,但Java又不支持泛型数组,因此程序只能把list当成List[]处理,这就发生了堆污染

但是这里有个值得注意的问题 ,就是java6和更早的版本,Java搬一起认为faultyMethod()方法完全没有问题,既不会提示错误,也没有提示警告

这里写图片描述

编译该程序将会在①处引发一个unchecked警告。这个警告出现的比较突兀(书中就是这么写的),定义faultyMethod()方法时没有任何警告,调用该方法时去引发了一个警告。

从Java7开始就会在编译ErrorUtils时就会发出一个警告,这样保证开发者“更早”地注意到程序中可能存在的“漏洞”

但在有些时候,开发者不希望看到这个警告,则可以使用如下三种方式来“抑制这个警告”

  • 使用@SafeVarargs修饰引发该警告的方法或构造器
  • 使用@SuppressWarnings(“unchecked”)修饰
  • 编译时使用-Xlint:varargs选项

前两种明显用的比较多,如果程序使用@SafeVarags修饰ErrorUtils类中的faultyMethod()方法,则编译上面两个程序时都不会发出任何警告。

Java8的函数式接口与@Funtionallnterface

Java8 规定,如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法),该接口就是函数式接口。@Funtionallnterface就是用来指定某个接口必须是函数是借口。例如,如下程序使用@Funtionallnterface修饰了函数式接口

函数式接口就是为了Java8的lambda表达式准备的,Java8允许使用lambda表达式创建函数式接口的实例,因此Java8专门增加了@Funtionallnterface

这里写图片描述

@Funtionallnterface告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错。@Funtionallnterface主要是帮助程序员避免一些低级错误,例如,在上面的funinterface接口中再增加一个抽象方法abc()编译程序时将出现如下错误提示

这里写图片描述

@Funtionallnterfac只能修饰接口,不能修饰其他程序元素。