Java中的方法和变量在继承时的覆盖问题

2008-02-23 08:16:42来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

想必你已经阅读了一两本这样的Java书籍,它们在开头都指出了面向对象编程的3个主要概念:封装、继承和多态。理解这3个概念对于领会Java 语言来说至关重要,而搞懂方法的覆盖又是理解继承概念的关键部分。

这个例子摘自 Java 语言规范

01:  class Super 

02:  { 

03:      static String greeting() 

04:      { 

05:         return "Goodnight"; 

06:      } 

07: 

08:      String name() 

09:      { 

10:         return "Richard"; 

11:      } 

12:  } 

 

01:  class Sub extends Super 

02:  { 

03:      static String greeting() 

04:      { 

05:         return "Hello"; 

06:      } 

07: 

08:      String name() 

09:      { 

10:         return "Dick"; 

11:      } 

12:  } 

 

01:  class Test 

02:  { 

03:      public static void main(String[] args) 

04:      { 

05:         Super s = new Sub(); 

06:         System.out.println(s.greeting()   ", "   s.name()); 

07:      } 

08:  }

运行 Test 类的结果如下

Goodnight, Dick

要是你得出了同样的输出结果,那么你或许对方法的覆盖有了较好的理解,如果你的结果和答案不一致,那就让我们一起找出原因,我们先分析一下各个类:Super类由方法 greeting和name组成,Sub 类继承了 Super 类,而且同样含有 greeting 和 name方法。Test 类只有一个 main方法。在 Test 类的第5 行中,我们创建了一个 Sub 类的实例。在这里,你必须明白的是:虽然变量 s的数据类型为 Super 类,但是它仍旧是 Sub 类的一个实例,如果你对此有些迷惑,那么可以这样理解: 变量s 是一个被强制转换为 Super 型的Sub 类的实例。

下一行(第 6 行)显示了s.greeting()返回的值,加上一个字符串,紧随其后的是 s.name()的返回值。关键问题就在这里,我们调用的到底是Super类的方法还是Sub类的方法,让我们首先判断调用的是哪个类的name()方法,两个类中的name()方法都不是静态方法,而是实例方法,因为Sub类继承了Super类,而且有一个和它父类同样标识的name()方法,所以Sub类中的name()

方法覆盖了Super类中的name()方法,那么前面提到的变量s又是Sub 类的一个实例,这样一来 s.name()的返回值就是“Dick”了。

至此,我们解决了问题的一半,现在我们需要判断被调用的greeting()方法究竟是Super类的还是Sub类的。需要注意的是,两个类中的greeting()方法都是静态方法,也称为类方法。尽管事实上Sub类的greeting()方法具有相同的返回类型、相同的方法名以及相同的方法参数。然而它并不覆盖Super类的greeting()方法,由于变量s被强制转换为Super型并且Sub类的greeting()方法没有覆盖Super类的greeting()方法,因此 s.greeting()的返回值为Goodnight。

还是很迷惑?请记住这条规则:“实例方法被覆盖,静态方法被隐藏”。

现在你可能会问“隐藏和覆盖有什么区别”你也许还未理解这点。然而实际上我们刚刚在这个Super/Sub 类的例子中已经解释了两者的不同。使用类的全局名可以访问被隐藏的方法,即使变量s是Sub类的一个实例,而且Sub类的greeting()方法隐藏了Super 类的同名方法,我们仍旧能够将s强制转换为Super型以便访问被隐藏的greeting()方法,与被隐藏的方法不同,对被覆盖的方法而言,除了覆盖它们的类之外,其他任何类都无法访问它们。这就是为何变量s调用的是Sub类的name(),而非Super类的name()方法。

也许对你来说 理解隐藏静态方法和覆盖实例方法的区别的最佳方式,就是自己创建几个类似于Sub/Super的类,再重复一次规则,实例方法被覆盖而静态方法被隐藏,被覆盖的方法只有覆盖它们的类才能访问它们,而访问被隐藏的方法的途径是提供该方法的全局名。现在你终于明白标题里问题的答案了吧。什么时候“被覆盖的”方法并非真地被覆盖了呢?答案就是“永远不会”。另外,还有几个要点,请谨记:

--试图用子类的静态方法隐藏父类中同样标识的实例方法是不合法的,编译器将会报错

--试图用子类的实例方法覆盖父类中同样标识的静态方法也是不合法的,编译器会报错

--静态方法和最终方法(带关键字final的方法)不能被覆盖

--实例方法能够被覆盖

--抽象方法必须在具体类中被覆盖

现在我们来看继承时变量覆盖和隐藏的问题,如果你认为你已经理解了上面的方法继承时的覆盖和隐藏问题,继而认为变量也如此的话,那么请继续往下看:

Java共有6种变量类型:类变量、实例变量、方法参数、构造函数参数、异常处理参数和局部变量。类变量包括在类中定义的静态数据成员以及在接口中声明的静态或非静态的数据成员。实例变量是在类体中声明的非静态变量,术语“变量成员”指的是类变量和实例变量。方法参数是用来传入方法体的。构造函数参数是用来传入构造函数的。异常处理参数用来传入一个try语句中的catch块的。最后,局部变量是在一个代码块或一个for语句中声明的变量。

class Base {

 int x = 1;

 static int y=2;

 int z=3;

 int method() {

  return x;

 }

}



class Subclass extends Base {

 int x = 4;

 int y=5;

 static int z=6;

 int method() {

  return x;

 }

}



public class Test {

 public static void main(String[] args) {

  Subclass s=new Subclass();

  System.out.println(s.x   " "   s.y  " "  s.z);

  System.out.println(s.method());

  

  Base b = (Subclass)s;

  System.out.println(b.x   " "   b.y  " "  b.z);

  System.out.println(b.method());

 }

}
			   
			   

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:使用PreparedStatement减少开发的时间

下一篇:Java 虚拟机类装载:原理、实现与应用