Java 中继承时,成员变量的访问规则跟方法完全不同,是初学者最容易混淆的地方之一。
核心一句话:
成员变量访问遵循“就近原则 + 编译期静态绑定”
(子类有同名变量 → 直接用子类的;没有才用父类的;this 和 super 是强制指定访问范围的工具)
1. 成员变量 vs 方法 的本质区别(最关键对比)
| 维度 | 成员变量(字段) | 成员方法(非静态) |
|---|---|---|
| 同名时叫什么 | 隐藏(hiding) | 重写(overriding) |
| 决定用哪个的时机 | 编译期(看引用类型) | 运行期(看实际对象类型) |
| 多态下表现 | 看左边(引用类型) | 看右边(实际类型) |
| 能否通过 super | 可以访问父类被隐藏的变量 | 可以调用父类被重写的方法 |
| this.字段 | 访问当前类声明的字段(就近) | — |
| super.字段 | 强制访问直接父类声明的字段 | 强制调用直接父类的方法 |
2. 就近原则 + this + super 规则详解
规则总结(子类中访问成员变量时):
- 直接写变量名(如
age)
→ 找当前类(子类)有没有声明age
→ 有 → 用子类的
→ 没有 → 去父类找(递归向上,直到 Object) this.age
→ 强制找当前类(子类)有没有声明age
→ 有 → 用子类的
→ 没有 → 编译错误(this 不会向上找)super.age
→ 强制找直接父类有没有声明age
→ 有 → 用父类的
→ 没有 → 继续向上找爷爷类(super 也会向上递归,直到找到或报错)
3. 经典代码示例(强烈建议自己运行验证)
class Father {
String name = "父亲";
int age = 50;
String hobby = "钓鱼";
}
class Son extends Father {
String name = "儿子"; // 隐藏了父类的 name
int age = 20; // 隐藏了父类的 age
// hobby 没有重新声明,继承父类的
void printAll() {
System.out.println("直接写变量名:");
System.out.println(name); // 儿子(就近 → 子类自己的)
System.out.println(age); // 20
System.out.println(hobby); // 钓鱼(子类没有 → 父类的)
System.out.println("\nthis. 强制当前类:");
System.out.println(this.name); // 儿子
System.out.println(this.age); // 20
// this.hobby // 编译错误!当前类没声明 hobby,this 不向上找
System.out.println("\nsuper. 强制直接父类:");
System.out.println(super.name); // 父亲
System.out.println(super.age); // 50
System.out.println(super.hobby);// 钓鱼
}
}
public class Test {
public static void main(String[] args) {
Son s = new Son();
s.printAll();
// 多态引用(最容易错的地方)
Father f = new Son(); // 父类引用 → 子类对象
System.out.println("\n多态下:");
System.out.println(f.name); // 父亲!(看引用类型 Father)
System.out.println(f.age); // 50
System.out.println(f.hobby); // 钓鱼
// f 是 Father 类型,所以它看到的都是 Father 声明的字段
}
}
输出结果:
直接写变量名:
儿子
20
钓鱼
this. 强制当前类:
儿子
20
super. 强制直接父类:
父亲
50
钓鱼
多态下:
父亲
50
钓鱼
4. 常见面试/易错点速查
| 问题 | 正确答案 |
|---|---|
| 子类声明了同名变量,父类的变量还能访问吗? | 可以,用 super.变量名 |
this.变量名 一定访问子类的吗? | 是的,如果子类声明了;否则编译错误(this 不向上找) |
多态下 父引用.变量 访问的是谁的? | 父类的(编译期看引用类型 → 静态绑定) |
| 成员变量有方法重写那样的动态绑定吗? | 没有!成员变量永远是静态绑定(编译期决定) |
| private 成员变量会被子类隐藏吗? | 不会,子类根本看不到 private 成员,更谈不上隐藏 |
| static 成员变量呢? | 也是静态绑定,看引用类型;但 static 变量不建议用继承方式访问 |
5. 实际开发建议(经验之谈)
- 尽量避免 子类和父类出现同名成员变量(非常容易制造 bug)
- 如果业务上必须同名,优先用 getter/setter 访问,而不是直接访问字段
- 想明确访问父类成员 → 永远用 super.(清晰、可读性高)
- 想访问当前类自己的 → 用 this.(尤其局部变量遮蔽成员变量时)
- 代码审查时看到大量
super.xxx或同名字段 → 考虑是否设计有问题
一句话记住:
方法看右边(动态),字段看左边(静态)
就近原则只在直接写变量名时生效,this 锁当前类,super 锁直接父类
有哪种特殊情况还想再验证?比如:
- 多层继承(爷爷-父亲-儿子)同名变量怎么找
- static 变量 + 继承的访问规则
- 接口中的 default 方法 + 字段(Java 8+)
- 内部类中 this 和 super 的特殊用法
随时追问~