14.1 基本注解
14.1 基本注解
注解必须使用工具来处理,工具负责提取注解里包含的元数据,工具还会根据这些元数据增加额外的功能。在系统学习新的注解语法之前,先看一下Java
提供的5个基本注解的用法:使用注解时要在其前面增加@
符号,并把该注解当成一个修饰符使用,用于修饰它支持的程序元素。
5个基本注解
5个基本的注解如下:
@Override
@Deprecated
@SuppressWarnings
@SafeVarargs
@FunctionalInterface
上面5个基本注解中的 @SafeVarargs
是Java7
新增的、@FunctionalInterface
是Java8
新增的。
这5个基本定义在java.lang包中
这5个基本的注解都定义在java.lang
包下,读者可以通过查阅它们的API
文档来了解关于它们的更多细节。
14.1.1 限定重写父类方法:@Override
@Override
就是用来指定方法覆载的,它可以强制一个子类必须覆盖父类的方法。
程序
如下程序中使用@Override
指定子类Apple
的info()
方法必须重写父类方法。
1 | public class Fruit { |
@Override可避免方法重写时 写错方法签名
编译上面程序,可能丝毫看不出程序中的@Override
有何作用,因为@Override
的作用是告诉编译器检査这个方法,保证父类要包含一个被该方法重写的方法,否则就会编译出错。@Override
主要是帮助程序员避免一些低级错误,例如把上面Apple
类中的info()
方法不小心写成了inf0
,这样的“低级错误”可能会成为后期排错时的巨大障碍
如果把Apple
类中的info()
方法误写成inf0()
,编译程序时将出现如下错误提示
1 | G:\Desktop\随书源码\疯狂Java讲义(第4版)光盘\codes\14\14.1>javac -encoding utf-8 Fruit.java |
@Override只能修饰方法
@Override
只能修饰方法,不能修饰其他程序元素。
14.1.2 Java9增强的@Deprecated
这@Deprecated
用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的类、方法时,编译器将会给出警告。
Java9增加的属性
Java9
为@Deprecated
注解增加了如下两个属性:
属性 | 描述 |
---|---|
boolean forRemoval |
指定该API 在将来是否会被删除。 |
String since |
指定该API 从哪个版本被标记为过时。 |
程序
如下程序指定Apple
类中的info
方法已过时,其他程序中使用Apple
类的info
法时编译器将会给出警告
1 | class Apple { |
上面程序中使用了Apple
的info()
方法,而Apple
类中定义info()
方法时使用了@Deprecated
修饰,表明该方法已过时,所以将会引起编译器警告。
@Deprecated
和@deprecated
文档标记的区别
@Deprecated
的作用与文档注释中的@deprecated
标记的作用基本相同,但它们的用法不同:@Deprecated
是JDK5
才支持的注解,无须放在文档注释语法(/**......*/
部分)中,而是直接用于修饰程序中的程序单元,如方法、类、接口等
14.1.3 抑制编译器警告:@SuppressWarnings
@SuppressWarnings
指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告。@SuppressWarnings
会一直作用于该程序元素的所有子元素,例如,使用@SuppressWarnings
修饰某个类取消显示某个编译器警告,同时又修饰该类里的某个方法取消显示另一个编译器警告,那么该方法将会同时取消显示这两个编译器警告。
程序
1 | import java.util.*; |
程序中的粗体字代码使用@SuppressWarnings
来关闭SuppressWarningsTest
类里的所有编译器警告,编译上面程序时将不会看到任何编译器警告。如果删除程序中的@SuppressWarnings(value = "unchecked")
注解,将会在程序的①处看到编译器警告:
1 | G:\Desktop\随书源码\疯狂Java讲义(第4版)光盘\codes\14\14.1>javac -encoding utf-8 SuppressWarningsTest.java |
正如从程序中粗体字代码所看到的,当使用@SuppressWarnings
注解来关闭编译器警告时,一定要在括号里使用name=Value
的形式为该注解的成员变量设置值。关于如何为注解添加成员变量请看下节介绍。
14.1.4 “堆污染”警告与Java9增强的@SafeVarargs
前面介绍泛型擦除时,介绍了如下代码可能导致运行时异常。
堆污染
Java
把引发这种错误的原因称为“堆污染”(HeapPollution
),当把一个不带泛型的对象赋给一个带泛型的变量时,往往就会发生这种“堆污染”,如上①号粗体字代码所示。
对于形参个数可变的方法,该形参的类型又是泛型,这将更容易导致“堆污染”。
程序
例如如下工具类。
1 | import java.util.*; |
上面程序中的代码:
1 | List[] listArray = listStrArray; |
已经发生了“堆污染”。由于该方法有个形参是List<String>
类型,个数可变的形参相当于数组,但Java
又不支持泛型数组,因此程序只能把List<String>
当成List[]
处理,这里就发生了“堆污染”。
在Java6
以及更早的版本中,Java
编译器认为faultyMethod()
方法完全没有问题,既不会提示错误也没有提示警告。
等到使用该方法时,例如如下程序。
1 | import java.util.*; |
编译该程序将会在①号代码处引发一个unchecked
警告。这ˆunchecked
警告岀现得比较“突兀”定义faultyMethod()
方法时没有任何警告,调用该方法时却引发了一个“警告”
上面程序故意利用了“堆污染”,因此程序运行时也会在①号代码处引发ClassCastException
异常。
从Java7
开始,Java
编译器将会进行更严格的检查,Java
编译器在编译ErrorUtils
时就会发出一个如下所示的警告
1 | G:\Desktop\随书源码\疯狂Java讲义(第4版)光盘\codes\14\14.1>javac -encoding utf-8 ErrorUtils.java |
由此可见,Java7
会在定义该方法时就发出“堆污染”警告,这样保证开发者“更早”地注意到程序中可能存在的“漏洞”
抑制堆污染警告的三种方法
但在有些时候,开发者不希望看到这个警告,则可以使用如下三种方式来“抑制”这个警告。
- 使用
@SafeVarargs
修饰引发该警告的方法或构造器。Java9
增强了该注解,允许使用该注解修饰私有实例方法。 - 使用
@SuppressWarnings("unchecked")
修饰。 - 编译时使用
-Xlint:unchecked
选项。
很明显,第三种方式一般比较少用,通常可以选择第一种或第二种方式,尤其是使用@SafeVarargs
修饰引发该警告的方法或构造器,它是Java7
专门为抑制“堆污染”警告提供的。
如果程序使用@SafeVarargs
修饰ErrorUtils
类中的faultyMethod()
方法,则编译上面两个程序时都不会发出任何警告
14.1.5 Java8的函数式接口与@FunctionalInterface
前面已经提到,Java8
规定:只有一个抽象方法的接口就是函数式接口(注意:函数式接口可以包含多个默认方法或多个static
方法)@FunctionalInterface
就是用来指定某个接口必须是函数式接口。
函数式接口就是为Java8
的Lambda
表达式准备的,Java8
允许使用Lambda
表达式创建函数式接口的实例,因此Java8
专门增加了@ .Functionalnterface
程序
例如,如下程序使用@FunctionalInterface
修饰了函数式接口
1 |
|
编译上面程序,可能丝毫看不出程序中的@FunctionalInterface
有何作用,因为@FunctionalInterface
只是告诉编译器检査这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错。@FunctionalInterface
主要是帮助程序员避免一些低级错误,例如,在上面的FunInterface
接口中再增加一个抽象方法abc()
,编译程序时将出现如下错误提示:
@FunctionalInterface
只能修饰接口,不能修饰其他程序元素。
1 | G:\Desktop\随书源码\疯狂Java讲义(第4版)光盘\codes\14\14.1>javac -encoding utf-8 FunInterface.java |