当我们在父类和子类中创建一个具有相同名称的变量,并尝试使用持有子类对象的父类引用访问它时,我们会得到什么?

为了理解这一点,让我们考虑下面的示例,其中在ParentChild类中声明一个具有相同名称的变量x

class Parent {// Declaring instance variable by name `x`String x = "Parent`s Instance Variable";public void print() {System.out.println(x);}
}class Child extends Parent {// Hiding Parent class's variable `x` by defining a variable in child class with same name.String x = "Child`s Instance Variable";@Overridepublic void print() {System.out.print(x);// If we still want to access variable from super class, we do that by using `super.x`System.out.print(", " + super.x + "\n");}
}

现在,如果我们尝试使用以下代码访问x ,将打印什么System.out.println(parent.x)

Parent parent = new Child();
System.out.println(parent.x) // Output -- Parent`s Instance Variable

一般而言,我们会说Child类将覆盖Parent类中声明的变量,并且parent.x将给我们任何Child's对象所持有的东西。 因为在方法上进行相同类型的操作时发生的是同一件事。

但是实际上并非如此, parent.x将为我们提供在Parent类中声明的Parent实例变量的值,但是为什么呢?

因为Java中的变量不遵循多态性,所以重写仅适用于方法,而不适用于变量。 并且,当子类中的实例变量与父类中的实例变量具有相同的名称时,则从引用类型中选择该实例变量。

在Java中,当我们在Child类中使用已经用于在Parent类中定义变量的名称定义变量时,Child类的变量将隐藏父类的变量,即使它们的类型不同。 这种概念称为可变隐藏。

换句话说,当子类和父类都具有相同名称的变量时,子类的变量将隐藏父类的变量。 您可以在文章什么是Java中的变量阴影和隐藏中阅读有关变量隐藏的更多信息。

变量隐藏与方法覆盖不同

尽管变量隐藏看起来像是覆盖变量,类似于方法覆盖,但事实并非如此,但覆盖仅适用于方法,而隐藏适用于变量。

方法覆盖的情况下,覆盖方法完全替代了继承的方法,因此当我们尝试通过持有子对象来从父对象的引用访问该方法时,将调用子类中的方法。 您可以在“方法重载与方法重载”一书中了解有关重载以及被重载的方法如何完全替代继承的方法的知识,以及为什么要遵循方法 重载 规则 。

但是在变量隐藏中,子类将隐藏继承的变量而不是替换它们,这基本上意味着子类的对象包含两个变量,而子变量则隐藏了父变量。 因此,当我们尝试从Child类中访问变量时,将从子类中访问该变量。

如果我简化了示例8.3.1.1-3。 隐藏 Java语言规范 的实例变量 :

