4.1 继承
继承就是把几个类的公共成员收拢到一个父类,子类用一个 extends 全部白得——它是面向对象三大特性的第二张牌,也是 3.3~3.5 地基的第一次兑现。
1. 为什么需要继承:先看痛点
第三章你写过 Car 类。现在让你再写 Dog、Cat、Bird 三个类,会发现它们天天抄同一份作业:
class Dog {
String name;
int age;
void eat() { System.out.println(name + "在吃饭"); }
void sleep(){ System.out.println(name + "在睡觉"); }
void bark() { System.out.println(name + "汪汪叫"); } // 独有
}
class Cat {
String name; // 又抄一遍
int age; // 又抄一遍
void eat() {...} // 又抄一遍
void sleep(){...} // 又抄一遍
void catchMouse() { ... } // 独有
}
三个坑:
- 抄三遍:同样的代码出现三次,文件越来越厚;
- 改三处:需求变了(比如给 eat 加个日志),三个类挨个改,漏一处就出 bug——这正好是 3.3 说的"把变化圈在一处"的反面教材;
- 不成体系:代码读不出来"猫和狗都是动物"这个常识。
继承就是解药:把公共部分抽成一个 Animal(父类),Dog/Cat/Bird 各自 extends 它,抄写变成"白得"。
2. extends 语法:五分钟上手
// 父类:装公共成员
class Animal {
String name;
int age;
void eat() { System.out.println(name + "在吃饭"); }
void sleep(){ System.out.println(name + "在睡觉"); }
}
// 子类:只写自己的绝活
class Dog extends Animal {
void bark() { System.out.println(name + "汪汪叫"); }
// name、age、eat()、sleep() 一个字没写,全都能用!
}
// 测试
Animal a = new Animal();
a.name = "动物";
a.eat(); // 动物在吃饭
Dog d = new Dog();
d.name = "旺财"; // 白得的属性,直接用
d.eat(); // 白得的方法,直接喊 → 旺财在吃饭
d.bark(); // 自己的绝活 → 旺财汪汪叫
术语对照(教材叫法不一,都认识):
| 叫法A | 叫法B | 谁是谁 |
|---|---|---|
| 父类 | 超类 / 基类 | Animal,被继承的 |
| 子类 | 派生类 | Dog,继承别人的 |
读法:Dog extends Animal = "狗是一种动物"——is-a 关系。能不能用继承,拿这句话检验:能自然念成"X 是一种 Y",才继承;"汽车是一种引擎"就念不通,那不是继承,是"包含"(下一章的组合)。
3. 继承了什么、没继承什么(本节考试中心)
- public / 默认权限的属性和方法 → 白得,直接用
- private 成员 → 被"继承"了但子类不能直接访问——锁还在(3.3 的 private 就是干这个的),子类想动只能走 getter/setter 这两扇门
- 构造方法不被继承——构造方法是"造这个类的专用流水线",Dog 的出生流程和 Animal 不同,所以不传。但子类构造时会先找父类构造报到,这是 4.2/4.3 的内容,先混脸熟
class Animal {
private int secret = 42; // private 上锁
public int getSecret() { // 门卫
return secret;
}
}
class Dog extends Animal {
void test() {
// System.out.println(secret); // 编译报错!锁着呢
System.out.println(getSecret()); // 走门卫 → 42
}
}
看到没?继承和封装不打架,反而配合默契:private 挡住子类的手,getter/setter 开受控的门。3.3 的努力在这里全部生效。
4. 三条规矩(记牢,考试常客)
规矩 1:Java 是单继承
class Dog extends Animal { } // ✅ 一个爹
class Dog extends Animal, Robot { } // ❌ 编译报错:不能有两个直接爹
为什么?两个爹各有一个 eat(),你听谁的?——多继承的冲突问题。Java 用"单继承 + 接口"(后面章节)绕开这个坑。C++ 支持多继承,代价是著名的"钻石问题",Java 不背这个锅。
规矩 2:可以多层继承(爷孙链)
Animal(爷爷)
↓ extends
Dog(爸爸)
↓ extends
GuideDog(导盲犬,孙子)
GuideDog 白得 Animal 和 Dog 的全部非 private 成员——属性一代代传下去。但 Java 不支持循环继承(A 继承 B、B 又继承 A,编译器直接报错)。
规矩 3:Object 是万物之祖
你写的任何类,不写 extends 时默认继承 Object。所以每个对象都有 toString()、equals() 这些方法——你没写过它们却能调用,谜底在这。继承链的最顶端永远是 Object。
5. 继承在内存里长什么样
延续全章的"栈钥匙堆实物"暗线:
Dog d = new Dog();
↓
栈:d(一把钥匙)
↓ 指向
堆:一个 Dog 对象
├─ 独有部分:bark() 相关
└─ 继承部分:name、age(从 Animal 图纸上"抄"进这个对象的格子里)
注意关键点:继承不是"链接到父类",而是 new 的时候把父类的成员直接搬进这个对象的格子里。改 d.name 只改这一只狗,Animal 类本身和别的对象毫发无伤。
6. 什么时候该用继承(动手前的判断课)
用 is-a 三问:
- 能不能自然念出"子类是一种父类"?(狗是一种动物 ✅ / 汽车是一种引擎 ❌)
- 子类是父类的特殊版本吗?(子类 = 父类 + 自己的绝活)
- 这些公共成员以后都会变吗?会变 → 抽到父类只改一处(3.3 的"圈住变化")
三个都答"是",才动手继承。继承不是"代码放哪方便"的问题,是"世界怎么分类"的问题——这也是它叫"面向对象思想"的原因。
7. 动手练习(保姆式,在 IDEA04 项目里)
① 新建项目 IDEA04(步骤同 1.5:File → New → Project → Java,JDK 选 17)
② src 上右键 → New → Java Class → 输入 Animal,把第 2 节的 Animal 类抄进去
③ 同法建 Dog 类,只抄 extends Animal 那两行
④ 同法建 TestAnimal,抄下面代码,点 main 旁边的绿三角 ▶
public class TestAnimal {
public static void main(String[] args) {
Dog d = new Dog();
d.name = "旺财";
d.age = 2;
d.eat(); // 旺财在吃饭 ← 一个字没写,凭空有了
d.sleep(); // 旺财在睡觉
d.bark(); // 旺财汪汪叫 ← 自己的绝活
}
}
成功标准:三行输出一字不差。特别是第一行——Dog 类里明明没有 eat(),它照样能跑,这就是"白得"的现场证据。
三个 30 秒实验(每个都值得做):
- private 实验:给 Animal 的 age 前加
private,回 Dog 类看d.age = 2报什么错——亲眼看"继承不等于随便访问"; - 单继承实验:再建一个
Robot类,让 Dog 写成extends Animal, Robot,看编译器怎么拒绝; - Object 实验:新建一个空类
class Empty { },在 Test 里敲new Empty().toString();——空类凭什么有方法?规矩 3 的现场证明。
8. 常见误区
更准确的说法:被继承了,但子类不能直接访问。成员确实进了子类对象(反射能摸到),只是 private 的锁还在。初学阶段记"private 子类用不了,要走 getter/setter"就够。
不能。构造方法是"造这个类的专用流程",Dog 有 Dog 的出生方式。但子类构造会先调用父类构造(super 的活),4.3 见。
抽父类前先问 is-a。为了"省代码"硬把不相干的类接成父子(比如让 Tree extends House 因为都"有高度"),代码是省了,世界观塌了。宁可不继承,不可错继承。
Java 是单继承,没有"特殊写法"。多继承的需求后面用接口(一个类可实现多个接口)满足,那是别的机制,不是继承。
白得成员是编译期就确定的,运行时零开销。慢的从来不是继承,是写错的代码。
9. 自测题(先自己答,再展开)
能,前提是 Animal 的 name 不是 private。private 成员被继承但上锁,子类不能直接碰,只能走 getter/setter。
避免冲突:两个父类都有同名方法时,编译器无法裁决听谁的(C++ 的"钻石问题")。Java 的取舍:单继承保简单,多需求用接口补。
有——默认继承 Object。所以它天生拥有 toString()、equals() 等方法。所有类的继承链顶端都是 Object。
① GuideDog extends Dog,Dog extends Animal → GuideDog 能用 Animal 的成员
② Dog 能同时继承 Animal 和 Robot
③ 子类能直接访问父类的 private 属性
①成立(多层继承,爷孙链上的都白得);②错(单继承);③错(private 上锁,走门卫)。
is-a 口诀:能自然念成"子是一种父"才继承。"轮胎是一种汽车" ❌ ——念不通,轮胎和汽车是"整体-零件"关系,不该继承。真正成立的是"跑车是一种汽车"。
10. 下一步学什么
- ☑
4.2 final关键字✅ 教材把 final 排在重写之前(合理:先知道"哪些不许改"):三把锁——final 变量/方法/类,static final 常量标配,final 锁钥匙不锁车 - ☐ 复习提示:本节的 private 实验(实验 1)是理解 4.2 重写边界的前提——先做实验再往下走
相关笔记
- 3.3 封装性 ← private 门卫:本节"继承不越权"的地基
- 3.5 this关键字 ← 下一节 super 是它的镜像:this 找自己,super 找爹
- 3.8 本章小结 ← 三大特性进度条:封装 ✅ → 继承(本节) → 多态待学
- 📇 Java基础 · 索引
- 🏠 返回主页