参考资料:黑马程序员
1.1 概述
1.1.1 引入
在面向对象编程中,继承是一种重要的机制,它允许我们通过已有类创建新类,实现代码的复用和扩展。让我们通过一个具体案例来理解继承的必要性。
假设我们需要定义以下三个类:
- 学生类
- 属性:姓名、年龄
- 行为:吃饭、睡觉
- 老师类
- 属性:姓名、年龄、薪水
- 行为:吃饭、睡觉、教书
- 班主任类
- 属性:姓名、年龄、薪水
- 行为:吃饭、睡觉、管理
观察这三个类,我们可以发现它们都包含相同的属性和行为(姓名、年龄、吃饭、睡觉),这导致了代码重复。继承机制可以很好地解决这个问题。

1.1.2 继承的含义
继承描述的是事物之间的"is-a"关系,即子类是父类的一种特殊类型。例如:
- 学生是人(Student is a Human)
- 老师是人(Teacher is a Human)
- 班主任是人(HeadTeacher is a Human)
通过继承,我们可以:
- 将共性的属性和行为放在父类中
- 让子类继承这些共性
- 在子类中添加特有的属性和行为
1.1.3 继承的好处
- 代码复用:避免重复编写相同的代码
- 扩展性:可以在不修改父类的情况下扩展功能
- 维护性:修改父类可以影响所有子类,便于维护
- 多态基础:为多态的实现提供了可能
1.2 继承的格式
在Java中,使用extends关键字实现继承:
class ParentClass {
// 父类成员
}
class ChildClass extends ParentClass {
// 子类特有成员
}注意:
- Java是单继承语言,一个类只能直接继承一个父类;虽然不支持多继承,但是支持多层继承
- Java中所有的类都直接或者间接继承于object类
- 子类可以继承父类的非私有成员(属性和方法)
- 子类可以添加自己的特有成员
1.3 继承案例实现
1.3.1 类设计
根据前面的分析,我们可以设计如下类结构:
- Human类(父类)
- 属性:name, age
- 行为:eat(), sleep()
- Student类(子类)
- 继承Human的所有成员
- Teacher类(子类)
- 继承Human的所有成员
- 新增属性:salary
- 新增行为:teach()
- HeadTeacher类(子类)
- 继承Human的所有成员
- 新增属性:salary
- 新增行为:manage()
1.3.2 代码实现
1. 父类Human
public class Human {
private String name;
private int age;
// Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 通用行为
public void eat() {
System.out.println(name + "正在吃饭");
}
public void sleep() {
System.out.println(name + "正在睡觉");
}
}2. 子类Teacher
public class Teacher extends Human {
private double salary;
public void teach() {
System.out.println(getName() + "老师正在授课");
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}3. 子类HeadTeacher
public class HeadTeacher extends Human {
private double salary;
public void manage() {
System.out.println(getName() + "班主任正在管理班级");
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}4. 子类Student
public class Student extends Human {
// 可以直接使用父类的所有非私有成员
// 也可以添加特有的属性和方法
}5. 测试类
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.setName("张老师");
teacher.setAge(35);
teacher.setSalary(8000);
teacher.eat();
teacher.teach();
HeadTeacher headTeacher = new HeadTeacher();
headTeacher.setName("李主任");
headTeacher.setAge(40);
headTeacher.setSalary(10000);
headTeacher.sleep();
headTeacher.manage();
Student student = new Student();
student.setName("王同学");
student.setAge(18);
student.eat();
// student.teach(); // 错误!Student类没有teach方法
}
}1.4 继承的细节
1.4.1 子类继承的内容
- 构造方法:子类不能继承父类的构造方法
- 私有成员:子类可以继承父类的私有成员,但不能直接访问
- 成员方法:虚方法(所有非
private、非static、非final的实例方法默认都是虚方法)可以被继承
1.4.2 访问父类私有成员
虽然子类不能直接访问父类的私有成员,但可以通过父类提供的公共方法间接访问:
public class Parent {
private int privateField;
public int getPrivateField() {
return privateField;
}
public void setPrivateField(int value) {
this.privateField = value;
}
}
public class Child extends Parent {
public void accessParentField() {
// System.out.println(privateField); // 错误!不能直接访问
System.out.println(getPrivateField()); // 正确!通过公共方法访问
}
}1.5 成员变量继承
1.5.1 成员变量不重名
当子类和父类的成员变量不重名时,访问没有影响:
class Parent {
int parentField = 10;
}
class Child extends Parent {
int childField = 20;
void show() {
System.out.println("父类字段: " + parentField);
System.out.println("子类字段: " + childField);
}
}3.5.2 成员变量重名
当子类和父类的成员变量重名时,遵循"就近原则":
class Parent {
int field = 10;
}
class Child extends Parent {
int field = 20;
void show() {
System.out.println("子类字段: " + field); // 输出20
System.out.println("父类字段: " + super.field); // 输出10
}
}1.5.3 super关键字
super关键字用于显式访问父类的成员:
- 访问父类的成员变量:
super.variableName - 调用父类的方法:
super.methodName() - 调用父类的构造方法:
super()
示例:
class Parent {
String name = "Parent";
void show() {
System.out.println("Parent show");
}
}
class Child extends Parent {
String name = "Child";
void show() {
System.out.println("Child show");
super.show(); // 调用父类方法
System.out.println("Parent name: " + super.name); // 访问父类变量
}
}1.6成员方法继承
1.6.1 成员方法不重名的情况
当子类和父类中的成员方法不重名时,方法调用遵循以下规则:
- 子类对象调用方法时,首先在子类中查找对应方法
- 如果子类中不存在该方法,则向上到父类中查找
- 这种查找过程会沿着继承链一直向上,直到找到对应方法或抛出异常
class Parent {
public void parentMethod() {
System.out.println("父类方法执行");
}
}
class Child extends Parent {
public void childMethod() {
System.out.println("子类特有方法执行");
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
child.parentMethod(); // 调用继承自父类的方法
child.childMethod(); // 调用子类特有方法
}
}1.6.2 成员方法重名的情况(方法重写)
当子类和父类中出现重名方法时,子类方法会覆盖父类方法,这是面向对象中多态性的重要体现。
class Parent {
public void commonMethod() {
System.out.println("父类实现");
}
}
class Child extends Parent {
@Override
public void commonMethod() {
System.out.println("子类重写实现");
}
}
public class Test {
public static void main(String[] args) {
Child child = new Child();
child.commonMethod(); // 输出"子类重写实现"
}
}1.7 方法重写详解
1.7.1 方法重写的核心概念
方法重写(Override)是指子类重新定义父类中已有的方法,需要满足以下条件:
- 方法名、参数列表必须完全相同
- 返回类型可以是父类方法返回类型的子类(协变返回类型)
- 访问修饰符不能比父类更严格
- 不能重写被final修饰的方法
1.7.2 @Override注解的重要性
使用@Override注解可以带来以下好处:
- 编译器会检查是否确实重写了父类方法
- 提高代码可读性,明确表明这是重写的方法
- 防止因拼写错误导致意外创建新方法
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
class Cat extends Animal {
@Override // 明确表示这是重写方法
public void makeSound() {
System.out.println("喵喵叫");
}
}1.7.3 方法重写的注意事项
- 访问权限:子类重写方法的访问权限不能低于父类方法
- 父类方法为public,子类必须为public
- 父类方法为protected,子类可为protected或public
- 异常处理:
- 子类方法抛出的异常不能比父类方法更宽泛
- 可以选择不抛出异常或抛出更具体的异常
- 静态方法:静态方法不能被重写,只能被隐藏
1.8 继承中的构造方法特性
1.8.1 构造方法的继承规则
- 构造方法不可继承:子类不能继承父类的构造方法
- 隐式调用:子类构造方法首行会隐式调用父类无参构造方法(super())
- 显式调用:如果父类没有无参构造,必须显式调用父类有参构造
1.8.2 构造方法执行顺序示例
class Person {
private String name;
public Person(String name) {
this.name = name;
System.out.println("Person构造方法执行");
}
}
class Student extends Person {
private int grade;
public Student(String name, int grade) {
super(name); // 必须显式调用父类有参构造
this.grade = grade;
System.out.println("Student构造方法执行");
}
}
public class Main {
public static void main(String[] args) {
Student s = new Student("张三", 90);
/* 输出:
Person构造方法执行
Student构造方法执行
*/
}
}1.8.3 构造方法调用要点
- super()必须第一行:在子类构造方法中,super()调用必须是第一条语句
- 默认无参调用:如果没有显式调用super(),编译器会自动添加对父类无参构造的调用
- 构造方法链:对象构造会从最顶层的父类开始,依次向下执行
















