一、什么是继承?
继承是面向对象编程的核心特性之一,它允许一个类(子类)继承另一个类(父类)的属性和方法。就像生物学中的遗传:
- 父类(基类)→ 通用特性
- 子类(派生类)→ 父类的特性 + 自己的特性
现实世界的例子:
交通工具(父类)
├── 汽车(子类)
├── 摩托车(子类)
└── 自行车(子类)
二、继承的基本语法
2.1 最简单的继承示例
// 父类
class Animal {
String name;
int age;
void eat() {
System.out.println(name + "正在吃东西");
}
void sleep() {
System.out.println(name + "正在睡觉");
}
}
// 子类继承父类
class Dog extends Animal { // 使用extends关键字
// Dog自动继承了Animal的name、age属性和eat()、sleep()方法
// Dog特有的方法
void bark() {
System.out.println(name + "在汪汪叫");
}
}
// 另一个子类
class Cat extends Animal {
// Cat特有的方法
void meow() {
System.out.println(name + "在喵喵叫");
}
}
public class InheritanceBasic {
public static void main(String[] args) {
System.out.println("=== 继承基本示例 ===");
// 创建Dog对象
Dog dog = new Dog();
dog.name = "旺财";
dog.age = 3;
// 调用继承自父类的方法
dog.eat();
dog.sleep();
// 调用子类特有的方法
dog.bark();
System.out.println("------------------");
// 创建Cat对象
Cat cat = new Cat();
cat.name = "咪咪";
cat.age = 2;
cat.eat();
cat.sleep();
cat.meow();
}
}
运行结果:
=== 继承基本示例 ===
旺财正在吃东西
旺财正在睡觉
旺财在汪汪叫
------------------
咪咪正在吃东西
咪咪正在睡觉
咪咪在喵喵叫
2.2 继承的层次结构
// 更复杂的继承层次
class Vehicle {
String brand;
String color;
int maxSpeed;
void start() {
System.out.println(color + "的" + brand + "启动了");
}
void stop() {
System.out.println(color + "的" + brand + "停下了");
}
}
// 第一层子类
class Car extends Vehicle {
int doors; // 车门数量
void honk() {
System.out.println(brand + "汽车在鸣笛:嘀嘀!");
}
}
class Motorcycle extends Vehicle {
boolean hasSidecar; // 是否有边斗
void revEngine() {
System.out.println(brand + "摩托车在轰油门:嗡嗡!");
}
}
// 第二层子类
class ElectricCar extends Car {
int batteryCapacity; // 电池容量
void charge() {
System.out.println(brand + "电动车正在充电");
}
}
class SportsCar extends Car {
boolean isConvertible; // 是否敞篷
void turboBoost() {
System.out.println(brand + "跑车启动涡轮增压!");
}
}
public class InheritanceHierarchy {
public static void main(String[] args) {
System.out.println("=== 继承层次结构 ===");
// 创建Vehicle对象
Vehicle vehicle = new Vehicle();
vehicle.brand = "通用";
vehicle.color = "白色";
vehicle.maxSpeed = 120;
vehicle.start();
vehicle.stop();
System.out.println("------------------");
// 创建Car对象
Car car = new Car();
car.brand = "丰田";
car.color = "黑色";
car.doors = 4;
car.start();
car.honk();
car.stop();
System.out.println("------------------");
// 创建ElectricCar对象
ElectricCar electricCar = new ElectricCar();
electricCar.brand = "特斯拉";
electricCar.color = "红色";
electricCar.doors = 4;
electricCar.batteryCapacity = 100;
electricCar.start();
electricCar.charge();
electricCar.honk();
electricCar.stop();
System.out.println("------------------");
// 创建SportsCar对象
SportsCar sportsCar = new SportsCar();
sportsCar.brand = "保时捷";
sportsCar.color = "黄色";
sportsCar.isConvertible = true;
sportsCar.start();
sportsCar.turboBoost();
sportsCar.stop();
}
}
三、方法重写(Override):子类的新特性
3.1 什么是方法重写?
方法重写是子类重新定义父类中已有的方法,以满足子类的特殊需求。
class Bird {
String name;
void fly() {
System.out.println(name + "在天空飞翔");
}
void makeSound() {
System.out.println(name + "发出声音");
}
}
class Penguin extends Bird {
// 企鹅不会飞,所以重写fly方法
@Override // 注解,表示重写父类方法(可选但推荐)
void fly() {
System.out.println(name + "不会飞,但游泳很棒!");
}
// 重写makeSound方法
@Override
void makeSound() {
System.out.println(name + "在叫:嘎嘎!");
}
}
class Eagle extends Bird {
// 鹰飞得更高更快
@Override
void fly() {
System.out.println(name + "在高空翱翔,速度很快!");
}
@Override
void makeSound() {
System.out.println(name + "在叫:啸!");
}
// 鹰特有的方法
void hunt() {
System.out.println(name + "正在捕猎");
}
}
public class MethodOverride {
public static void main(String[] args) {
System.out.println("=== 方法重写示例 ===");
Bird bird1 = new Bird();
bird1.name = "普通鸟";
bird1.fly();
bird1.makeSound();
System.out.println("------------------");
Penguin penguin = new Penguin();
penguin.name = "企鹅";
penguin.fly(); // 调用重写后的方法
penguin.makeSound();
System.out.println("------------------");
Eagle eagle = new Eagle();
eagle.name = "老鹰";
eagle.fly(); // 调用重写后的方法
eagle.makeSound();
eagle.hunt(); // 调用特有方法
}
}
3.2 super关键字:访问父类成员
class Smartphone {
String brand;
double screenSize;
Smartphone(String brand, double screenSize) {
this.brand = brand;
this.screenSize = screenSize;
}
void makeCall(String number) {
System.out.println("用" + brand + "手机打电话给" + number);
}
void showInfo() {
System.out.println("品牌:" + brand + ",屏幕尺寸:" + screenSize + "英寸");
}
}
class SmartphonePro extends Smartphone {
boolean has5G;
int cameraCount;
// 子类构造方法必须调用父类构造方法
SmartphonePro(String brand, double screenSize, boolean has5G, int cameraCount) {
super(brand, screenSize); // 调用父类构造方法
this.has5G = has5G;
this.cameraCount = cameraCount;
}
// 重写父类方法,并扩展功能
@Override
void showInfo() {
super.showInfo(); // 调用父类的方法
System.out.println("5G支持:" + (has5G ? "是" : "否"));
System.out.println("摄像头数量:" + cameraCount + "个");
}
// 子类特有的方法
void takePhoto() {
System.out.println("用" + cameraCount + "个摄像头拍照");
}
}
public class SuperKeyword {
public static void main(String[] args) {
System.out.println("=== super关键字示例 ===");
Smartphone phone1 = new Smartphone("小米", 6.5);
phone1.makeCall("10086");
phone1.showInfo();
System.out.println("------------------");
SmartphonePro phone2 = new SmartphonePro("华为", 6.8, true, 4);
phone2.makeCall("10010"); // 继承自父类
phone2.showInfo(); // 调用重写后的方法
phone2.takePhoto(); // 子类特有方法
}
}
四、访问修饰符与继承
4.1 不同访问修饰符的继承特性
class Parent {
public String publicField = "公共字段"; // 任何地方都可访问
protected String protectedField = "受保护字段"; // 同包或子类可访问
String defaultField = "默认字段"; // 同包可访问
private String privateField = "私有字段"; // 只有本类可访问
public void publicMethod() {
System.out.println("公共方法");
}
protected void protectedMethod() {
System.out.println("受保护方法");
}
void defaultMethod() {
System.out.println("默认方法");
}
private void privateMethod() {
System.out.println("私有方法");
}
// 测试私有字段的访问
public void testPrivateAccess() {
System.out.println("父类内部可以访问:" + privateField);
privateMethod();
}
}
// 同包子类
class ChildSamePackage extends Parent {
public void testAccess() {
System.out.println("\n同包子类访问测试:");
System.out.println(publicField); // ✓ 可以
System.out.println(protectedField); // ✓ 可以(子类)
System.out.println(defaultField); // ✓ 可以(同包)
// System.out.println(privateField); // ✗ 错误:私有
publicMethod(); // ✓ 可以
protectedMethod(); // ✓ 可以(子类)
defaultMethod(); // ✓ 可以(同包)
// privateMethod(); // ✗ 错误:私有
}
}
// 不同包子类
class ChildDifferentPackage extends Parent {
public void testAccess() {
System.out.println("\n不同包子类访问测试:");
System.out.println(publicField); // ✓ 可以
System.out.println(protectedField); // ✓ 可以(子类)
// System.out.println(defaultField); // ✗ 错误:不同包
// System.out.println(privateField); // ✗ 错误:私有
publicMethod(); // ✓ 可以
protectedMethod(); // ✓ 可以(子类)
// defaultMethod(); // ✗ 错误:不同包
// privateMethod(); // ✗ 错误:私有
}
}
public class InheritanceAccess {
public static void main(String[] args) {
System.out.println("=== 访问修饰符与继承 ===");
Parent parent = new Parent();
System.out.println(parent.publicField);
parent.publicMethod();
parent.testPrivateAccess();
ChildSamePackage child1 = new ChildSamePackage();
child1.testAccess();
}
}
五、构造方法的继承
class Employee {
String name;
int id;
double salary;
// 父类无参构造方法
Employee() {
System.out.println("Employee无参构造方法被调用");
this.name = "未知";
this.id = 0;
this.salary = 0.0;
}
// 父类有参构造方法
Employee(String name, int id, double salary) {
System.out.println("Employee有参构造方法被调用");
this.name = name;
this.id = id;
this.salary = salary;
}
void work() {
System.out.println(name + "正在工作");
}
void showInfo() {
System.out.println("员工:" + name + ",工号:" + id + ",薪资:" + salary);
}
}
class Manager extends Employee {
String department;
int teamSize;
// 子类构造方法1:无参
Manager() {
super(); // 调用父类无参构造方法(可省略,编译器会自动添加)
System.out.println("Manager无参构造方法被调用");
this.department = "未分配";
this.teamSize = 0;
}
// 子类构造方法2:有参,调用父类有参构造方法
Manager(String name, int id, double salary, String department, int teamSize) {
super(name, id, salary); // 必须放在第一行
System.out.println("Manager有参构造方法被调用");
this.department = department;
this.teamSize = teamSize;
}
// 子类构造方法3:部分参数
Manager(String name, String department) {
super(name, 1000, 8000.0); // 调用父类构造方法
this.department = department;
this.teamSize = 5;
}
// 重写父类方法
@Override
void work() {
super.work(); // 调用父类方法
System.out.println(name + "作为" + department + "部门经理在管理工作");
}
// 重写showInfo方法
@Override
void showInfo() {
super.showInfo();
System.out.println("部门:" + department + ",团队人数:" + teamSize);
}
// 经理特有的方法
void holdMeeting() {
System.out.println(name + "正在召开部门会议");
}
}
public class ConstructorInheritance {
public static void main(String[] args) {
System.out.println("=== 构造方法的继承 ===");
System.out.println("\n1. 创建普通员工:");
Employee emp = new Employee("张三", 1001, 5000.0);
emp.work();
emp.showInfo();
System.out.println("\n2. 创建经理(无参构造):");
Manager mgr1 = new Manager();
mgr1.showInfo();
System.out.println("\n3. 创建经理(有参构造):");
Manager mgr2 = new Manager("李四", 1002, 15000.0, "技术部", 10);
mgr2.work();
mgr2.showInfo();
mgr2.holdMeeting();
System.out.println("\n4. 创建经理(部分参数):");
Manager mgr3 = new Manager("王五", "市场部");
mgr3.showInfo();
}
}
六、final关键字与继承
6.1 final类:不能被继承
// final类:不能被继承
final class Circle {
double radius;
Circle(double radius) {
this.radius = radius;
}
double getArea() {
return Math.PI * radius * radius;
}
}
// ❌ 错误:不能继承final类
// class SpecialCircle extends Circle { }
public class FinalClass {
public static void main(String[] args) {
Circle circle = new Circle(5.0);
System.out.println("圆面积:" + circle.getArea());
}
}
6.2 final方法:不能被子类重写
class Shape {
// final方法:不能被子类重写
final void displayType() {
System.out.println("这是一个形状");
}
// 普通方法:可以被子类重写
void draw() {
System.out.println("绘制形状");
}
}
class Square extends Shape {
// ❌ 错误:不能重写final方法
// @Override
// void displayType() { }
// ✓ 正确:可以重写非final方法
@Override
void draw() {
System.out.println("绘制正方形");
}
}
6.3 final变量:常量
class Constants {
// final变量:常量,必须初始化且不能修改
final double PI = 3.1415926535;
final int MAX_SIZE;
Constants() {
MAX_SIZE = 100; // 可以在构造方法中初始化
}
void test() {
// PI = 3.14; // ❌ 错误:不能修改final变量
// MAX_SIZE = 200; // ❌ 错误:不能修改final变量
}
}
七、综合示例:完整的员工管理系统
import java.util.ArrayList;
// 基类:员工
class Employee {
private String name;
private int id;
private double baseSalary;
public Employee(String name, int id, double baseSalary) {
this.name = name;
this.id = id;
this.baseSalary = baseSalary;
}
// 计算工资(基类中定义,子类可以重写)
public double calculateSalary() {
return baseSalary;
}
public void displayInfo() {
System.out.println("员工:" + name + ",工号:" + id);
System.out.println("基本工资:" + baseSalary + ",实发工资:" + calculateSalary());
}
// Getter方法
public String getName() { return name; }
public int getId() { return id; }
public double getBaseSalary() { return baseSalary; }
}
// 子类1:普通员工
class RegularEmployee extends Employee {
private int overtimeHours; // 加班小时数
private double overtimeRate = 50.0; // 加班费率
public RegularEmployee(String name, int id, double baseSalary, int overtimeHours) {
super(name, id, baseSalary);
this.overtimeHours = overtimeHours;
}
@Override
public double calculateSalary() {
// 工资 = 基本工资 + 加班费
return super.getBaseSalary() + (overtimeHours * overtimeRate);
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("职位:普通员工,加班小时:" + overtimeHours + ",加班费:" + (overtimeHours * overtimeRate));
}
}
// 子类2:经理
class Manager extends Employee {
private double bonus; // 奖金
private String department;
public Manager(String name, int id, double baseSalary, double bonus, String department) {
super(name, id, baseSalary);
this.bonus = bonus;
this.department = department;
}
@Override
public double calculateSalary() {
// 工资 = 基本工资 + 奖金
return super.getBaseSalary() + bonus;
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("职位:经理,部门:" + department + ",奖金:" + bonus);
}
// 经理特有方法
public void manageTeam() {
System.out.println(getName() + "正在管理" + department + "部门");
}
}
// 子类3:销售员
class Salesperson extends Employee {
private double salesAmount; // 销售额
private double commissionRate = 0.1; // 提成比例
public Salesperson(String name, int id, double baseSalary, double salesAmount) {
super(name, id, baseSalary);
this.salesAmount = salesAmount;
}
@Override
public double calculateSalary() {
// 工资 = 基本工资 + 销售提成
return super.getBaseSalary() + (salesAmount * commissionRate);
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("职位:销售员,销售额:" + salesAmount + ",提成:" + (salesAmount * commissionRate));
}
// 销售员特有方法
public void makeSale(double amount) {
salesAmount += amount;
System.out.println(getName() + "完成一笔销售,金额:" + amount);
}
}
// 公司类:管理所有员工
class Company {
private String companyName;
private ArrayList<Employee> employees;
public Company(String companyName) {
this.companyName = companyName;
this.employees = new ArrayList<>();
}
// 添加员工
public void addEmployee(Employee employee) {
employees.add(employee);
System.out.println("添加员工:" + employee.getName());
}
// 显示所有员工信息
public void displayAllEmployees() {
System.out.println("\n=== " + companyName + " 员工列表 ===");
for (Employee emp : employees) {
emp.displayInfo();
System.out.println("--------------------");
}
}
// 计算总工资支出
public double calculateTotalSalary() {
double total = 0.0;
for (Employee emp : employees) {
total += emp.calculateSalary();
}
return total;
}
// 根据类型统计员工
public void countEmployeesByType() {
int regularCount = 0;
int managerCount = 0;
int salesCount = 0;
for (Employee emp : employees) {
if (emp instanceof RegularEmployee) {
regularCount++;
} else if (emp instanceof Manager) {
managerCount++;
} else if (emp instanceof Salesperson) {
salesCount++;
}
}
System.out.println("\n=== 员工类型统计 ===");
System.out.println("普通员工:" + regularCount + "人");
System.out.println("经理:" + managerCount + "人");
System.out.println("销售员:" + salesCount + "人");
System.out.println("总计:" + employees.size() + "人");
}
}
public class EmployeeManagementSystem {
public static void main(String[] args) {
System.out.println("=== 员工管理系统 ===");
// 创建公司
Company company = new Company("科技公司");
// 创建各种员工
Employee emp1 = new RegularEmployee("张三", 1001, 5000.0, 10);
Employee emp2 = new Manager("李四", 1002, 10000.0, 5000.0, "技术部");
Employee emp3 = new Salesperson("王五", 1003, 4000.0, 50000.0);
Employee emp4 = new RegularEmployee("赵六", 1004, 4500.0, 5);
Employee emp5 = new Manager("钱七", 1005, 12000.0, 8000.0, "市场部");
// 添加员工到公司
company.addEmployee(emp1);
company.addEmployee(emp2);
company.addEmployee(emp3);
company.addEmployee(emp4);
company.addEmployee(emp5);
// 显示所有员工信息
company.displayAllEmployees();
// 统计员工类型
company.countEmployeesByType();
// 计算总工资支出
double totalSalary = company.calculateTotalSalary();
System.out.println("\n=== 工资支出 ===");
System.out.println("总工资支出:" + totalSalary + "元");
// 测试多态
System.out.println("\n=== 多态测试 ===");
Employee emp = emp2; // Manager对象赋给Employee引用
emp.displayInfo(); // 调用的是Manager的displayInfo方法
// 检查类型并调用特有方法
if (emp instanceof Manager) {
Manager mgr = (Manager) emp; // 向下转型
mgr.manageTeam();
}
// 销售员完成一笔销售
if (emp3 instanceof Salesperson) {
Salesperson sales = (Salesperson) emp3;
sales.makeSale(10000.0);
System.out.println("销售后工资:" + sales.calculateSalary());
}
}
}
八、总结
8.1 继承的优点
- 代码复用:子类可以重用父类的代码
- 易于维护:修改父类,所有子类自动受影响
- 多态基础:为多态提供支持
- 层次清晰:表达类之间的"是一种"关系
8.2 继承的局限性
- 单一继承:Java只支持单继承(一个类只能有一个父类)
- 紧耦合:子类与父类紧密关联,父类修改可能影响子类
- 不适当的继承:滥用继承会导致设计复杂
8.3 何时使用继承?
- 是一种(is-a)关系:狗是一种动物,汽车是一种交通工具
- 需要代码复用:多个类有共同的属性和方法
- 需要多态:需要通过父类引用操作不同子类对象
8.4 重要概念总结
- extends关键字:用于继承
- 方法重写:子类重新定义父类方法
- super关键字:访问父类成员
- final关键字:限制继承
- 访问修饰符:控制继承的可见性
8.5 快速记忆口诀
- 父类通用,子类特殊
- 继承用extends,重写加@Override
- super访问父类,this访问自己
- final防继承,protected护子类