5

所以我想弄清楚为什么一个程序会这样编译,希望你们能为我解释一下。

class Vehicle{
   public void drive() throws Exception{
     System.out.println("Vehicle running");
   }
}

class Car extends Vehicle{
   public void drive(){
      System.out.println("Car Running");
   }

   public static void main(String[] args){
      Vehicle v = new Car();
      Car c = new Car();
      Vehicle c2 = (Vehicle) v;

      c.drive();
      try {
          v.drive();
      } catch (Exception e) {
          e.printStackTrace();
      } //try v.drive()

      try {
          c2.drive();
      } catch (Exception e) {
          e.printStackTrace();
      } //try c2.drive()
   }
}

所以上述程序的输出将是

汽车运行
汽车运行
汽车运行

我的问题是,为什么我必须做一个 try/catch 块来为 v 和 c2 对象而不是 c 调用 drive() 方法?它们都是 Car 的实例,所以这里发生了什么?

4

4 回答 4

10

Vehicle有一个drive()抛出异常的方法。

Car用它自己的不抛出异常的方法覆盖Vehicle's方法。Drive()Drive()

你得到你所做的输出的原因是因为即使Vehicle v是 car 类型,编译器在编译时也不知道这个事实,所以当你调用v.drive()编译器时不知道你正在调用 Car 的drive方法。

假设您v通过以下方式实例化:

Vehicle v;
if(rand(0,1) == 1)
    v = new Car();
else
    v = new Vehicle();

编译时您不会知道 v 是否是汽车。在你运行程序之前你不会知道。

于 2012-07-12T19:50:18.770 回答
1

被覆盖的方法可以更具体地说明它们返回和抛出的内容。它们可以使用返回对象和异常的子类,或者省略父方法签名中声明的异常。当您调用 ((Vehicle)new Car()).drive() 时,将执行子级的实现,但在编译时使用父级的方法签名,这会迫使您捕获 Vehicle.Driver 中定义的异常。

于 2012-07-12T20:22:27.270 回答
0

其他答案已经解释了覆盖的方法;但是,还有另一个问题。你之前这么说:

它们都是 Car 的实例,所以这里发生了什么?

但是对于编译器来说,它们并不是 Car 的所有实例——编译器会查看变量的类型:

  Vehicle v = new Car();
  Car c = new Car();
  Vehicle c2 = (Vehicle) v;

在编译时,v被视为 a Vehiclec作为 a Car,并且将相应地处理异常。在运行时,JVM 知道v实际包含 a Car,但这是不同的。

于 2012-07-12T19:54:19.977 回答
0

我们有:

  Vehicle v = new Car();
  Car c = new Car();
  Vehicle c2 = (Vehicle) v;

此时,v和。这意味着您正在调用. 这就是为什么您需要为这些方法而不是为这些方法尝试/捕获(因为该方法不会引发异常)。c2Vehiclepublic void drive() throws ExceptionVehiclec.drive()

于 2012-07-12T19:51:15.853 回答