`
雅马哈大野熊
  • 浏览: 6700 次
  • 性别: Icon_minigender_1
  • 来自: 西安
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

你或许不知道——在极端情况下,父类访问子类变量所存在的问题

阅读更多
程序清单:
package com.yjk;

public class Parent
{

//定义一个名为i的实例变量
private int i = 1;

public Parent()
{
this.display();
}
public void display()
{
System.out.println(i);
}
}
---------------------------------------------------------------------
public class Son extends Parent
{
//定义一个名为i的实例变量
private int i = 2;

//构造器,将实例变量i初始化为3
public Son()
{
i = 3;                                          //⑵
}
public void display()
{
System.out.println(i);
}

}
---------------------------------------------------------------------
package com.yjk;

public class Test {

public static void main(String[] args)
{
//创建子类实例
new Son();                                    //⑴
}

}
}
---------------------------------------------------------------------

这行代码会调用子类Son里的构造器,由于子类Son继承了父类Parent,而且子类Son的构造器中,并没有显示的使用Super

调用父类Parent的构造器,因此系统会自动调用Parent类中的无参构造器来执行初始化。

在父类Parent的无参构造器中,只是调用了display方法来向控制台输出变量i的值,但是结果却为0。

OK,那么到此,如果你知道是为什么,就可以略过以下内容,反之,请耐心的滚动你的鼠标:

当程序运行至1处时,系统为Son实例对象分配了两块内存空间,是的,换言之就是说,Son对象拥有2个名为i的实例变量

声明:
  永远记住,构造起只负责对 实例对象的变量进行赋值 ,别无它用。构造器不是用来创建对象的,不是,真的不是!

其实在构造器执行之前,系统已经为该对象分派好内存了,此时此刻,内存中的所有变量值都为默认值,比

如:0,false,null.

言归正传,系统为Son对象分配了2块内存,分别用于存放变量i,其中一个属于Parent类定义的i实例变量,另一个属于

Son类定义的i实例变量,此时此刻,二者的值都为0;

接下来,便是先执行父类的构造器对其值进行赋值操作。Parent中定义i的时候便对其进行了初始化操作,且值为1.接着便

执行display()操作,OK,问题来了人,该方法this.diaplay() 中的this 是谁?对,此时还是不够混乱,OK,我们对Parent做

一点小修改:

package com.yjk;

public class Parent
{

//定义一个名为i的实例变量
private int i = 1;

public Parent()
{
System.out.println(this.i);
this.display();

}
public void display()
{
System.out.println(i);
}
}
再次运行,我们可以得到1,0;此刻是不是觉得更加混乱了?

揭秘:

当this在构造器中出现的时候,它所代表的正是当前初始化的实例对象,此时的this位于Son的构造方法中,但是代码们

却出现在父类Parent的构造方法中,那么系统便在Son构造器中,隐式的调用父类Parent构造器中的代码,故,此时的this

是Son对象,既然如此,它输出的却是父类对象的值,这是因为该行代码位于Parent类型中的构造器中,故其编译类型是

Parent,而它实际引用Son了对象.

下面对其进一步进行解释:
package com.yjk;

public class Parent
{

//定义一个名为i的实例变量
private int i = 1;

public Parent()
{
System.out.println(this.i);
this.display();
System.out.println(this.getClass());
}
public void display()
{
System.out.println(i);
}
}

---------------------------------------------------------------------


当变量的编译时的类型和运行时的类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该

变量的类型决定。但通过该变量调用它引用的对象的实例方法时,该方法行为将由它实际引用的对象来决定。因此,当

程序访问this.i时,将会访问Parent类中定义的i实例变量,也就是输出1;但执行this.display()方法时,则实际表现出Son对

象的行为,也就是输出Son对象的实例变量,即 0.

”—李刚

看一次可能不能完全接受,多看即便就好了,最后,希望这个帖子能够为你带来一定的收获。  
分享到:
评论

相关推荐

    在父类获取子类属性

    但是,子类可能会添加自己的特有属性,这些属性在父类中是不存在的。 **继承的优势:** 1. **代码重用**:子类可以直接使用或覆盖父类的方法和属性,减少重复代码。 2. **多态性**:子类实例可以被视为父类类型,...

    父类赋值给子类

    子类的构造函数可以接受一个父类对象作为参数,然后在构造函数内部,使用成员变量直接赋值或者通过 `base` 关键字调用父类的构造函数。例如: ```csharp public class Parent { public int Id { get; set; } ...

    java多态性详解——父类引用子类对象

    这种情况下,当调用子类对象的方法时,实际上执行的是子类中重写后的方法,而不是父类中的同名方法。这是多态性中最为重要的一个方面,因为它体现了动态绑定的特性。 #### 方法的重载 方法的重载则是在同一类中...

    论JAVA继承机制中父类与子类的关系

    如果在某些情况下,开发者不希望子类覆盖或重写父类的方法或成员变量,可以利用`super`关键字来显式地访问父类中的成员。例如,可以在子类中通过`super.变量名`或`super.方法()`的方式访问父类的成员。 #### 二、...

    子类、父类查询

    有关子类和父类。A是子类,Teacher是父类。A是Teacher 的一种(子类)会继承父类李非私

    java 子类对象赋值给父类对象的使用

    在Java编程语言中,子类继承父类是一种常见的特性,它允许子类扩展或复用父类的功能。当我们创建一个子类对象并将其赋值给父类引用时,这种行为被称为向上转型(Upcasting)。这一过程是自动且安全的,因为子类对象...

    java多态性详解——父类引用子类对象.pdf

    在这种情况下,父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的。 第二点,多态性可以通过方法的重写和重载来实现。在上面的例子中,Father 类中有两个...

    父类引用指向子类对象

    访问权限方面,`public`属性和方法在任何情况下都可以被访问,`protected`成员在同包内和子类中可以访问,`default`(包访问权限)仅在同包内可访问,而`private`成员只在其定义的类内部可见。因此,如果父类引用...

    子类可以调用父类.pdf

    如果一个类继承一个父类,那么子类会拥有父类所规定的所以成员 子类还可以拥有父类没有的独有成员 父类可以完成的任务,子类对象也可以完成 子类可以调用父类Person p= new student(); 声明父类变量,实例化子类...

    java 静态非静态 字段方法 子类父类构造_初始化顺序!

    java 静态_非静态 字段_方法_代码块 子类父类构造_初始化顺序! 三个class 让你清清楚楚 第一个class java代码如下: package initialOrder; class Parent { // 静态变量 public static String p_StaticField...

    C#子类舍弃属性转换为父类UtilSubToBase.cs

    利用C#反射将子类舍弃多余的属性转换为父类,用途比较少。

    Java中父类和子类之间的转换操作示例

    这意味着,我们不能将父类对象赋值给子类引用变量,然后使用子类引用变量访问子类中的方法和变量。例如,在上面的代码中,我们尝试将 `People` 类的对象赋值给 `Chinese` 类的引用变量 `c1`,但是这将会抛出一个 `...

    Java中父类怎么调用子类的方法

    Java中父类调用子类的方法是面向对象编程中一个常见的问题,了解如何在Java中实现父类调用子类的方法对于编程开发者来说非常重要。本文将通过示例代码和详细的解释,介绍三种方式来实现父类调用子类的方法,并对每...

    将子类引用赋给父类对象时,java虚拟主机并没有将那些非继承成员丢弃

    然而,这并不意味着在转换过程中会丢失子类的非继承成员,即那些在父类中不存在的方法或属性。Java虚拟机(JVM)在内存中以特定方式存储这些成员,使得它们在需要时依然可以访问。 首先,理解Java中的类继承关系。...

    java中的继承(子类调用父类构造方法还不懂).doc

    - 子类方法的访问级别不能小于父类方法的访问级别。 - 重写通常发生在继承关系中。 **2. 重载(Overloading)** - **定义**:在同一类中,方法名相同但参数列表不同的多个方法,称为方法重载。 - **规则**: - ...

    再次详细说明在继承中的程序执行顺序,子类与父类到底哪个先执行

    本文将深入探讨在继承中的程序执行顺序,以及子类与父类的执行顺序问题。 首先,我们需要了解Java或Python等面向对象语言中类的构造过程。当一个类实例化时,其构造函数(在Java中是`构造器`,在Python中是`__init_...

    java代码-使用java解决父类与子类的声明应用的源代码

    java代码-使用java解决父类与子类的声明应用的源代码 ——学习参考资料:仅用于个人学习使用!

    java,定义父类和子类.pdf

    ### Java中的父类与子类概念详解 在Java编程语言中,继承是一种强大的特性,它允许我们创建新的类,这些新类会继承已有类的属性和行为。这种机制不仅能够提高代码的复用性,还能使得类之间的关系更加清晰、易于理解...

Global site tag (gtag.js) - Google Analytics