首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
V2EX  ›  问与答

学了一段时间的 Java , 我发现我的面向对象知识白学了,和 PHP 的好像好多不同。

  •  
  •   DavidNineRoc · 70 天前 · 1339 次点击
    这是一个创建于 70 天前的主题,其中的信息可能已经有所发展或是发生改变。

    话不多说,直接上代码。

    
    import java.io.*;
    class Main  
    {
    	public static void main (String[] args)
    	{
    		new Son().show();
    	}
    }
    
    class Father {
        
        protected String name = "father";
        
        public void show() {
            
            System.out.println(name);
        }
        
    }
    
    class Son extends Father {
        
        protected String name = "son";
    }
    

    PHP

    <?php
    
    class Father {
        
        protected $name = "father";
        
        public function show() {
        
            echo $this->name;
        }
        
    }
    
    class Son extends Father {
        
        protected $name = "son";
    }
    
    
    (new Son)->show();
    

    这两个结果居然执行不一样的,最近在写一个小 demo 发现,写继承经常碰见不可预料的结果

    话说真正的答案应该是怎么样的呢???

    第 1 条附言  ·  70 天前
    每一门语言都有点不同. 习惯在 PHP 重写属性惯了.
    非常感谢各位的指导,又学了一课
    20 回复  |  直到 2019-08-11 17:24:01 +08:00
        1
    lihongming   70 天前 via iPhone
    Java 变量在使用前必须声明,所以你在子类里的声明是声明了一个新的变量,不是改变父类里同名变量的值。

    而 PHP 没有声明的要求,声不声明都一样。
        2
    Cbdy   70 天前 via Android
    Java 是一门很严谨的语言
        3
    AlvaIM   70 天前
    你这种学习叫死记硬背似的学习, 其实根本没有理解面向对象的实质
        4
    xlcoder166   70 天前
    Within a class, a field that has the same name as a field in the superclass hides the superclass's field, even if their types are different. Within the subclass, the field in the superclass cannot be referenced by its simple name. Instead, the field must be accessed through super. Generally speaking, we don't recommend hiding fields as it makes code difficult to read.

    From this definition, member fields can not be overridden like methods. When a subclass defines a field with the same name, the subclass just declares a new field. The field in the superclass is hidden. It is NOT overridden, so it can not be accessed polymorphically.

    若是你希望输出 son 你需要重写在 show

    最后不要在项目中这样做 会被喷的
        5
    DavidNineRoc   70 天前
    @lihongming 我并没有打算改变父级变量的名字,而是想通过父类 统一 的获取子类的字段,而不是重写,在 Java 中是不是有别的写法呢?
    @Cbdy 嗯,很严谨
    @AlvaIM 大哥懂得多,能像楼下说两句话有实质性的话吗?
    @xlcoder166 PHP 写多了,很多框架也这样子写。导致误解了。 因为子类继承基类之后,基类有一堆统一的方法操作子类。这才有多态的意义啊。不然每次都要子类重写好麻烦。
        6
    donething   70 天前
    拿本 Java 的书从头学吧,别和 PHP 比较了。
        7
    xlcoder166   70 天前
    你这个说法有歧义呀 话句话说 你究竟想获得什么预期?

    子类操作基类的 Filed 还是啥 若是前者可以通过构造函数
        8
    troywinter   70 天前
    @DavidNineRoc 父类操作子类?你怕不是对面向对象有什么误解吧
        9
    troywinter   70 天前
    如果你想改变 son 的值,应该直接 this.son = "son" , 而不是重新定义它,你还是先了解一下子类的 initialization 顺序吧
        10
    kenvix   70 天前
    用 PHP 做入门语言就会被这个语言神奇的语法所迷惑。
        11
    bkmi   70 天前
    Java 不支持 override 成员变量,Kotlin 支持,每一门语音都会有一些差异,不要让老的思想禁锢了你。
        12
    nguoidiqua   70 天前   ♥ 3
    多态的意义是同一个方法可以由子类进行不同的实现,然后用父类调用的时候可以表现不同的形态。

    所以你这个例子本身就体现不了多态的意义,能表现的只有继承机制的意义。

    你是想 show 的时候显示子类赋值的名字而已,那你直接给 name 字段改个值就是了,Java 声明并赋值和单纯赋值是有区别的,这跟 PHP 不一样。

    你去反编译下 class 文件看看就知道了,如果子类没有重写父类方法的话,实际上子类是没有这个方法的,它会去引用的父类方法,字段也是如此的,没有声明的话就是没有这个字段。它运行时会先从自身寻找方法或字段,找不到就去上级类找。而父类那个 show 方法,编译后它会把参数改成 this.name,所以它打印的是一定是它自身的 name 字段。

    现在你没有去改 super.name 的值,你是在子类这层新声明了一个新的 name 字段,你赋值的时候,改的只是子类这层的字段,父类不会变的。如果你没有声明新字段,而是直接赋值 this.name = "son",那么由于子类是没有这个字段的,它就回去往上找这个字段,找到父类有这个字段,然后改成 ” son “,这就能达到了你的目的。
        13
    DavidNineRoc   70 天前
    @donething 从头学是应该的
    @xlcoder166 抱歉,没表达清楚.因为想通过基类操作子类的属性.达到代码量的减少.
    @troywinter 不是改变 son 的值,比如我有一个基类窗口标题是通过 name 属性显示,我想要它的所有子类重新定义 name 值. 然后就可以通过 基类的 getName 方法获取到不一样的值.
    @kenvix 是有点迷
    @bkmi 好的,谢谢忠告
    @nguoidiqua
        14
    DavidNineRoc   70 天前
    @nguoidiqua 感谢, 看了文档. 了解更多了.
        15
    random0O   70 天前 via Android
    @DavidNineRoc 你需要的是在父类声明一个 abstract getName(), 然后不同子类提供不同实现,父类不必操心他们实际把值存在哪里。
        16
    DavidNineRoc   70 天前
    @random0O 写多 PHP 更喜欢定义属性,而不是重写方法. 看来要改改了. 不过已经参考 12 l 的做法了.
        17
    xlcoder166   70 天前
    Talk is Cheap

    abstract public class Foo {

    String name ;

    abstract void getName();
    }

    public class FooA extends Foo {

    FooA (String name) {
    this.name = name;
    }

    public void getName() {
    System.out.println(this.name);
    }

    public static void main(String[] agrs) {
    new FooA("TianYa").getName();
    }
    }
        18
    nguoidiqua   70 天前
    如果你非要说父类的字段我就是不要改,我就是要两个字段同时存在并打印子类新声明的同名字段,那只能重写 show,虽然一模一样……

    如果你只是想要子类打出和父类不一样的值,那给字段赋值的方法很多,最简单的就是在初始化代码块里面赋值,就写两个大括号而已,不要声明,直接赋值。

    class Son extends Father {
    {
    this. name = "son";
    }
    }

    其实 PHP 和 Java 的继承机制不同的,我试了下把你这个 PHP 例子里面 Father 的 name 字段直接去掉,子类依然可以正常调用 show 方法,倒是 Father 自己调会报错。我感觉 PHP 的继承好像就是把父类的字段和方法直接 include 到子类里面去,和 Java 完全不一样的。

    老实说,比起它们这两者,我更喜欢 GO 那种面向对象的实现方式。
        19
    DavidNineRoc   70 天前
    @xlcoder166 这也是一个方案 +
    @nguoidiqua 对. 现在已经去掉子类属性,改成构造函数初始化属性了. 这两种语言的继承机制还有有点差距.
        20
    dvaknheo   69 天前
    @random0O
    怪不得 JavaBean 这么流行

    String getName(){ return this.name; } 就解决了?
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2219 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 25ms · UTC 03:09 · PVG 11:09 · LAX 20:09 · JFK 23:09
    ♥ Do have faith in what you're doing.