在 Java 中,情况与 C++ 非常相似 ,但也存在一个关键的语法区别 (关键字不同)和一个根本的机制区别(访问权限)。
核心结论速查表
| 特性 | C++ | Java |
|---|---|---|
| 现象名称 | 变量隐藏 (Hiding) | 变量隐藏 (Hiding) / 遮蔽 (Shadowing) |
| 内存情况 | 父类和子类各存一份,互不干扰 | 父类和子类各存一份,互不干扰 |
| 访问子类变量 | obj.var 或 ptr->var |
obj.var 或 this.var |
| 访问父类变量 | obj.Parent::var |
super.var (关键区别!) |
| 多态性 | ❌ 无 (静态绑定,看引用类型) | ❌ 无 (静态绑定,看引用类型) |
| 能否通过父类引用访问子类变量? | ❌ 不能 (编译报错) | ❌ 不能 (编译报错) |
1. 代码实战演示
假设我们有以下类结构:
java
class Parent {
int value = 100; // 父类变量
}
class Child extends Parent {
int value = 200; // 子类变量 (隐藏了父类的 value)
public void show() {
// 1. 访问子类变量 (默认)
System.out.println("Child value: " + this.value); // 输出 200
System.out.println("Child value: " + value); // 输出 200 (省略 this)
// 2. 访问父类变量 (关键语法)
System.out.println("Parent value: " + super.value); // 输出 100
}
}
public class Main {
public static void main(String[] args) {
Child obj = new Child();
Child cRef = obj; // 子类引用
Parent pRef = obj; // 父类引用 (指向同一个对象)
System.out.println("=== 1. 通过子类引用访问 ===");
System.out.println("cRef.value: " + cRef.value); // 输出 200 (看引用类型 Child)
System.out.println("\n=== 2. 通过父类引用访问 ===");
System.out.println("pRef.value: " + pRef.value); // 输出 100 (看引用类型 Parent)
// pRef.super.value; // ❌ 错误!外部代码不能使用 super 关键字
// pRef.Child::value; // ❌ 错误!Java 没有 :: 语法
// 如果非要通过父类引用访问子类变量,需要强制类型转换
System.out.println("((Child)pRef).value: " + ((Child)pRef).value); // 输出 200
}
}
2. 关键区别解析
A. 语法关键字:super vs ::
这是最直观的区别:
- C++ :使用作用域解析运算符
::。- 写法:
obj.Parent::value或ptr->Parent::value。 - 逻辑:明确指定"我要
Parent命名空间下的value"。
- 写法:
- Java :使用
super关键字。- 写法:
super.value。 - 限制:
super只能在子类的方法内部使用 。你不能在Main类或其他外部类中写obj.super.value,这会编译报错。外部只能通过强制类型转换((Child)obj).value来访问子类变量,或者根本无法直接访问被隐藏的父类变量(除非父类提供了 getter 方法)。
- 写法:
B. 访问权限的严格性
- C++ :只要继承方式是
public或protected,且成员变量不是private,子类就可以通过Parent::value访问父类变量。 - Java :
- 如果父类变量是
private:子类完全无法访问 ,连super.value都会报错。必须通过父类的public/protected方法(如getValue())访问。 - 如果父类变量是
default(包私有):只有当子类和父类在同一个包下时,才能访问。 - 如果父类变量是
protected或public:可以通过super.value访问。
- 如果父类变量是
C. "多态"的缺失 (与 C++ 一致)
这一点 Java 和 C++ 完全一样 :成员变量没有多态性。
- 无论你实际创建的是
new Child()还是new Parent()。 - 编译器只看引用变量的声明类型 。
Parent p = new Child();->p.value永远访问父类的那份数据 (100)。Child c = new Child();->c.value永远访问子类的那份数据 (200)。
- 这与方法(Method)的重写(Override)完全不同,方法是根据实际对象类型动态绑定的。
3. 如何在外部访问被隐藏的父类变量?
在 Jav中,如果你在 main 方法里有一个 Parent 类型的引用指向 Child 对象,你想访问父类的那个 value:
- 直接访问 :
pRef.value-> 得到的就是父类的值 (100)。这通常就是你想要的。 - 如果你想访问子类的值 :需要强转
((Child)pRef).value-> 得到 200。 - 如果你想在子类方法内部访问父类值 :使用
super.value。
注意 :Java 不支持 像 C++ 那样在外部通过 obj.Parent::value 这种语法来"穿透"子类的遮挡去显式读取父类变量(虽然在这个特定场景下,用父类引用 pRef 直接读到的本来就是父类变量,所以通常不需要这种语法)。
总结
| 你的需求 | C++ 写法 | Java 写法 |
|---|---|---|
| 在子类方法内访问父类变量 | Parent::value |
super.value |
| 在外部通过对象访问父类变量 | obj.Parent::value |
无法直接写 (需用父类类型引用 parentRef.value) |
| 在外部通过对象访问子类变量 | obj.value (需子类指针) |
obj.value (需子类类型引用) |
| 核心机制 | 静态绑定 (看引用类型) | 静态绑定 (看引用类型) |
一句话记住:
Java 中变量也是隐藏 而非重写,没有多态。在子类内部用 super.变量名 访问父类变量;在外部则完全取决于你手里拿的是父类引用 还是子类引用。
拥有子类的引用或者指针,在c++中能通过作用域限定访问父类的变量,而java中只能访问子类变量