目录
- 🧬 13 继承
- 📑 目录
- 13.1 为什么需要继承
- 13.2 继承的语法
- 13.2.1 基本语法
- 13.2.2 示例:人 → 老师/学生
- 13.2.3 测试
- 13.3 继承中成员变量的特点
- 13.3.1 变量访问规则
- 13.3.2 三种变量对比
- 13.4 方法重写(Override)
- 13.4.1 什么是方法重写
- 13.4.2 重写的规则
- 13.4.3 重写 vs 重载
- 13.4.4 重写的应用
- 13.5 super关键字
- 13.5.1 super的用法
- 13.5.2 示例
- 13.6 继承中构造方法的特点
- 13.6.1 子类构造方法会调用父类构造方法
- 13.6.2 构造方法的执行顺序
- 13.6.3 使用super(参数)调用父类有参构造
- 13.7 Object类
- 13.7.1 Object是什么
- 13.7.2 Object的常用方法
- 13.7.3 重写toString()
- 13.7.4 重写equals()
- 13.8 继承的注意事项
- 13.8.1 Java只支持单继承
- 13.8.2 继承的使用场景
- 13.8.3 访问修饰符总结
- 13.9 综合案例:动物世界
- 13.10 本章总结
- 知识回顾
- 练习题
- 💬 互动时间
- 📚 参考资料
🧬 13 继承
更新日期:2026年5月
版权声明:本文为原创内容,转载请注明出处。
系列:Java入门到精通系列 · 第二阶段 · 面向对象
📑 目录
- 13.1 为什么需要继承
- 13.2 继承的语法
- 13.3 继承中成员变量的特点
- 13.4 方法重写(Override)
- 13.5 super关键字
- 13.6 继承中构造方法的特点
- 13.7 Object类
- 13.8 继承的注意事项
- 13.9 综合案例:动物世界
- 13.10 本章总结
13.1 为什么需要继承
假设我们要设计一个学校管理系统,需要管理老师和学生:
// 老师类publicclassTeacher{privateStringname;privateintage;privateStringsubject;publicvoidintroduce(){System.out.println("我是老师,叫"+name+",今年"+age+"岁");}}// 学生类publicclassStudent{privateStringname;privateintage;privateStringstudentId;publicvoidintroduce(){System.out.println("我是学生,叫"+name+",今年"+age+"岁");}}问题:name、age、introduce()在两个类中完全重复!
继承的出现就是为了解决这种代码重复问题。将公共代码提取到一个父类中,子类自动获得父类的属性和方法。
13.2 继承的语法
13.2.1 基本语法
// 父类(基类/超类)publicclass父类名{// 公共属性和方法}// 子类(派生类)publicclass子类名extends父类名{// 子类特有的属性和方法}13.2.2 示例:人 → 老师/学生
// 父类:PersonpublicclassPerson{protectedStringname;// protected:子类可访问protectedintage;publicPerson(){}publicPerson(Stringname,intage){this.name=name;this.age=age;}publicvoidintroduce(){System.out.println("我叫"+name+",今年"+age+"岁");}publicvoideat(){System.out.println(name+"正在吃饭");}publicvoidsleep(){System.out.println(name+"正在睡觉");}}// 子类:TeacherpublicclassTeacherextendsPerson{privateStringsubject;publicTeacher(){}publicTeacher(Stringname,intage,Stringsubject){super(name,age);// 调用父类构造方法this.subject=subject;}publicvoidteach(){System.out.println(name+"正在教"+subject);}}// 子类:StudentpublicclassStudentextendsPerson{privateStringstudentId;publicStudent(){}publicStudent(Stringname,intage,StringstudentId){super(name,age);this.studentId=studentId;}publicvoidstudy(){System.out.println(name+"正在学习,学号:"+studentId);}}13.2.3 测试
publicclassInheritTest{publicstaticvoidmain(String[]args){Teachert=newTeacher("王老师",35,"Java");t.introduce();// 继承自Person:我叫王老师,今年35岁t.eat();// 继承自Person:王老师正在吃饭t.teach();// 自己的方法:王老师正在教JavaSystem.out.println("------");Students=newStudent("张三",20,"2026001");s.introduce();// 继承自Person:我叫张三,今年20岁s.sleep();// 继承自Person:张三正在睡觉s.study();// 自己的方法:张三正在学习,学号:2026001}}13.3 继承中成员变量的特点
13.3.1 变量访问规则
当子类和父类有同名变量时,遵循就近原则:
publicclassFu{intnum=10;}publicclassZiextendsFu{intnum=20;publicvoidshow(){intnum=30;System.out.println(num);// 30(局部变量)System.out.println(this.num);// 20(本类成员变量)System.out.println(super.num);// 10(父类成员变量)}}13.3.2 三种变量对比
| 关键字 | 含义 | 示例 |
|---|---|---|
num | 就近原则,优先局部 | System.out.println(num) |
this.num | 本类成员变量 | System.out.println(this.num) |
super.num | 父类成员变量 | System.out.println(super.num) |
13.4 方法重写(Override)
13.4.1 什么是方法重写
子类对父类中已有方法进行重新实现,方法名、参数列表、返回类型必须相同。
publicclassPerson{publicvoidintroduce(){System.out.println("我是一个人");}}publicclassTeacherextendsPerson{@Override// 注解:标识这是一个重写方法publicvoidintroduce(){System.out.println("我是一名老师");}}publicclassStudentextendsPerson{@Overridepublicvoidintroduce(){System.out.println("我是一名学生");}}13.4.2 重写的规则
| 规则 | 说明 |
|---|---|
| 方法名 | 必须相同 |
| 参数列表 | 必须相同 |
| 返回值类型 | 必须相同(或为其子类型) |
| 访问权限 | 不能比父类更严格(可以更宽松) |
| @Override | 建议加上,编译器会检查是否正确重写 |
13.4.3 重写 vs 重载
| 对比 | 重写(Override) | 重载(Overload) |
|---|---|---|
| 位置 | 子类对父类 | 同一个类中 |
| 方法名 | 相同 | 相同 |
| 参数列表 | 相同 | 不同 |
| 返回值 | 相同 | 无要求 |
| 访问权限 | 不能更严格 | 无要求 |
| 目的 | 覆盖父类行为 | 提供多种调用方式 |
13.4.4 重写的应用
publicclassAnimal{publicvoidspeak(){System.out.println("动物发出声音");}publicStringtoString(){return"Animal{}";}}publicclassDogextendsAnimal{@Overridepublicvoidspeak(){System.out.println("汪汪汪!");}@OverridepublicStringtoString(){return"Dog{}";}}publicclassCatextendsAnimal{@Overridepublicvoidspeak(){System.out.println("喵喵喵~");}}13.5 super关键字
super代表父类对象的引用。
13.5.1 super的用法
| 用法 | 说明 | 示例 |
|---|---|---|
super.属性 | 访问父类成员变量 | super.name |
super.方法() | 调用父类方法 | super.introduce() |
super(参数) | 调用父类构造方法 | super("张三", 20) |
13.5.2 示例
publicclassTeacherextendsPerson{privateStringsubject;@Overridepublicvoidintroduce(){super.introduce();// 先调用父类的方法System.out.println("我教的科目是:"+subject);}}输出:
我叫王老师,今年35岁 我教的科目是:Java13.6 继承中构造方法的特点
13.6.1 子类构造方法会调用父类构造方法
publicclassFu{publicFu(){System.out.println("Fu的无参构造被调用");}}publicclassZiextendsFu{publicZi(){// 隐藏了一行:super();System.out.println("Zi的无参构造被调用");}}Ziz=newZi();输出:
Fu的无参构造被调用 Zi的无参构造被调用13.6.2 构造方法的执行顺序
new Zi() 调用流程: 1. → Zi() 的第一行隐含 super() 2. → Fu() 先执行 3. → Fu() 执行完毕后,Zi() 继续执行📌规则:子类构造方法中,第一行必须调用父类构造方法。如果不写,编译器自动加
super()(无参)。
13.6.3 使用super(参数)调用父类有参构造
publicclassFu{publicFu(){System.out.println("Fu无参构造");}publicFu(Stringname){System.out.println("Fu有参构造:"+name);}}publicclassZiextendsFu{publicZi(){super("张三");// 显式调用父类有参构造System.out.println("Zi无参构造");}}newZi();输出:
Fu有参构造:张三 Zi无参构造⚠️ 注意:
this()和super()不能同时出现,因为它们都必须在第一行。
13.7 Object类
13.7.1 Object是什么
java.lang.Object是所有Java类的根基类。如果一个类没有显式继承任何类,则默认继承Object。
// 以下两种写法等价publicclassStudent{}publicclassStudentextendsObject{}13.7.2 Object的常用方法
| 方法 | 说明 | 用途 |
|---|---|---|
toString() | 返回对象的字符串表示 | 打印对象信息 |
equals(Object obj) | 比较对象是否相等 | 对象比较 |
hashCode() | 返回对象的哈希值 | 集合中使用 |
getClass() | 返回对象的运行时类 | 反射 |
clone() | 克隆对象 | 对象复制 |
finalize() | GC回收前调用 | 已废弃 |
13.7.3 重写toString()
publicclassStudent{privateStringname;privateintage;publicStudent(Stringname,intage){this.name=name;this.age=age;}// 不重写toString:输出地址值如 Student@1b6d3586// 重写后:@OverridepublicStringtoString(){return"Student{name='"+name+"', age="+age+"}";}}Students=newStudent("张三",20);System.out.println(s);// 自动调用toString()// 输出:Student{name='张三', age=20}13.7.4 重写equals()
@Overridepublicbooleanequals(Objectobj){if(this==obj)returntrue;if(obj==null||getClass()!=obj.getClass())returnfalse;Studentother=(Student)obj;returnthis.age==other.age&&this.name.equals(other.name);}| 比较方式 | 说明 |
|---|---|
== | 基本类型比较值,引用类型比较地址 |
equals() | 默认比较地址,可重写为比较内容 |
13.8 继承的注意事项
13.8.1 Java只支持单继承
// ❌ 错误!Java不支持多继承publicclassZiextendsFu1,Fu2{}但支持多层继承:
publicclassAnimal{}publicclassDogextendsAnimal{}publicclassHuskyextendsDog{}// Husky → Dog → Animal13.8.2 继承的使用场景
| 适合继承 | 不适合继承 |
|---|---|
| is-a 关系(Dog is a Animal) | has-a 关系(Car has a Engine) |
| 子类是父类的一种 | 两者只是关联关系 |
| 需要复用父类代码 | 没有公共代码需要复用 |
13.8.3 访问修饰符总结
| 修饰符 | 同类 | 同包子类 | 不同包子类 | 不同包非子类 |
|---|---|---|---|---|
| public | ✅ | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ✅ | ❌ |
| 默认 | ✅ | ✅ | ❌ | ❌ |
| private | ✅ | ❌ | ❌ | ❌ |
13.9 综合案例:动物世界
// 父类publicclassAnimal{protectedStringname;protectedintage;publicAnimal(){}publicAnimal(Stringname,intage){this.name=name;this.age=age;}publicvoideat(){System.out.println(name+"正在吃东西");}publicvoidsleep(){System.out.println(name+"正在睡觉");}@OverridepublicStringtoString(){returnthis.getClass().getSimpleName()+"{name='"+name+"', age="+age+"}";}}// 子类:狗publicclassDogextendsAnimal{privateStringbreed;publicDog(Stringname,intage,Stringbreed){super(name,age);this.breed=breed;}publicvoidbark(){System.out.println(name+"("+breed+")汪汪汪!");}publicvoidfetch(){System.out.println(name+"去捡球了!");}@Overridepublicvoideat(){System.out.println(name+"正在吃狗粮");}}// 子类:猫publicclassCatextendsAnimal{privatebooleanisIndoor;publicCat(Stringname,intage,booleanisIndoor){super(name,age);this.isIndoor=isIndoor;}publicvoidmeow(){System.out.println(name+"喵喵喵~");}publicvoidscratch(){System.out.println(name+"在抓沙发!");}@Overridepublicvoideat(){System.out.println(name+"正在吃猫粮");}}// 子类:鸟publicclassBirdextendsAnimal{privatebooleancanFly;publicBird(Stringname,intage,booleancanFly){super(name,age);this.canFly=canFly;}publicvoidsing(){System.out.println(name+"在唱歌:叽叽喳喳!");}publicvoidfly(){if(canFly){System.out.println(name+"展翅高飞!");}else{System.out.println(name+"不会飞...");}}}测试类:
publicclassAnimalTest{publicstaticvoidmain(String[]args){Dogdog=newDog("旺财",3,"金毛");Catcat=newCat("咪咪",2,true);Birdbird=newBird("小黄",1,true);Animal[]animals={dog,cat,bird};System.out.println("===== 动物园 =====");for(Animala:animals){System.out.println(a);// 调用toStringa.eat();// 调用重写后的eata.sleep();// 继承父类的sleepSystem.out.println("---");}// 子类特有方法dog.bark();dog.fetch();cat.meow();bird.fly();}}13.10 本章总结
知识回顾
| 知识点 | 核心内容 |
|---|---|
| 继承 | 子类 extends 父类,获得父类属性和方法 |
| 方法重写 | 子类重新实现父类的方法,加@Override |
| super | 访问父类成员变量、方法、构造方法 |
| 构造方法 | 子类构造默认先调用super(),先父后子 |
| Object类 | 所有类的根基,常用toString和equals |
| 单继承 | Java只支持单继承,但支持多层继承 |
练习题
- 设计一个
Shape父类,包含getArea()方法。创建Circle和Rectangle子类分别实现计算面积。 - 重写
equals()和toString()方法,使其比较和显示对象内容。
💬 互动时间
- Java为什么不支持多继承?这样设计有什么好处?
- 方法重写和方法重载的区别是什么?
📢下篇预告:[14-多态]—— 学习面向对象最核心的特性:多态!
📚 参考资料
- Oracle Java 官方文档 - Inheritance
- 《Java核心技术 卷I》第5章
- 《Effective Java》第3版