• 11.1 基本注解(Annotation)


    一、基本注解

    注解必须使用工具来处理,工具负责提取注解里包含的元数据,工具还会根据这些元数据增加额外的功能。
    先看Java提供的5个基本注解的用法——使用注解时要在前面加@符号,并把注解当成一个修饰符使用,用于修饰它支持的程序元素:
    ★@Override
    ★@Deprecated
    ★@Suppress Warnings
    ★@Safe Varargs(Java 7新增的)
    ★@FunctionalInterface(Java 8新增的)

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

    @Override用来指定方法覆载的,它可以强制一个子类必须覆盖子类的方法。如下程序指定子类Apple的info()方法必须重写父类方法。

    class Fruit
    {
        public void info()
        {
            System.out.println("水果的info方法...");
        }
    }
    public class Apple extends Fruit
    {
        @Override
        public void info()
        {
            System.out.println("苹果类重写水果的info方法...");
        }
        public static void main(String[] args)
        {
            Fruit f1=new Apple();//向上转换
            f1.info();
        }
    }
    输出结果:
    苹果类重写水果的info方法...
    

    @Override告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则就会出现编译错误。

    1.2 Java 9增强的@Deprecated

    @Deprecated用于表示某个程序元素(类、方法)已过时,当其他程序使用已过时的类、方法时,编译器会发出警告。
    Java 9为@Deprecated注解增加了两个如下属性:
    (1)forRemoval:该boolean类型的属性指定该API在将来是否会被删除。
    (2)since:该String类型的属性指定该API从哪个版本被标记为过时。

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

    1.3 抑制编译器警告:@SuppressWarnings

    @SuppressWarnings用于指示该注解修饰的程序元素(以及该程序元素中的所有元素)取消显式指定的编译器警告。@SuppressWarnings会一直作用于该程序元素的所有子元素。例如,使用@SuppressWarnings修饰某个类取消某个编译器警告,同时又修饰该类里的某个方法取消另一个编译器警告,那么该方法会同时取消这两个编译器警告。
    在通常情况下,如果程序中使用没有泛型限制的集合将会引起编译器警告,为了编码编译器警告,可以使用@SuppressWarnings修饰。下面程序取消了没有使用泛型的编译器警告:

    package section1;
    import java.util.List;
    @SuppressWarnings(value = "unchecked")
    public class SuppressWarningsTest
    {
        public static void main(String[] args)
        {
            List<String> myList =new ArrayList();
            myList.add("字符型");
            myList.add("2");
            System.out.println(myList);
        }
    }
    

    当使用@SuppressWarnings注解来瓜纳比编译器警告时,一定要在括号里使用name=value的形式为该注解的成员变量设置值。

    1.4 “堆物染”警告于Java 9增强的@SafeVaragrs

    回顾泛型的擦除

    List list=new ArrayList<Integer>();//把一个值能装Integer的集合直接赋值给List集合
    list.add(20);//添加元素时引发“为经检查的转换”的警告,编译、运行完全正确
    List<String> ls=list;//①
    //但只要访问ls里的元素,如下代码将会引起运行时异常
    System.out.println(ls.get(0));
    

    java把这种错误的原因称为“堆物染”(Heap pollution),当把一个不带泛型的对象赋值给一个带泛型的变量时,往往就会发生“堆物染”,如上①号代码。
    对于形参个数可变的方法,这种形参又时泛型,这将更容易导致“堆物染”。例如如下工具类:

    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<>();
            myList.add(new Random().nextInt(100));
            System.out.println(myList);
            // 把listArray的第一个元素赋为myList
            listArray[0] = myList;
            String s = listStrArray[0].get(0);
        }
    }
    

    上面程序①号代码处已经发生了堆物染。由于该方法有一个形参List...类型,个数可变的形参相当于数组,但Java又不支持泛型数组,因此程序只能把List...当成List[]处理,这里就发生了堆物染。
    在Java 6以及更早的版本中,Java编译器会认为finalMethod()没有任何问题,既不会提示错误,也没有警告。
    等使用该方法时,例如:

    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<>();
            myList.add(new Random().nextInt(100));
            System.out.println(myList);
            // 把listArray的第一个元素赋为myList
            listArray[0] = myList;
            String s = listStrArray[0].get(0);
        }
        public static void main(String[] args)
        {
            ErrorUtils.faultyMethod(Arrays.asList("Hello"),
                    Arrays.asList("world!"));//①
        }
    }
    


    该编译会在代码①处引发一个警告:

    ---------- 编译Java ----------
    注: ErrorUtils.java使用了未经检查或不安全的操作。
    注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。
    
    输出完成 (耗时 1 秒) - 正常终止
    

    这个unchecked警告比较突兀,定义faultyMethod()方法没有任何警告,调用该方法时却引发一个警告。
    而且程序运行时,会在String s = listStrArray[0].get(0);处引发一个ClassCastException异常。
    从Java 7开始程序将会进行更严格的检查,Java编译器会在编译器编译ErrorUtils时就会发出一个警告。
    由此可见,Java 7会在定义该方法时就会发出“堆物染”警告,这样保证开发者“更早”地注意到程序中可能存在的漏洞。
    有时开发者不希望看到这个警告,则会使用如下三个方式来抑制警告:
    (1)@SafeVaragrs修饰引发该警告的构造器或方法。Java 9增强了该注解,允许使用该注解修饰私有实例方法。
    (2)@SuppressWarnings("unchecked")修饰
    (3)编译时使用-Xlint:vargars选项

    1.5 函数式接口与@FunctionalInterface

    从Java 8开始:如果接口中只有一个抽象方法(可以包含多个默认方法或多个Static方法),该接口就是函数时接口。@FunctionalInterface就是用于指定某个接口必须是函数式接口。
    提示:函数式接口就是Java 8专门为Lambda表达式准备的,Java 8允许使用Lambda表达式创建函数式接口的实例,因此Java 8专门增加了@FunctionalInterface.

    package section1;
    @FunctionalInterface
    interface Interface
    {
        static void foo()
        {
            System.out.println("Foo类方法");
        }
        default void bar()
        {
            System.out.println("bar类方法");
        }
        void test(int a,int b);
    }
    public class FunctionalIterfaceTest
    {
        public static void main(String[] args)
        {
            Interface i=(a,b)->{
                System.out.println("a+b="+(a+b));
            };
            Interface.foo();
            i.bar();
            i.test(3,5);
        }
    }
    Foo类方法
    bar类方法
    a+b=8
    

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

    {
    public static void main(String[] args)
    {
    //打印IngeritableTest类是否具有@Ingeritable修饰
    System.out.println(IngeritableTest.class.isAnnotationPresent(Inheritable.class));//true
    }
    }

  • 相关阅读:
    3. Node.js REPL(交互式解释器)
    2. npm 的使用
    hdu 6155 - Subsequence Count
    蒟蒻ACMer回忆录 · 一段弱校ACM的奋斗史
    Codeforces731F Video Cards
    Codeforces731E Funny Game
    Codeforces731D 80-th Level Archeology
    Codeforces732F Tourist Reform
    Codeforces732E Sockets
    Codeforces732D Exams
  • 原文地址:https://www.cnblogs.com/weststar/p/12716650.html
Copyright © 2020-2023  润新知