
在java面向对象编程中,继承是实现代码复用和多态性的核心机制。然而,如果不恰当使用,继承也可能引入一些不易察觉的问题,其中之一便是“变量遮蔽”(variable shadowing)。变量遮蔽发生在子类声明了一个与父类中已存在的实例变量同名且同类型的变量时。此时,子类中的同名变量会“遮蔽”父类中的变量,使得子类实例在访问该变量时,默认访问的是子类自己的变量,而不是父类的变量。这与方法重写(method overriding)不同,方法重写是运行时多态的体现,而变量遮蔽则是一种编译时绑定行为。
考虑一个旨在演示依赖反转原则(DIP)的开关系统。我们有一个抽象基类Switchable,它定义了设备的开关状态state以及turn_on()和turn_off()抽象方法。Lamp和Television是继承自Switchable的具体设备。PowerSwitch类负责通过调用Switchable接口的方法来控制设备。
初始代码结构如下:
// 定义设备状态枚举
public enum State {
on, off;
}
// 抽象基类:定义可切换设备的通用接口和状态
public abstract class Switchable {
public State state; // 基类中的状态变量
abstract public void turn_on();
abstract public void turn_off();
}
// Lamp 类:具体设备实现
public class Lamp extends Switchable {
public State state; // 子类中再次声明了同名状态变量,遮蔽了父类的state
public Lamp() {
state = State.off; // 初始化子类自己的state
}
public void turn_on() {
this.state = State.on; // 修改的是子类自己的state
System.out.println("lamp's on");
}
public void turn_off() {
this.state = State.off; // 修改的是子类自己的state
System.out.println("lamp's off");
}
}
// Television 类:另一个具体设备实现
public class Television extends Switchable {
public State state; // 子类中再次声明了同名状态变量
public Television() {
state = State.off;
}
public void turn_on() {
this.state = State.on;
System.out.println("lamp's on"); // 注意:这里也错误地打印了"lamp's on"
}
public void turn_off() {
this.state = State.off;
System.out.println("lamp's off"); // 注意:这里也错误地打印了"lamp's off"
}
}
// PowerSwitch 类:负责控制Switchable设备
public class PowerSwitch {
Switchable sw;
public PowerSwitch(Switchable sw) {
this.sw = sw;
}
public void ClickSwitch() {
// 这里访问的是Switchable引用sw所指向对象中继承自Switchable的state
if (sw.state == State.off) {
sw.turn_on();
} else {
sw.turn_off();
}
}
}
// 主函数:测试代码
public class Main {
public static void main(String[] args) {
Switchable sw = new Lamp();
PowerSwitch ps = new PowerSwitch(sw);
ps.ClickSwitch(); // 第一次点击
ps.ClickSwitch(); // 第二次点击
}
}在上述代码中,当我们执行Main方法并期望PowerSwitch能正确地切换Lamp的状态时,实际输出却是两次“lamp's off”。这是因为Lamp类内部再次声明了一个public State state;变量,它“遮蔽”了从Switchable基类继承的同名变量。
具体分析如下:
立即学习“Java免费学习笔记(深入)”;
结果是,PowerSwitch所依赖的state(基类变量)始终为null,从未被改变,因此每次都触发turn_off()。而Lamp内部的state(子类变量)虽然被修改,但PowerSwitch无法感知。这种逻辑上的断裂导致了程序行为与预期不符。
解决此问题的关键在于消除子类中的变量遮蔽。一个对象在继承体系中,对于同一个逻辑属性,应该只维护一个实例变量。正确的做法是让Lamp和Television类直接使用从Switchable基类继承的state变量,而不是重新声明一个。
修改后的代码示例如下:
以上就是Java继承中变量遮蔽的陷阱与解决方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号