(一)什么是Annotation
Annotation,其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充的信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。
Annotation提供了一种为程序元素设置元数据的方法,从某些方面来看,Annotation就像修饰符一样,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在Annotation的“name=value"对中。
Annotation是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注解里的元数据。如果希望让程序中的Annotation在运行时起一定的作用,只有通过某种配套的工具对Annotation中的信息进行访问和处理,访问和处理Annotation的工具统称APT(Annotation Processing Tool).
(二) Annotation 作用
1. 标记,用于告诉编译器一些信息
2. 编译时动态处理,如动态生成代码
3. 运行时动态处理,如得到注解信息
(三)Annotation 例子
va7新增的,@FunctionalInterface是Java8新增的。这5个基本的Annotation都定义在java.lang包下。
1.限定重写父类方法:@Override
@Override就是用来指定方法重写的,它可以强制一个子类必须复写父类的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class Fruit { public void info() { System.out.println("水果的info方法..."); } } class Apple extends Fruit { // 使用@Override指定下面方法必须重写父类方法 @Override public void inf0() { System.out.println("苹果重写水果的info方法..."); } } |
PS:@Override主要帮助程序员避免一些低级错误,例如把上面Apple类中的info方法不小心写成了inf0,这样的"低级错误",可能会成为后期排错时的巨大障碍。
2.标示已过时:@Deprecated
@Deprecated用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的类、方法是,编译器将会给出警告。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Apple { // 定义info方法已过时 @Deprecated public void info() { System.out.println("Apple的info方法"); } } public class DeprecatedTest { public static void main(String[] args) { // 下面使用info方法时将会被编译器警告 new Apple().info(); } } |
3.抑制编译器警告:@SuppressWarnings
@SuppressWarnings指示被该Annotation修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告。
1 2 3 4 5 6 7 8 9 |
// 关闭整个类里的编译器警告 @SuppressWarnings(value="unchecked") public class SuppressWarningsTest { public static void main(String[] args) { List<string> myList = new ArrayList(); // ① } }</string> |
4.Java7的“堆污染”警告与@SafeVarargs
1 2 3 4 5 6 7 8 9 10 11 |
public class Demo { public static void main(String[] args){ List list = new ArrayList<integer>(); list.add(20);//添加元素时引发unchecked异常 //下面代码引起"未经检查的转换"的警告,编译、运行时完全正常 List<string> ls = list;//① //但只要访问ls里的元素,如下面代码就会引起运行时异常 System.out.println(ls.get(0)); } } </string></integer> |
Java把引发这种错误的原因称为"堆污染"(Heap pollution),当把一个不带泛型的对象赋给一个带泛型的变量时候,往往会发生这种"堆污染",如上①号粗体字代码所示。
对于形参个数可变的方法,该形参的类型又是泛型,这将更容易导致"堆污染".例如下面工具类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class ErrorUtils { @SafeVarargs public static void faultyMethod(List<string>... listStrArray) { // Java语言不允许创建泛型数组,因此listArray只能被当成List[]处理 // 此时相当于把List<string>赋给了List,已经发生了“擦除” List[] listArray = listStrArray; List<integer> myList = new ArrayList<integer>(); myList.add(new Random().nextInt(100)); // 把listArray的第一个元素赋为myList listArray[0] = myList; String s = listStrArray[0].get(0); } }</integer></integer></string></string> |
上面程序中的粗体字代码已经发生了"堆污染"。由于该方法有一个形参是List...类型,个数可变的形参相当于数组,但Java又不支持泛型数组,因此程序中只能把List...当成List[]处理,这里就发生了"堆污染"。
在Java6以及更早的版本中,Java编译器认为faultyMethod()方法完全没有问题,既不会提示错误,也没有提示警告。
1 2 3 4 5 6 7 |
public class ErrorUtilsTest { public static void main(String[] args) { ErrorUtils.faultyMethod(Arrays.asList("Hello!"), Arrays.asList("World!")); } } |
编译该程序将会在①号代码处引发一个unchecked警告。这个unchecked警告出现得比较"突兀":定义faultyMethod()方法时没有任何警告,调用该方法时却引发了一个"警告".
从Java7开始,Java编译器将会进行更严格的检查,Java编译器在编译ErrorUtils时就会发出一个警告。
由此可见,Java7会在定义该方法时就发出"堆污染"警告,这样保证开发者"更早"地注意到程序中可能存在的"漏洞"。但在有些时候,开发者不希望看到这个警告,则可以使用如下三种方式来"抑制"这个警告。
①:使用@SafeVarargs修饰引发该警告的方法或构造器
②:使用@SuppressWarnings("unchecked")修饰
③:编译时使用-Xlint:varargs选项
5.Java8的函数式接口与@FunctionalInterface
如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法),该接口就是函数式接口。函数式接口就是为Java8的Lambda表达式准备的,Java8允许使用Lambda表达式创建函数式接口的实例,因此Java8专门增加了@FunctionalInterface.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@FunctionalInterface public interface FunInterface { static void foo() { System.out.println("foo类方法"); } default void bar() { System.out.println("bar默认方法"); } void test(); // 只定义一个抽象方法 } |
PS:@FunctionalInterface只是告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错。@FunctionalInterface只能修饰接口,不能修饰其他程序元素。
转自 https://www.2cto.com/kf/201803/733649.html