当我们在Child类中声明一个具有相同名称(例如x作为Parent类中的实例变量的变量时,

  1. 子类的对象包含两个变量(一个是从Parent类继承的,另一个是在Child本身中声明的),但是子类变量隐藏了父类的变量。
  2. 由于声明xChild皮的定义xParent ,类的声明中Child ,简单名称x总是指到外地类中声明的Child 。 而且,如果Child类方法中的代码想要引用Parent类的变量x ,则可以将其作为super.x来完成。
  3. 如果我们尝试访问ParentChild类之外的变量,则从引用类型中选择实例变量。 因此,以下代码中的表达式parent2.x给出了属于父类的变量值,即使它持有Child的对象,但((Child) parent2).x可以从Child类访问该值,因为我们进行了相同的转换参考Child

为什么以这种方式设计可变隐藏

因此,我们知道实例变量是从引用类型而不是实例类型中选择的,并且多态性不适用于变量,但是真正的问题是为什么? 为什么变量被设计为跟随隐藏而不是覆盖。

因为如果我们在子类中更改其类型,则变量覆盖可能会破坏从父级继承的方法。

我们知道每个子类都从其父类继承变量和方法(状态和行为)。 想象一下,如果Java允许变量覆盖,并且我们在子类中将变量的类型从int更改为Object 。 它将破坏使用该变量的任何方法,并且由于子级已从父级继承了这些方法,因此编译器将在child级中给出错误。

例如:

class Parent {int x;public int increment() {return ++x;}public int getX() {return x;}
}class Child extends Parent {Object x;// Child is inherting increment(), getX() from Parent and both methods returns an int // But in child class type of x is Object, so increment(), getX() will fail to compile.
}

如果Child.x覆盖Parent.xincrement()getX()工作? 在子类中,这些方法将尝试返回错误类型的字段的值!

如前所述,如果Java允许变量覆盖,则Child的变量不能替代Parent的变量,这将破坏Liskov替代性原则(LSP)。

为什么从引用类型而不是实例中选择实例变量

如JVM内部如何处理方法重载和覆盖中所述 ,在编译时,覆盖方法调用仅从引用类处理,但是所有覆盖的方法在运行时都使用vtable被覆盖方法替代,这种现象称为运行时多态性。

同样,在编译时,变量访问也从引用类型处理,但是正如我们所讨论的,变量不遵循重写或运行时多态性,因此它们在运行时不会被子类变量替代,仍然引用引用类型。

一般而言,没有人会建议隐藏字段,因为这会使代码难以阅读并造成混乱。 如果我们始终坚持下去,这种混乱就不会出现。
创建POJO并通过将它们声明为私有并封装我们的字段的一般准则,并根据需要提供getter / setter,以便在该类之外看不到变量,并且子类无法访问它们。

您可以在此Github存储库中找到完整的代码,请随时提供宝贵的反馈。

翻译自: https://www.javacodegeeks.com/2018/11/instance-variable-class-overridden-class.html

为什么在子类中不重写超类的实例变量相关推荐

  1. 子类重写父类变量_为什么在子类中不重写超类的实例变量

    子类重写父类变量 当我们在父类和子类中创建一个具有相同名称的变量,并尝试使用持有子类对象的父类引用访问它时,我们会得到什么? 为了理解这一点,让我们考虑下面的示例,在该示例中,我们在Parent和Ch ...

  2. java 实例方法直接调用超类的实例方法_Java继承关系中,父类方法使用实例变量和调用实例方法的探究...

    面向对象编程中,某一个实例方法使用实例变量和调用其它实例方法的情况是常见的.当存在继承关系时,这种情况就变得复杂起来.以下就对继承关系中,父类的某实例方法使用实例变量和其它实例方法的情况进行探究.因为 ...

  3. Category中实现了原始类实例变量的get方法导致的警告

    开发任务完成了,就把项目中的警告清理了一下,最后就剩下这一个警告了. 警告信息 ld: warning: instance method 'alertView' in category from /U ...

  4. php继承 重写方法吗,php中如何重写一个方法呢?

    摘要: 下文讲述php类中"重写方法"的方法简介,如下所示: 在php类中,有时候父级中原有的方法无法满足系统的要求时, 此时我们可以通过继承父类,在子类中重写父类的方法, 使其功 ...

  5. java中重写的作用_Java中的重写

    如果一个类从它的父类继承了一个方法,如果这个方法没有被标记为final ,就可以对这个方法进行重写. 重写的好处是:能够定义特定于子类类型的行为,这意味着子类能够基于要求来实现父类的方法. 在面向对象 ...

  6. 在python中、实例变量在类的内部通过_[宜配屋]听图阁

    1.类变量.实例变量概念 类变量: 类变量就是定义在类中,但是在函数体之外的变量.通常不使用self.变量名赋值的变量.类变量通常不作为类的实例变量的,类变量对于所有实例化的对象中是公用的. 实例变量 ...

  7. 在python中、实例变量在类的内部通过_【python】类变量、实例变量

    1.类变量.实例变量概念 类变量: 类变量就是定义在类中,但是在函数体之外的变量.通常不使用self.变量名赋值的变量.类变量通常不作为类的实例变量的,类变量对于所有实例化的对象中是公用的. 实例变量 ...

  8. java中子类能否继承 重写父类的静态方法

    文章目录 1.疑问 子类能否继承父类的静态变量和静态方法 子类能否重写父类的静态方法 2. 原因 1.疑问 子类能否继承父类的静态变量和静态方法 首先,子类是可以继承父类的静态变量和静态方法的. cl ...

  9. 继承实现的原理、子类中调用父类的方法、封装

    一.继承实现的原来 1.继承顺序 Python的类可以继承多个类.继承多个类的时候,其属性的寻找的方法有两种,分别是深度优先和广度优先. 如下的结构,新式类和经典类的属性查找顺序都一致.顺序为D--- ...

最新文章

  1. 肠道菌群机制研究及国自然课题设计专题会议
  2. mybatis中sqlSession的设计与实现
  3. 2019牛客暑期多校训练营(第五场)F - maximum clique 1 (最大团:补图最大独立集)
  4. 用Vue的父子组件通信实现todolist的功能
  5. android 监听屏幕是否锁屏
  6. webpack(一) 配置
  7. Qt第五课 无构造函数可以接受源类型,或构造函数重载决策不明确
  8. 医学专业失业率最高 三类相关行业人才紧缺
  9. 如何配置mysql_怎样配置MySQL
  10. 【转载】金山词霸”屏幕取词技术揭密(讨论稿)
  11. ws2812B+单片机驱动
  12. opc 接口计算机,OPC接口使用技巧
  13. 单片机c语言实验报告心得,关于单片机实训心得体会
  14. java分支讵_Java实现简体字向繁体字的转换
  15. oracle SCAN
  16. element -ui如何去掉原来的蓝色下划线
  17. zt 李鸿章听过《彩云追月》?
  18. 在升序排序的数组中插入一个元素 用函数编程实现在一个按升序排序的数组中查找x应插入的位置,将x插入数组中,使数组元素仍按升序排列。 提示:插入(Insertion)是数组的基本操作之一。插入法排序算法
  19. 全景图转换为天空盒图
  20. 发明专利与实用新型的联系?

热门文章

  1. 【深搜】骑士游历(二)
  2. Educational Codeforces Round 54 (Rated for Div.2)
  3. hdu4609 3idiots 三角形计数 FFT
  4. 双向广搜 8数码问题
  5. 通俗易懂,常用线程池执行的-流程图
  6. Java压缩技术(五) GZIP相关——浏览器解析
  7. JVM内存管理------GC算法精解(复制算法与标记/整理算法)
  8. 什么是G1垃圾回收算法
  9. 到底什么是 OAuth 2.0
  10. MyBatis中动态SQL