面向对象编程(OOP)是Java的核心思想,它让代码更加模块化、可重用和易维护。今天,我们将深入探讨OOP的三大支柱:封装、继承和多态,这是每个Java程序员必须掌握的核心概念。
引入:为什么需要面向对象编程?
想象一下,你正在开发一个游戏系统:
- 游戏中有玩家(Player)、敌人(Enemy)、道具(Item)等多种角色
- 这些角色都有共同的特征(如位置、血量),但也有各自的特殊能力
- 如果不使用OOP,代码会变得混乱不堪,难以维护
使用面向对象编程,我们可以将现实世界的实体抽象为"对象",每个对象都有自己的数据和行为,就像乐高积木一样,可以灵活组合。
一、封装:保护数据的"保险箱"
1.1 什么是封装?
封装就是将数据和对数据的操作包装在一起,并隐藏实现细节。就像银行的ATM机:
- 你只能通过界面按钮(公共方法)操作
- 不能直接接触内部的现金(私有数据)
- ATM内部的运作机制对你隐藏(实现细节)
1.2 封装的实现示例
java
public class EncapsulationDemo {
public static void main(String[] args) {
// 创建银行账户
BankAccount account = new BankAccount("张三", "123456");
// ✅ 正确的访问方式:通过公共方法
account.deposit(1000); // 存款
account.withdraw(500); // 取款
account.displayBalance(); // 查看余额
// ❌ 错误的方式:直接访问私有数据
// account.balance = 1000000; // 编译错误!无法直接访问
// account.password = "hack"; // 编译错误!无法直接访问
System.out.println("\n尝试非法操作:");
account.withdraw(10000); // 余额不足,安全被拒绝
System.out.println("\n尝试破解密码:");
// 无法直接修改密码,只能通过验证流程
account.changePassword("wrong", "new123"); // 失败
account.changePassword("123456", "new123"); // 成功
}
}
class BankAccount {
// 私有属性:外部不能直接访问(封装的精髓)
private String accountHolder; // 账户持有人
private String password; // 密码
private double balance; // 余额
private final String accountNumber; // 账号(final表示不可变)
// 构造方法
public BankAccount(String holder, String pwd) {
this.accountHolder = holder;
this.password = pwd;
this.balance = 0.0;
this.accountNumber = generateAccountNumber();
}
// 公共方法:存款
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("成功存款:" + amount + "元");
logTransaction("存款", amount);
} else {
System.out.println("存款金额必须大于0");
}
}
// 公共方法:取款
public void withdraw(double amount) {
if (amount <= 0) {
System.out.println("取款金额必须大于0");
} else if (amount > balance) {
System.out.println("余额不足!当前余额:" + balance);
} else {
balance -= amount;
System.out.println("成功取款:" + amount + "元");
logTransaction("取款", amount);
}
}
// 公共方法:显示余额
public void displayBalance() {
System.out.println("账户:" + accountNumber);
System.out.println("持有人:" + accountHolder);
System.out.println("当前余额:" + balance + "元");
}
// 公共方法:修改密码(需要验证原密码)
public boolean changePassword(String oldPwd, String newPwd) {
if (this.password.equals(oldPwd)) {
this.password = newPwd;
System.out.println("密码修改成功");
return true;
} else {
System.out.println("原密码错误,修改失败");
return false;
}
}
// 私有方法:只能在类内部使用(封装的实现细节)
private String generateAccountNumber() {
// 模拟生成账号:时间戳 + 随机数
return "ACC" + System.currentTimeMillis() % 10000;
}
private void logTransaction(String type, double amount) {
// 记录交易日志
System.out.println("[日志] " + type + " " + amount + "元");
}
// Getter方法:提供对私有属性的受控访问
public String getAccountHolder() {
return accountHolder;
}
public double getBalance() {
return balance;
}
// 没有提供password的getter,保护密码安全
// Setter方法:提供对私有属性的受控修改
public void setAccountHolder(String holder) {
if (holder != null && !holder.trim().isEmpty()) {
this.accountHolder = holder;
}
}
}
1.3 封装的好处
- 数据安全:防止外部代码随意修改对象状态
- 易于维护:内部实现可以改变而不影响外部代码
- 代码清晰:明确区分"可以做什么"和"如何实现"
- 减少耦合:类之间的关系更清晰,更易于修改
二、继承:代码复用的"基因传递"
2.1 什么是继承?
继承允许一个类(子类)继承另一个类(父类)的属性和方法。就像生物学中的遗传:
- 孩子继承父母的特征
- 但孩子也可以有自己独特的特征
- 继承建立了类之间的层次关系
2.2 继承的实现示例
java
public class InheritanceDemo {
public static void main(String[] args) {
System.out.println("=== 继承示例:车辆系统 ===\n");
// 创建不同类型的车辆
Vehicle genericVehicle = new Vehicle("通用车辆", 2020);
Car myCar = new Car("丰田卡罗拉", 2022, 4);
Motorcycle myBike = new Motorcycle("本田CBR", 2023, true);
ElectricCar tesla = new ElectricCar("特斯拉Model 3", 2023, 4, 75);
// 展示多态性:父类引用指向子类对象
Vehicle[] vehicles = {genericVehicle, myCar, myBike, tesla};
for (Vehicle vehicle : vehicles) {
vehicle.displayInfo();
vehicle.start(); // 多态:同一个方法,不同行为
System.out.println();
}
// 访问子类特有的方法
System.out.println("=== 子类特有方法 ===");
myCar.openSunroof();
myBike.doWheelie();
tesla.chargeBattery();
}
}
// 父类:车辆
class Vehicle {
// 受保护属性:子类可以访问,外部不能
protected String brand;
protected int year;
// 构造方法
public Vehicle(String brand, int year) {
this.brand = brand;
this.year = year;
}
// 公共方法:启动车辆
public void start() {
System.out.println(brand + " 正在启动...");
}
// 公共方法:停止车辆
public void stop() {
System.out.println(brand + " 已停止");
}
// 公共方法:显示信息
public void displayInfo() {
System.out.println("品牌:" + brand);
System.out.println("年份:" + year);
}
// 计算年龄
public int calculateAge(int currentYear) {
return currentYear - year;
}
}
// 子类:汽车(继承Vehicle)
class Car extends Vehicle {
// 子类特有的属性
private int doorCount;
// 子类构造方法:使用super调用父类构造方法
public Car(String brand, int year, int doorCount) {
super(brand, year); // 必须首先调用父类构造方法
this.doorCount = doorCount;
}
// 重写父类方法(方法覆盖)
@Override
public void start() {
System.out.println(brand + " 汽车启动:钥匙转动,引擎轰鸣!");
}
// 子类特有的方法
public void openSunroof() {
System.out.println(brand + " 的天窗已打开");
}
// 重写显示信息方法,添加子类特有信息
@Override
public void displayInfo() {
super.displayInfo(); // 调用父类方法
System.out.println("类型:汽车");
System.out.println("车门数量:" + doorCount);
}
}
// 子类:摩托车(继承Vehicle)
class Motorcycle extends Vehicle {
private boolean hasSidecar;
public Motorcycle(String brand, int year, boolean hasSidecar) {
super(brand, year);
this.hasSidecar = hasSidecar;
}
@Override
public void start() {
System.out.println(brand + " 摩托车启动:踩下启动杆,引擎咆哮!");
}
// 摩托车特有方法
public void doWheelie() {
System.out.println(brand + " 正在抬头特技!");
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.println("类型:摩托车");
System.out.println("是否有边车:" + (hasSidecar ? "是" : "否"));
}
}
// 子类的子类:电动汽车(继承Car)
class ElectricCar extends Car {
private double batteryCapacity; // 电池容量(千瓦时)
public ElectricCar(String brand, int year, int doorCount, double batteryCapacity) {
super(brand, year, doorCount);
this.batteryCapacity = batteryCapacity;
}
@Override
public void start() {
System.out.println(brand + " 电动汽车启动:无声启动,零排放!");
}
// 电动汽车特有方法
public void chargeBattery() {
System.out.println(brand + " 正在充电,电池容量:" + batteryCapacity + "kWh");
}
@Override
public void displayInfo() {
super.displayInfo(); // 调用Car的displayInfo
System.out.println("动力类型:电动");
System.out.println("电池容量:" + batteryCapacity + "kWh");
}
}
2.3 继承的关键点
-
super关键字:用于访问父类的属性、方法和构造方法
-
方法重写(Override) :子类重新实现父类的方法
-
访问修饰符:
- private:只有本类能访问
- protected:本类、子类和同包类能访问
- public:所有类都能访问
-
final关键字:
- final类:不能被继承
- final方法:不能被子类重写
- final变量:常量,不能被修改
三、多态:灵活多变的"变形金刚"
3.1 什么是多态?
多态(Polymorphism)意为"多种形态"。在Java中,多态允许我们使用父类引用指向子类对象,同一个方法调用会根据实际对象类型产生不同的行为。
3.2 多态的实现示例
java
public class PolymorphismDemo {
public static void main(String[] args) {
System.out.println("=== 多态示例:几何图形计算 ===\n");
// 使用父类引用指向不同的子类对象
Shape[] shapes = new Shape[4];
shapes[0] = new Circle(5.0);
shapes[1] = new Rectangle(4.0, 6.0);
shapes[2] = new Triangle(3.0, 4.0, 5.0);
shapes[3] = new Circle(7.0);
// 多态的魅力:同一个方法调用,不同的行为
double totalArea = 0;
double totalPerimeter = 0;
for (Shape shape : shapes) {
System.out.println(shape.getShapeName() + ":");
System.out.printf(" 面积: %.2f\n", shape.calculateArea());
System.out.printf(" 周长: %.2f\n", shape.calculatePerimeter());
totalArea += shape.calculateArea();
totalPerimeter += shape.calculatePerimeter();
// 检查具体类型并进行特殊处理
if (shape instanceof Circle) {
Circle circle = (Circle) shape; // 向下转型
System.out.printf(" 半径: %.2f\n", circle.getRadius());
}
System.out.println();
}
System.out.printf("总面积: %.2f\n", totalArea);
System.out.printf("总周长: %.2f\n", totalPerimeter);
// 多态在方法参数中的应用
System.out.println("\n=== 多态在方法参数中的应用 ===");
displayShapeInfo(shapes[0]);
displayShapeInfo(shapes[1]);
}
// 方法接收父类类型参数,可以接受任何子类对象
public static void displayShapeInfo(Shape shape) {
System.out.println("形状信息:");
shape.displayInfo();
System.out.println();
}
}
// 抽象类:形状(不能实例化)
abstract class Shape {
protected String color;
public Shape() {
this.color = "黑色";
}
public Shape(String color) {
this.color = color;
}
// 抽象方法:子类必须实现
public abstract double calculateArea();
public abstract double calculatePerimeter();
public abstract String getShapeName();
// 具体方法:子类可以继承或重写
public void displayInfo() {
System.out.println("形状: " + getShapeName());
System.out.println("颜色: " + color);
System.out.printf("面积: %.2f\n", calculateArea());
System.out.printf("周长: %.2f\n", calculatePerimeter());
}
// Getter和Setter
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
// 具体类:圆形
class Circle extends Shape {
private double radius;
public Circle(double radius) {
super();
this.radius = radius;
}
public Circle(double radius, String color) {
super(color);
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}
@Override
public String getShapeName() {
return "圆形";
}
// Circle特有的方法
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
if (radius > 0) {
this.radius = radius;
}
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.printf("半径: %.2f\n", radius);
System.out.printf("直径: %.2f\n", 2 * radius);
}
}
// 具体类:矩形
class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
super("蓝色");
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
@Override
public double calculatePerimeter() {
return 2 * (width + height);
}
@Override
public String getShapeName() {
return "矩形";
}
// Rectangle特有的方法
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.printf("宽度: %.2f\n", width);
System.out.printf("高度: %.2f\n", height);
}
}
// 具体类:三角形
class Triangle extends Shape {
private double sideA;
private double sideB;
private double sideC;
public Triangle(double a, double b, double c) {
super("绿色");
this.sideA = a;
this.sideB = b;
this.sideC = c;
}
@Override
public double calculateArea() {
// 使用海伦公式计算三角形面积
double s = (sideA + sideB + sideC) / 2;
return Math.sqrt(s * (s - sideA) * (s - sideB) * (s - sideC));
}
@Override
public double calculatePerimeter() {
return sideA + sideB + sideC;
}
@Override
public String getShapeName() {
return "三角形";
}
@Override
public void displayInfo() {
super.displayInfo();
System.out.printf("边长: %.2f, %.2f, %.2f\n", sideA, sideB, sideC);
}
}
3.3 多态的两种形式
java
public class PolymorphismForms {
public static void main(String[] args) {
System.out.println("=== 多态的两种形式 ===\n");
// 形式1:编译时多态(方法重载)
Calculator calc = new Calculator();
System.out.println("加法重载示例:");
System.out.println("两个整数相加: " + calc.add(5, 3));
System.out.println("三个整数相加: " + calc.add(5, 3, 2));
System.out.println("两个小数相加: " + calc.add(5.5, 3.3));
System.out.println("整数和小数相加: " + calc.add(5, 3.3));
// 形式2:运行时多态(方法重写)
System.out.println("\n动物叫声示例:");
Animal myAnimal;
// 运行时决定调用哪个方法
myAnimal = new Dog();
myAnimal.makeSound(); // 输出:汪汪汪!
myAnimal = new Cat();
myAnimal.makeSound(); // 输出:喵喵喵!
myAnimal = new Bird();
myAnimal.makeSound(); // 输出:叽叽喳喳!
// 实际应用:工厂模式
System.out.println("\n=== 工厂模式示例 ===");
AnimalFactory factory = new AnimalFactory();
Animal animal1 = factory.createAnimal("dog");
animal1.makeSound();
Animal animal2 = factory.createAnimal("cat");
animal2.makeSound();
Animal animal3 = factory.createAnimal("bird");
animal3.makeSound();
}
}
// 编译时多态:方法重载
class Calculator {
// 同一个方法名,不同的参数列表
public int add(int a, int b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
public double add(double a, double b) {
return a + b;
}
public double add(int a, double b) {
return a + b;
}
}
// 运行时多态:方法重写
abstract class Animal {
public abstract void makeSound();
public void sleep() {
System.out.println("动物正在睡觉...");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪汪!");
}
public void fetch() {
System.out.println("狗狗叼回了飞盘");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵喵!");
}
public void scratch() {
System.out.println("猫在抓沙发");
}
}
class Bird extends Animal {
@Override
public void makeSound() {
System.out.println("叽叽喳喳!");
}
public void fly() {
System.out.println("鸟在飞翔");
}
}
// 工厂模式:使用多态创建对象
class AnimalFactory {
public Animal createAnimal(String type) {
switch (type.toLowerCase()) {
case "dog":
return new Dog();
case "cat":
return new Cat();
case "bird":
return new Bird();
default:
throw new IllegalArgumentException("未知的动物类型: " + type);
}
}
}
3.4 多态的优势
- 代码灵活性:可以编写更通用的代码
- 易于扩展:添加新的子类时,无需修改现有代码
- 接口统一:不同对象可以通过统一接口操作
- 解耦合:降低模块间的依赖关系
四、三大特性综合实战:员工管理系统
java
import java.util.ArrayList;
public class EmployeeManagementSystem {
public static void main(String[] args) {
System.out.println("=== 员工管理系统 ===\n");
// 创建公司
Company company = new Company("科技无限公司");
// 创建不同类型的员工
Employee manager = new Manager("张经理", 50000, 20000, 10);
Employee developer = new Developer("王程序员", 30000, "Java", 5);
Employee sales = new Salesperson("李销售", 25000, 1000000);
// 添加员工到公司
company.hireEmployee(manager);
company.hireEmployee(developer);
company.hireEmployee(sales);
// 临时工
company.hireEmployee(new Intern("赵实习生", 5000, 6));
// 展示所有员工信息
company.displayAllEmployees();
// 计算总工资支出
System.out.println("\n=== 月度工资报告 ===");
company.calculateMonthlyPayroll();
// 员工工作
System.out.println("\n=== 员工工作 ===");
company.makeEmployeesWork();
// 晋升员工
System.out.println("\n=== 员工晋升 ===");
company.promoteEmployee("王程序员", "Senior Developer");
// 展示晋升后信息
company.displayAllEmployees();
}
}
// 抽象员工类
abstract class Employee {
protected String name;
protected double baseSalary;
protected String employeeId;
protected static int nextId = 1000; // 静态变量,用于生成ID
public Employee(String name, double baseSalary) {
this.name = name;
this.baseSalary = baseSalary;
this.employeeId = "EMP" + nextId++;
}
// 抽象方法:计算工资(不同员工计算方式不同)
public abstract double calculateSalary();
// 抽象方法:工作(不同员工工作内容不同)
public abstract void work();
// 具体方法:所有员工共享
public void attendMeeting() {
System.out.println(name + " 正在参加会议");
}
public void takeBreak() {
System.out.println(name + " 正在休息");
}
// 显示员工信息
public void displayInfo() {
System.out.println("ID: " + employeeId);
System.out.println("姓名: " + name);
System.out.println("职位: " + getPosition());
System.out.printf("基本工资: ¥%.2f\n", baseSalary);
System.out.printf("实发工资: ¥%.2f\n", calculateSalary());
}
// 获取职位名称
public abstract String getPosition();
// Getter方法
public String getName() {
return name;
}
public String getEmployeeId() {
return employeeId;
}
// Setter方法
public void setName(String name) {
if (name != null && !name.trim().isEmpty()) {
this.name = name;
}
}
}
// 经理类
class Manager extends Employee {
private double bonus;
private int teamSize;
public Manager(String name, double baseSalary, double bonus, int teamSize) {
super(name, baseSalary);
this.bonus = bonus;
this.teamSize = teamSize;
}
@Override
public double calculateSalary() {
// 经理工资 = 基本工资 + 奖金
return baseSalary + bonus;
}
@Override
public void work() {
System.out.println(name + " 经理正在制定项目计划和分配任务");
System.out.println("管理团队大小: " + teamSize + "人");
}
@Override
public String getPosition() {
return "经理";
}
// 经理特有方法
public void conductReview(Employee employee) {
System.out.println(name + " 正在对员工 " + employee.getName() + " 进行绩效评估");
}
public void setBonus(double bonus) {
if (bonus >= 0) {
this.bonus = bonus;
}
}
}
// 开发人员类
class Developer extends Employee {
private String programmingLanguage;
private int yearsOfExperience;
public Developer(String name, double baseSalary, String language, int experience) {
super(name, baseSalary);
this.programmingLanguage = language;
this.yearsOfExperience = experience;
}
@Override
public double calculateSalary() {
// 开发人员工资 = 基本工资 + 经验津贴(每年经验+5%)
double experienceBonus = baseSalary * 0.05 * yearsOfExperience;
return baseSalary + experienceBonus;
}
@Override
public void work() {
System.out.println(name + " 开发人员正在用 " + programmingLanguage + " 编写代码");
System.out.println("经验: " + yearsOfExperience + "年");
}
@Override
public String getPosition() {
return "开发人员 (" + programmingLanguage + ")";
}
// 开发人员特有方法
public void debugCode() {
System.out.println(name + " 正在调试代码");
}
public void attendTechMeeting() {
System.out.println(name + " 正在参加技术会议");
}
}
// 销售人员类
class Salesperson extends Employee {
private double salesAmount;
public Salesperson(String name, double baseSalary, double salesAmount) {
super(name, baseSalary);
this.salesAmount = salesAmount;
}
@Override
public double calculateSalary() {
// 销售人员工资 = 基本工资 + 销售提成(销售额的10%)
double commission = salesAmount * 0.10;
return baseSalary + commission;
}
@Override
public void work() {
System.out.println(name + " 销售人员正在联系客户,完成销售目标");
System.out.printf("本月销售额: ¥%.2f\n", salesAmount);
}
@Override
public String getPosition() {
return "销售人员";
}
// 销售人员特有方法
public void makeSale(double amount) {
salesAmount += amount;
System.out.println(name + " 完成一笔销售: ¥" + amount);
}
public void attendSalesTraining() {
System.out.println(name + " 正在参加销售培训");
}
}
// 实习生类
class Intern extends Employee {
private int internshipDuration; // 实习时长(月)
public Intern(String name, double baseSalary, int duration) {
super(name, baseSalary);
this.internshipDuration = duration;
}
@Override
public double calculateSalary() {
// 实习生只有基本工资,没有奖金
return baseSalary;
}
@Override
public void work() {
System.out.println(name + " 实习生正在学习并协助完成简单任务");
System.out.println("实习时长: " + internshipDuration + "个月");
}
@Override
public String getPosition() {
return "实习生";
}
// 实习生特有方法
public void learn() {
System.out.println(name + " 正在学习新技能");
}
public void requestMentor() {
System.out.println(name + " 请求导师指导");
}
}
// 公司类
class Company {
private String name;
private ArrayList<Employee> employees;
public Company(String name) {
this.name = name;
this.employees = new ArrayList<>();
}
// 雇佣员工(多态:可以接收任何Employee子类)
public void hireEmployee(Employee employee) {
employees.add(employee);
System.out.println("已雇佣: " + employee.getName() + " (" + employee.getPosition() + ")");
}
// 展示所有员工信息
public void displayAllEmployees() {
System.out.println("=== " + name + " 员工列表 ===");
System.out.println("=".repeat(50));
for (Employee emp : employees) {
emp.displayInfo();
System.out.println("-".repeat(50));
}
}
// 计算月度工资支出
public void calculateMonthlyPayroll() {
double total = 0;
for (Employee emp : employees) {
total += emp.calculateSalary();
}
System.out.printf("员工总数: %d人\n", employees.size());
System.out.printf("月度工资总支出: ¥%.2f\n", total);
// 按职位统计
System.out.println("\n按职位统计:");
for (Employee emp : employees) {
System.out.printf("%-20s: ¥%.2f\n",
emp.getPosition(), emp.calculateSalary());
}
}
// 让所有员工工作(多态:调用各自的工作方法)
public void makeEmployeesWork() {
for (Employee emp : employees) {
System.out.print("> ");
emp.work();
}
}
// 晋升员工
public void promoteEmployee(String employeeName, String newPosition) {
for (Employee emp : employees) {
if (emp.getName().equals(employeeName)) {
System.out.println(emp.getName() + " 晋升为: " + newPosition);
// 根据晋升调整工资
if (emp instanceof Developer) {
Developer dev = (Developer) emp;
System.out.println("工资增加20%");
// 实际应用中会有更复杂的逻辑
} else if (emp instanceof Manager) {
Manager mgr = (Manager) emp;
System.out.println("奖金增加30%");
}
return;
}
}
System.out.println("未找到员工: " + employeeName);
}
}
五、总结:三大特性的核心要点
🎯 核心要点总结:
-
封装(Encapsulation) :把数据和行为包装在类中,隐藏实现细节
- 使用
private保护数据 - 通过
public方法提供访问接口 - 好处:安全性、易维护、低耦合
- 使用
-
继承(Inheritance) :子类继承父类的属性和方法
- 使用
extends关键字 super调用父类构造方法- 方法重写(
@Override) - 好处:代码复用、建立层次关系
- 使用
-
多态(Polymorphism) :同一接口,不同实现
- 编译时多态:方法重载
- 运行时多态:方法重写
- 父类引用指向子类对象
- 好处:灵活性、扩展性、解耦合
💡 实战建议:
- 合理设计类层次:不要让继承层次太深(通常不超过3层)
- 优先使用组合而不是继承:除非有明显的"is-a"关系
- 为扩展而设计:使用抽象类和接口定义契约
- 遵守里氏替换原则:子类应该能够替换父类而不影响程序功能