JAVA: 子类“覆盖”父类的成员变量

问题描述

JAVA本身并不提供子类“覆盖”父类成员变量的方法,而事实上,从面相对象的角度上来说,子类也不应当可以“覆盖”父类的成员变量。但有时候我们就是有这种需求,比如:

public class Person {
    String name = "Person";

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

public class Dad extends Person {
    String name = "Dad";
}

Person dad = new Dad();
dad.printName();

希望打印出dad,但是实际上是person。

分析

实际上,即使子类声明了与父类完全一样的成员变量,也不会覆盖掉父类的成员变量。而是在子类实例化时,会同时定义两个成员变量,子类也可以同时访问到这两个成员变量,但父类不能访问到子类的成员变量(父类不知道子类的存在)而具体在方法中使用成员变量时,究竟使用的是父类还是子类的成员变量,则由方法所在的类决定;即,方法在父类中定义和执行,则使用父类的成员变量,方法在子类中定义(包括覆盖父类方法)和执行,则使用子类的成员变量。

解决方法一:使用get/set方法

public class Person {
    private String name = "person";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Dad extends Person {
    private String name = "Dad";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Person dad = new Dad();
System.out.println(dad.getName());

由于dad.getName()执行的是子类中重载父类的getName(),因此返回的也是子类中定义的name。这种方法最为推荐,但用起来也繁琐一些。因为这种方法同时维护了两个相同的成员变量,因此使用起来也得小心一些。

方法二:使用父类函数

public class Person {
    protected String name = "person";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Dad extends Person {
    private String hisName = "Dad";

    public Dad() {
        super.name = hisName;
    }
}

Person dad = new Dad();
System.out.println(dad.getName());

这种方法是在子类的构造函数上做文章。子类的hisName即子类自己的成员变量,但只在构造函数中使用,而在构造函数中就是通过super给父类的成员变量赋值。这样做的好处就是只有一个成员变量,没有出现真正的“覆盖”的问题,而且父类和子类中的方法也可以放心大胆用这个成员变量,不用担心隐藏的问题;坏处当然就是不太“正规”了。

方法三:通过static

public class Person {
    static String name = "person";

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Dad extends Person {
    static {
        name = "Dad";
    }
}

Person dad = new Dad();
System.out.println(dad.getName());

这个方法和上面的那个很像,但从原理上来说还是有些区别的。static块会在类初始化而不是实例化时被执行,而父类中的static成员变量会在子类static块执行前就定义完成,所以子类初始化时会修改父类的成员变量值,子类实例化时自然得到的父类成员变量值也是修改过的,这样完成了“覆盖”

注:像下面这种方法是错误的

public class Dad extends Person {
    name = "Dad";
}

JAVA中变量是不能在方法之外进行赋值操作的;而static块恰恰是利用了JAVA会无条件执行staitc块这一特性,达到了这个目的。这种方法说坏处的话,估计就是成员变量必须是static了。

此外,子类通过static块使用父类成员变量的潜在风险。

先看一个例子:

public class Person {
    public static String name = "person";
}

public class Dad extends Person {
    static {
        name = "Dad";
    }
}

public class Mom extends Person {
    static {
        name = "Mom";
    }
}

public static void main(String[] args) {
    System.out.println(Person.name);
    System.out.println(Dad.name);
    System.out.println(Mom.name);
    Person dad = new Dad();
    System.out.println(Person.name);
    System.out.println(Dad.name);
    System.out.println(Mom.name);
    Person mom = new Mom();
    System.out.println(Person.name);
    System.out.println(Dad.name);
    System.out.println(Mom.name);
}

输出:

person
person
person
Dad
Dad
Dad
Mom
Mom
Mom

分析:

如果单单只看这个简单例子,从static的含义来说,问题很清楚;因为static变量namePerson类是否实例化无关,当改变name值时会使得所有的Person类及其子类,以及实例中name值都发生变化,即name是共享的

static块会在类初始化时执行,并且整个程序运行过程中只执行一次,而static块中对name值的修改就会在初始化时发生。所以例子中在Dad类初始化时会修改nameDadMom类初始化时会修改nameMom,而在Person类及其子类中name值都相同。

潜在风险:应当注意在某个类有多个子类时,子类对父类的static成员变量是共享的,拿父类static成员变量存储本子类独有的数据时,很可能引发错误。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值