Annotation 注解

(一)什么是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

原文地址:https://www.cnblogs.com/cducz/p/10546192.html