8.2 内建注解

  在Java的java.lang包中,预定义了三个注解,它们分别是限定重写父类方法的@Override注解,标记已过时的@Deprecated 注解和抑制编译器警告的@SuppressWarnings 注解,通常称这三个注解为内建注解或基本注解。

8.2.1 @Override注解

  @Override注解被用于标注方法,被该注解标注的方法是重写了父类的方法,起到了确定的作用。下面通过一个例子来演示@Override注解的作用。

  假设“租车系统”中Vehicle类和Truck类的代码如下,其中Truck类继承自Vehicle类,且重写了Vehicle类的drive()方法(根据需求这个方法必须要重写)。

  1. public class Vehicle
  2. {
  3. String name = "汽车";
  4. int oil = 20;
  5. int loss = 0;
  6. public Vehicle(String name)
  7. {
  8. this.name = name;
  9. }
  10. //车辆行驶的方法
  11. public void drive()
  12. {
  13. if(oil < 10)
  14. {
  15. System.out.println("油量不足10升,需要加油!");
  16. }else{
  17. System.out.println("正在行驶!");
  18. oil = oil - 5;
  19. loss = loss + 10;
  20. }
  21. }
  22. }
  23. public class Truck extends Vehicle
  24. {
  25. private String load = "10吨";
  26. public Truck(String name,String load){
  27. super(name);
  28. this.load = load;
  29. }
  30. public static void main(String[] args){
  31. Vehicle t1 = new Truck("大力士","5吨");
  32. t1.drive();
  33. }
  34. //子类重写父类的drive()方法
  35. public void drive()
  36. {
  37. if(oil < 15)
  38. {
  39. System.out.println("油量不足15升,需要加油!");
  40. }else{
  41. System.out.println("正在行驶!");
  42. oil = oil - 10;
  43. loss = loss + 10;
  44. }
  45. }
  46. }

  编译、运行Truck类,程序可以按用户需求执行。但是有可能程序员在写Truck类的代码时,误将drive()写成了driver(),然而在执行t1.drive();语句时,因为drive()方法并未被重写,因此t1.drive()调用的还是Vehicle类的drive()方法。最不幸的是,这种错误程序编译时是不会报错的,即使在运行时如果不跟踪代码也不容易发现这个错误,这样最终会为以后修复这个错误带来很大的困难。

  @Override注解就是为了解决类似问题出现的,我们可以在子类重写父类的方法前加上@Override,表示这个方法是覆盖了父类的方法。如果该方法不是覆盖了父类的方法,例如如上所述将drive()写成了driver(),此时在driver()方法前加上@Override注解的话,则代码编译不能通过,提示被@Override注解的方法必须在父类中存在同样的方法,程序才能编译通过。

  需要补充一句,@Override注解只能用来修饰方法,不能用来修饰其他元素。

8.2.2 @Deprecated注解

  如果大家之前使用Eclipse等集成开发环境编写过Java程序,会经常在属性或方法提示中看到@Deprecated。如果某个类成员的提示中出现了@Deprecated,就表示这个类成员已经过时,在未来的JDK版本中可能被删除,不建议使用。之所以现在还保留,是因为给那些已经使用了这些类成员的程序一个缓冲期,否则如果现在就删除这个类成员,那么这些程序就无法在新的环境下编译运行了。

  在学习多线程的时候,提到过终止一个线程可以调用这个线程的stop()方法,但该方法已被废弃,不建议使用。通过查看JDK API可以看到,Thread类的stop()方法是被@Deprecated注解标注的,所以准确来说,这个stop()方法是因为过时才不建议使用的。

  简化前面Truck类的代码,并在drive()方法前使用@Deprecated注解标注。如果集成开发环境换成Eclipse,则在方法定义处、方法引用处以及在成员列表中都有变化,如图8.1所示。

8.2 内建注解 - 图1


图8.1 @Deprecated注解的使用

  在这个例子中,Truck类的drive()方法被@Deprecated注解标注,提醒程序员这是一个过时的方法,尽量不要使用,避免以后出现问题。假设有个BigTruck类继承Truck类,并且重写了这个过时drive()方法,又会怎样呢?编译程序,编译器会报错,提示“注意:BigTruck.java使用或覆盖了已过时的API。”和“注意:要了解详细信息,请使用-Xlint:deprecation重新编译。”

8.2.3 @SuppressWarnings注解

  这个世界总是这么奇妙,既然有可以使编译器产生警告信息的注解,那么通常也会有抑制编译器产生警告信息的注解,@SuppressWarnings注解就是为了这样一个目的而存在的。让我们先看看下面的代码:

  1. import java.util.*;
  2. public class TestZuChe
  3. {
  4. public static void main(String[] args)
  5. {
  6. List vehAL = new ArrayList();
  7. Truck t1 = new Truck("大力士","5吨");
  8. vehAL.add(t1);
  9. }
  10. }

  编译程序,编译器会抛出如下的警告信息:“注意:TestZuChe.java使用了未经检查或不安全的操作。”和“注意:要了解详细信息,请使用-Xlint:unchecked重新编译。”

  增加了-Xlint:unchecked编译参数之后重新编译,显示结果如图8.2所示。

8.2 内建注解 - 图2


图8.2 编译器警告信息

  这个警告信息提示List类必须使用泛型才是安全的,才可以进行类型检查,现在未做检查,所以存在不安全因素。如果想取消这些警告信息,具体代码如下:

  1. import java.util.*;
  2. public class TestZuChe
  3. {
  4. @SuppressWarnings(value = "unchecked")
  5. public static void main(String[] args)
  6. {
  7. //List<Truck> vehAL = new ArrayList<Truck>();
  8. List vehAL = new ArrayList();
  9. Truck t1 = new Truck("大力士","5吨");
  10. vehAL.add(t1);
  11. }
  12. }

  再次编译程序,警告被抑制。当然,编译器出现警告,是要提醒程序员有哪些地方需要注意,抑制警告不是目的,正确的解决办法是使用泛型对集合中的元素进行约束,使对集合的操作可以被检查,如代码中被注释的部分那样。

  @SuppressWarnings注解和前面两个注解不同之处在于,这个注解带一个参数,或者说有一个属性。这里注解@SuppressWarnings(value = “unchecked”)的含义为抑制不检查的警告。当然还可以同时抑制其他警告,例如@SuppressWarnings(value = {“unchecked”, “unused”})就是同时抑制了不检查和未被使用的警告。下面列举了@SuppressWarnings注解相关属性值的含义。

  • deprecation:使用了过时的程序元素。

  • unchecked:执行了未检查的转换。

  • unused:有程序元素未被使用。

  • fallthrough:switch程序块直接通往下一种情况而没有break。

  • path:在类路径中有不存在的路径。

  • serial:在可序列化的类上缺少serialVersionUID定义。

  • finally:任何finally子句不能正常完成。

  • all:所有情况。