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

2016-02-19 19:29 7 1 收藏

下面图老师小编跟大家分享Java中的方法和变量在继承时的覆盖问题,一起来学习下过程究竟如何进行吧!喜欢就赶紧收藏起来哦~

【 tulaoshi.com - 编程语言 】

  想必你已经阅读了一两本这样的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()); }}

  
   运行可以得到输出:

  4 5 6

  4

  1 2 3

  4

  由此我们可以得出:

  实例变量和类变量能被隐藏,被子类的同名变量成员隐藏。局部变量和各种参数永远不会被隐藏(参见下例)。

  

class Hidden  {  public static void main(String args[])  { int args=0;  //compile errorString s="abc";  int s=10;//compile error  }  }

  如何访问被隐藏的变量呢? 使用“this”要害字可以访问被局部变量隐藏的本类中的实例变量,要害字“super”可以访问父类中被隐藏的实例变量,类变量可以用类加“.”来访问。强制转换为父类型。

  变量和方法覆盖和隐藏的不同:一个类的实例无法通过使用全局名或者强制自己转换为父类型,以访问父类中被隐藏的方法,然而强制转换子类为父类型之后,可以访问父类中被隐藏的变量。另外静态方法不能覆盖父类的实例方法,而静态变量却可以隐藏父类的一个同名实例变量,同样,实例方法不能覆盖父类的同名静态方法,而变量却可以隐藏父类的同名变量成员,不论父类的这个变量成员是类变量或者是实例变量。

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

来源:http://www.tulaoshi.com/n/20160219/1621147.html

延伸阅读
一、类的初始化 对于类的初始化:类的初始化一般只初始化一次,类的初始化主要是初始化静态成员变量。 类的编译决定了类的初始化过程。 编译器生成的class文件主要对定义在源文件中的类进行了如下的更改: 1)       先按照静态成员变量的定义顺序在类内部声明成员变量。 2)     ...
标签: flash教程
 Flash的actionScript和其他的语言有不同的地方, 我查了几本书, 把我所了解的补充一下。     1.Flash没有真正意义上的全局变量, 这点和Director不同, 全局变量是指一旦声明, 在整个电影都有效, 这点Flash 是做不到的。对于我们来说, 要讨论的是:     a...
       UDP协议 UDP协议的全称是用户数据报,在网络中它与TCP协议一样用于处理数据包。在OSI模型中,在第四层传输层,处于IP协议的上一层。UDP有不提供数据报分组、组装和不能对数据包的排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。 为什么要使用UDP 在选择使用协议的时候,选择UDP必须...
首先来看看以下程序将会打印出什么: 代码如下: class Dog {     public static void bark() {         System.out.print("woof ");     } } class Basenji extends Dog {     public static void bark() { } } public class Bark {     ...
标签: Java JAVA基础
编者按: 程序中经常有这样的要求,整个程序运行时只有一个实例被使用。比如:数据库连接池,系统参数配置,Java API 中的 Runtime, Calendar ...如何实现这种需求成为一个值得讨论的问题。让我们来看看本文的例子。   A.常见的例子 以往的做法,是在程序的某个类里面(比如是 GlobalObject )...

经验教程

274

收藏

17
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部