【问题标题】:Java Inheritance - this keywordJava 继承 - this 关键字
【发布时间】:2012-12-15 09:08:24
【问题描述】:

我在网上搜索了类似的问题,但没有找到。所以,在这里发帖。

在下面的程序中为什么'i'的值被打印为100?

AFAIK 'this' 指的是当前对象;在这种情况下是“TestChild”,并且类名也正确打印。但是为什么实例变量的值不是200呢?

public class TestParentChild {
    public static void main(String[] args) {
        new TestChild().printName();
    }
}

class TestChild extends TestParent{
    public int i = 200;
}

class TestParent{
    public int i = 100;
    public void printName(){
        System.err.println(this.getClass().getName());
        System.err.println(this.i); //Shouldn't this print 200
    }
}

此外,以下输出与我预期的一样。当我从父类调用“this.test()”时,将调用子类方法。

public class TestParentChild {
    public static void main(String[] args) {
        new TestChild().printName();
    }
}

class TestChild extends TestParent{
    public int i = 200;
    public void test(){
        System.err.println("Child Class : "+i);
    }

}

class TestParent{
    public int i = 100;
    public void printName(){
        System.err.println(this.getClass().getName());
        System.err.println(this.i); //Shouldn't this print 200
        this.test();
    }
    public void test(){
        System.err.println("Parent Class : "+i);
    }
}

【问题讨论】:

    标签: java inheritance overriding this


    【解决方案1】:

    没有办法覆盖类变量。

    您不会覆盖 Java 中的类变量,而是隐藏它们。覆盖是实例方法,隐藏不同于覆盖。

    在您给出的示例中,通过在类TestChild 中声明名为“i”的类变量,您隐藏了它将从其超类TestParent 继承的具有相同名称“i”的类变量。以这种方式隐藏变量不会影响超类TestParent中类变量'i'的值@

    要获得所需的行为,您只需覆盖 getI() 方法

    class TestChild extends TestParent{
    
        private int i = 200;
    
        @Override
        public int getI() {
             return this.i;
        }
    }
    

    【讨论】:

    • 非常感谢您的回答。我读过关于隐藏(阴影)的文章,但忘记了。 :(再次感谢。
    【解决方案2】:

    因为 Java 中的字段不是继承的。使用您的声明,您已经有效地声明了两个名为i不同 字段,TestChild 的实例将同时拥有这两个字段。在编译TestParent 时,其方法中对i 的引用将始终引用TestParent.i

    【讨论】:

    • 字段是继承的,只是没有被覆盖。
    • 当然,这样措辞更好。
    【解决方案3】:

    Java 没有虚拟字段,因此 printName 中的 i 字段始终引用 TestParent.i 而不是任何后代子节点。

    Java 中通过继承实现的多态性只发生在方法上,所以如果你想要你所描述的行为,那么你会想要这个:

    class TestChild extends TestParent{
    
        private int i = 200;
    
        @Override
        public int getI() { return this.i; }
    }
    
    class TestParent{
    
        private int i = 100;
    
        public int getI() { return this.i; }
    
        public void printName(){
            System.err.println( this.getClass().getName() );
            System.err.println( this.getI() ); // this will print 200
        }
    }
    

    【讨论】:

    • 感谢您的及时回复。可以请教一下,这是什么意思? “在 Java 中的字段不能被标记为虚拟” 正如我所提到的,在我的第二个程序中,实例方法被正确调用。但是实例变量是指父类。
    • 在 Java 中,所有方法(除非指定)都是“虚拟的”,这意味着它们具有关联的 vtable,允许运行时调度。但是,字段作为绝对(ish)内存位置存在,并且不会被任何 vtable 偏移。我将混淆归咎于 Java 的设计文档,它没有明确的“虚拟”和“覆盖”关键字,而 C# 有。
    • 还有一个问题。 Java 规范说 this 指的是当前对象,在这种情况下是 TestChild 的实例。这就是当我调用 this.test() 时,它从子类调用方法的原因。但是当我说 this.i 时,Java 如何解释 this 关键字?
    • @user1790274 基本上,您的对象有两个完全独立的i 版本:TestChild.iTestParent.i。忘记它们看起来像变量i 的事实,它们是完全独立的。当一个方法使用i 时,Java 会寻找它可以找到的最具体的版本。对于在 TestChild 中定义的方法,它是 TestChild.i,对于 TestParent 也是如此。顺便说一句,这个术语是“阴影”,它可能是令人困惑的错误的来源(正如您所发现的那样!)。最好的解决方案是始终使用私有变量,这样就没有理由期望this.i 永远是父母的i
    • (对不起,一条评论太长了)这种行为对于方法是不同的,因为正如@Dai 指出的那样,Java 的方法是虚拟的。也就是说,您(基本上,暂时需要一些手)只有 一个 版本的 test() 方法,并且孩子的 test() 替换了父母的。所以基本上,方法是通过继承替换的,而变量不是,而是创建了两个独立的、名称容易混淆的变量。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-11
    • 2014-05-28
    • 2017-08-25
    • 1970-01-01
    • 2018-05-30
    • 1970-01-01
    • 2014-02-27
    相关资源
    最近更新 更多