1. 引言

本文将深入探讨 Java 中的变量隐藏(Variable Hiding)方法隐藏(Method Hiding)机制。

这两个概念虽然不常被提及,但在继承和作用域处理中非常关键。理解它们有助于避免一些“踩坑”行为,尤其是在维护大型项目或阅读他人代码时。我们将通过清晰的示例,剖析其工作原理,并对比相似但本质不同的“方法重写(Overriding)”。

2. 变量隐藏

变量隐藏指的是:当一个作用域内的变量名与外层作用域的变量名相同时,内层变量会“遮蔽”外层变量,导致外层变量在当前作用域中不可见——这种现象也常被称为“变量遮蔽(Shadowing)”。

在 Java 中,变量的作用域主要分为三类:

  • 局部变量(Local Variables):定义在方法、构造器或代码块内的变量
  • 实例变量(Instance Variables):定义在类中、但不属于任何方法,属于对象实例
  • 类变量 / 静态变量(Static Variables):用 static 修饰,属于类本身,而非实例

接下来我们通过具体例子来说明不同场景下的变量隐藏。

2.1 局部变量的“遮蔽力”

看下面这个 HideVariable 类:

public class HideVariable {

    private String message = "this is instance variable";

    HideVariable() {
        String message = "constructor local variable";
        System.out.println(message);
    }

    public void printLocalVariable() {
        String message = "method local variable";
        System.out.println(message);
    }

    public void printInstanceVariable() {
        String message = "method local variable";
        System.out.println(this.message);
    }
}

在这个类中,message 这个名字被用了四次:

  • 一次是实例变量
  • 三次是局部变量(构造器、两个方法中)

✅ 在 printLocalVariable() 中,System.out.println(message) 打印的是局部变量。
✅ 在 printInstanceVariable() 中,虽然局部变量也叫 message,但我们通过 this.message 显式访问了实例变量。

测试代码:

HideVariable variable = new HideVariable();
variable.printLocalVariable();
variable.printInstanceVariable();

输出结果:

constructor local variable
method local variable
this is instance variable

⚠️ 关键点:一旦局部变量与实例变量同名,实例变量就被“遮蔽”了,必须用 this 才能访问。否则,默认使用的是局部变量。

2.2 继承中的变量隐藏

当子类和父类拥有同名的实例变量时,子类的变量会隐藏父类的变量。

父类定义如下:

public class ParentVariable {

    String instanceVariable = "parent variable";

    public void printInstanceVariable() {
        System.out.println(instanceVariable);
    }
}

子类定义:

public class ChildVariable extends ParentVariable {

    String instanceVariable = "child variable";

    public void printInstanceVariable() {
        System.out.println(instanceVariable);
    }
}

测试代码:

ParentVariable parentVariable = new ParentVariable();
ParentVariable childVariable = new ChildVariable();

parentVariable.printInstanceVariable();
childVariable.printInstanceVariable();

输出:

parent variable
child variable

看起来很正常?但这里有个大坑 ❌:

  • childVariableParentVariable 类型,指向 ChildVariable 实例
  • 调用的是子类重写的 printInstanceVariable() 方法
  • 所以打印的是子类自己的 instanceVariable

⚠️ 重点来了:变量没有多态性!即使你用父类引用指向子类对象,变量的访问是在编译期决定的,基于引用类型

举个例子:

System.out.println(childVariable.instanceVariable); // 输出 "parent variable" ❗

因为 childVariable 的**引用类型是 ParentVariable**,所以访问的是父类的变量,而不是子类的!

✅ 正确做法:避免在父子类中使用同名变量。应该使用 private 封装,并通过 getter/setter 访问,这才是面向对象的正道。

3. 方法隐藏

方法隐藏发生在静态方法的继承场景中。

当子类定义了一个与父类静态方法同名、同签名的方法时,子类的方法会“隐藏”父类的方法——这叫 Method Hiding

注意:这和实例方法的 Override(重写) 完全不是一回事。

先看父类:

public class BaseMethodClass {

    public static void printMessage() {
        System.out.println("base static method");
    }
}

子类:

public class ChildMethodClass extends BaseMethodClass {

    public static void printMessage() {
        System.out.println("child static method");
    }
}

调用:

ChildMethodClass.printMessage();

输出:

child static method

看起来像多态?其实不是。

3.1 方法隐藏 vs 方法重写

特性 方法隐藏(Hiding) 方法重写(Overriding)
作用于 static 方法 实例方法
绑定时机 编译期(静态绑定) 运行期(动态绑定)
多态支持 ❌ 不支持 ✅ 支持
调用依据 引用类型 实际对象类型
关键字 无特殊要求 @Override 推荐使用

举个例子说明区别:

BaseMethodClass childRef = new ChildMethodClass();
childRef.printMessage(); // 输出: base static method

⚠️ 即使实际对象是 ChildMethodClass,但因为 printMessage() 是静态方法,调用的是引用类型 BaseMethodClass 的静态方法,所以输出的是父类的内容!

而如果是实例方法重写:

// 假设 printInstanceMessage 是实例方法且被重写
baseRef.printInstanceMessage(); // 输出子类内容,因为是动态绑定

✅ 简单粗暴总结:

  • 静态方法看“左边”(引用类型)
  • 实例方法看“右边”(实际对象类型)

4. 总结

本文系统梳理了 Java 中的变量隐藏和方法隐藏机制:

  • 变量隐藏:发生在同名变量在不同作用域或继承层级中,优先使用内层或子类变量,但访问受引用类型限制
  • 方法隐藏:仅针对 static 方法,子类同名静态方法会隐藏父类方法,且绑定发生在编译期
  • 避免踩坑:不要在父子类中定义同名变量;理解静态方法无多态性
  • 最佳实践:优先使用 private + getter/setter 封装变量;用 @Override 明确标识重写

所有示例代码已上传至 GitHub:https://github.com/yourname/java-hiding-examples


原始标题:Variable and Method Hiding in Java