Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 385|回复: 0

[Java基础知识]Java中方法和变量在继承时的覆盖问题

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-10-1 15:02:29 | 显示全部楼层 |阅读模式
    想必你已经阅读了一两本这样的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 error
         String s="abc";
       int s=10;        //compile error
           }
       }
      ---如何访问被隐藏的变量呢? 使用“this”关键字可以访问被局部变量隐藏的本类中的实例变量,关键字“super”可以访问父类中被隐藏的实例变量,类变量可以用类加“.”来访问。强制转换为父类型。

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

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-4 22:14 , Processed in 0.303349 second(s), 38 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表