日期:2014-05-20  浏览次数:20904 次

看书看到继承与多态那,问两个基础问题
1、public   abstract     class   A{
              abstract   void   method();
              void   test(){
                    method();
              }
      }

      public   class   B   extends   A{
              void   method(){
                    System.out.println( "Sub ");
              }
      public   static   void   main(String[]   args);
              new   B().test();
              }
        }
书上说方法test()在A中定义,它调用了方法method()。虽然方法method()在类中被定义成抽象的,但仍然可以被调用,因为在运行环境中,Java虚拟机会执行B的实例的method方法。一个实例所属的类肯定实现了父类中所有的抽象方法。那倒数第三行的new   B().test();是不是就相当于   A   a   =   new   B();   a.test()呀?就是说这个实例B是属于类A的对么?

2、书上说对于一个引用类型的变量,Java编译器按照它声明的类型来处理。例如:
Base   who   =   new   Sub();
who.subVar   =   "123 ";     //编译出错,提示Base类中没有subVar属性
                  对于一个引用类型的变量,Java虚拟机按照它实际引用的对象来处理。例如以下代码编译通过,但运行时会抛出ClassCastException异常:
Base   who   =   new   Base();
Sub   s   =   (Sub)who;//运行时抛ClassCastException异常
我想问的是如何区分Java编译器和Java虚拟机呀?它们都分别是怎么处理这个引用变量的?

谢谢!

------解决方案--------------------

1 这种解释是正确的。Java的所有方法调用都是动态绑定的,即运行时系统会根据引用所表示的实际对象类型来确定调用哪个类的方法。new B()创建了一个类型B的对象,也可以说它是一个类型A的对象,因为类型B从类型A继承。由于类型B没有重写test()方法,所以new B().test()调用基类A的方法。在类A的test()方法中调用method()方法时,由于实际的对象是类型B的,且类型B实现了method()方法,所以会调用类型B的method()方法。这就是方法调用的动态绑定在起作用了。你可以试试,即使类型A不是抽象类,method()不是抽象方法,new B().test()也会调用类型B的方法的。因为Java中的所有方法调用都是动态绑定的。

2
Base who = new Sub();
who.subVar = "123 "; //编译出错,提示Base类中没有subVar属性
上面说的动态绑定,只能在运行时起作用,因为编译时编译系统不能确定变量的类型。就这个例子来说,who被声明为Base类型的变量,但是实际上引用一个Sub类型的对象。(假设程序编译通过,并开始运行)程序如果运行起来了,运行时系统当然知道who实际上表示一个Sub类型的对象,Sub类型定义有名字为subVar的public成员,可以进行who.subVar= "123 "这样的赋值操作,但是编译系统不了解这个情况,它认为who是Base类型的,而Base类型没有声明名为subVar的public成员,所以不能通过编译。

Base who = new Base();
Sub s = (Sub)who;//运行时抛ClassCastException异常
像这样进行强制类型转换当然是可以的,前提是Base和Sub类型之间存在继承关系(不论是直接的,还是间接的),如果不存在继承关系,编译也不能通过的。因为存在继承关系,那么,就可以把一个Sub类型的对象赋值给Base类型的变量,也就是说,这样的语句是合法的:Base who = new Sub()。此时,Base类型的变量who实际上表示Sub类型的对象。由于存在这种可能性,所以,编译系统允许进行这种类型转换。但是到了运行的时候,运行时系统发现,who实际表示的是Base类型的对象,不能转换到Sub类型,所以引发类型转换异常。