002-面向对象基本概念
🟢 难度: 初级 | ⏱️ 预计时间: 2.5小时 | 📋 前置: 001-面向对象概述
📚 导航
⬅️ 上一章: 001-面向对象概述
➡️ 下一章: 003-类与对象
🏠 返回目录: 面向对象教程
学习目标
完成本章节后,你将能够:
- 深入理解面向对象的四大基本特征:抽象、封装、继承、多态
- 掌握类与对象的关系和区别
- 理解消息传递机制
- 掌握面向对象的基本术语和概念
- 能够识别和分析简单的面向对象设计
1. 面向对象四大基本特征
面向对象编程有四大基本特征,它们构成了面向对象技术的理论基础:
mindmap
root((面向对象四大特征))
抽象(Abstraction)
数据抽象
过程抽象
忽略无关细节
突出本质特征
封装(Encapsulation)
数据隐藏
接口统一
访问控制
模块化
继承(Inheritance)
代码重用
层次结构
"is-a"关系
特化与泛化
多态(Polymorphism)
同一接口
不同实现
动态绑定
运行时决定
1.1 抽象(Abstraction)
定义:抽象是指忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。
抽象的类型
1. 数据抽象
- 描述对象的属性和状态
- 定义对象的数据结构
- 隐藏数据的具体实现
2. 过程抽象
- 描述对象的行为和操作
- 定义对象的方法接口
- 隐藏方法的具体实现
抽象的实现示例
// 抽象类:图形
public abstract class Shape {
// 数据抽象:基本属性
protected String color;
protected double x, y; // 位置坐标
// 构造方法
public Shape(String color, double x, double y) {
this.color = color;
this.x = x;
this.y = y;
}
// 过程抽象:抽象方法(子类必须实现)
public abstract double calculateArea(); // 计算面积
public abstract double calculatePerimeter(); // 计算周长
public abstract void draw(); // 绘制图形
// 具体方法:通用行为
public void move(double deltaX, double deltaY) {
this.x += deltaX;
this.y += deltaY;
System.out.println("图形移动到: (" + x + ", " + y + ")");
}
// 获取颜色
public String getColor() {
return color;
}
}
// 具体实现:圆形
public class Circle extends Shape {
private double radius;
public Circle(String color, double x, double y, double radius) {
super(color, x, y);
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}
@Override
public void draw() {
System.out.println("绘制" + color + "圆形,半径: " + radius);
}
}
抽象的层次
1.2 封装(Encapsulation)
定义:封装是把对象的属性和方法结合成一个独立的系统单位,并尽可能隐藏对象的内部细节。
封装的目的
- 数据保护:防止外部直接访问和修改对象内部数据
- 接口统一:提供统一的访问接口
- 降低耦合:减少模块间的依赖关系
- 提高安全性:控制对数据的访问权限
访问控制修饰符
| 修饰符 | 同一类 | 同一包 | 子类 | 其他包 | 说明 |
|---|---|---|---|---|---|
| private | ✓ | ✗ | ✗ | ✗ | 私有,仅本类可访问 |
| default | ✓ | ✓ | ✗ | ✗ | 包私有,同包可访问 |
| protected | ✓ | ✓ | ✓ | ✗ | 受保护,子类可访问 |
| public | ✓ | ✓ | ✓ | ✓ | 公有,所有类可访问 |
封装的实现示例
public class BankAccount {
// 私有属性:外部无法直接访问
private String accountNumber;
private String accountHolder;
private double balance;
private String password;
// 构造方法
public BankAccount(String accountNumber, String accountHolder, String password) {
this.accountNumber = accountNumber;
this.accountHolder = accountHolder;
this.password = password;
this.balance = 0.0;
}
// 公有方法:提供受控的访问接口
public boolean deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("存款成功,当前余额: " + balance);
return true;
}
System.out.println("存款金额必须大于0");
return false;
}
public boolean withdraw(double amount, String inputPassword) {
// 密码验证
if (!verifyPassword(inputPassword)) {
System.out.println("密码错误");
return false;
}
// 余额检查
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("取款成功,当前余额: " + balance);
return true;
}
System.out.println("取款失败:金额无效或余额不足");
return false;
}
// 私有方法:内部使用,外部无法访问
private boolean verifyPassword(String inputPassword) {
return this.password.equals(inputPassword);
}
// 受保护的方法:子类可以访问
protected void updateBalance(double newBalance) {
this.balance = newBalance;
}
// 公有访问器:只读访问
public double getBalance(String inputPassword) {
if (verifyPassword(inputPassword)) {
return balance;
}
throw new SecurityException("密码错误,无法查询余额");
}
public String getAccountNumber() {
return accountNumber;
}
public String getAccountHolder() {
return accountHolder;
}
}
封装的好处
1.3 继承(Inheritance)
定义:继承是一种机制,允许在已有类的基础上创建新类,新类可以继承已有类的属性和方法。
继承的基本概念
- 父类(超类、基类):被继承的类
- 子类(派生类):继承其他类的类
- 单继承:一个子类只能有一个直接父类
- 多继承:一个子类可以有多个直接父类(Java不支持,但支持接口多实现)
继承关系图
继承的实现示例
// 父类:交通工具
public class Vehicle {
protected String brand; // 品牌
protected String model; // 型号
protected int year; // 年份
protected boolean isRunning; // 运行状态
// 构造方法
public Vehicle(String brand, String model, int year) {
this.brand = brand;
this.model = model;
this.year = year;
this.isRunning = false;
}
// 启动
public void start() {
if (!isRunning) {
isRunning = true;
System.out.println(brand + " " + model + " 启动了");
}
}
// 停止
public void stop() {
if (isRunning) {
isRunning = false;
System.out.println(brand + " " + model + " 停止了");
}
}
// 获取信息
public String getInfo() {
return year + "年 " + brand + " " + model;
}
// 获取运行状态
public boolean isRunning() {
return isRunning;
}
}
// 子类:汽车
public class Car extends Vehicle {
private int doors; // 门数
private String fuelType; // 燃料类型
private boolean trunkOpen; // 后备箱状态
// 构造方法
public Car(String brand, String model, int year, int doors, String fuelType) {
super(brand, model, year); // 调用父类构造方法
this.doors = doors;
this.fuelType = fuelType;
this.trunkOpen = false;
}
// 重写父类方法
@Override
public void start() {
System.out.println("检查燃料: " + fuelType);
super.start(); // 调用父类方法
System.out.println("汽车准备就绪");
}
// 新增方法:打开后备箱
public void openTrunk() {
if (!trunkOpen) {
trunkOpen = true;
System.out.println("后备箱已打开");
}
}
// 新增方法:锁车门
public void lockDoors() {
if (!isRunning) {
System.out.println("所有 " + doors + " 个车门已锁定");
} else {
System.out.println("车辆运行中,无法锁定车门");
}
}
// 重写获取信息方法
@Override
public String getInfo() {
return super.getInfo() + ", " + doors + "门, 燃料: " + fuelType;
}
}
// 子类:摩托车
public class Motorcycle extends Vehicle {
private boolean hasSidecar; // 是否有边车
public Motorcycle(String brand, String model, int year, boolean hasSidecar) {
super(brand, model, year);
this.hasSidecar = hasSidecar;
}
@Override
public void start() {
System.out.println("检查头盔和护具");
super.start();
System.out.println("摩托车准备就绪");
}
// 新增方法:翘头
public void wheelie() {
if (isRunning && !hasSidecar) {
System.out.println("摩托车翘头表演!");
} else {
System.out.println("无法执行翘头:车辆未启动或有边车");
}
}
@Override
public String getInfo() {
return super.getInfo() + (hasSidecar ? ", 带边车" : ", 无边车");
}
}
继承的类型
1.4 多态(Polymorphism)
定义:多态是指同一个接口可以有多种不同的实现方式,在运行时根据对象的实际类型来决定调用哪个具体的实现。
多态的类型
1. 编译时多态(静态多态)
- 方法重载(Overloading)
- 运算符重载
2. 运行时多态(动态多态)
- 方法重写(Overriding)
- 虚方法调用
多态的实现条件
- 继承关系:子类继承父类
- 方法重写:子类重写父类方法
- 向上转型:父类引用指向子类对象
多态的实现示例
// 抽象父类:动物
public abstract class Animal {
protected String name;
protected int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
// 抽象方法:发出声音(多态的基础)
public abstract void makeSound();
// 抽象方法:移动方式
public abstract void move();
// 具体方法:睡觉
public void sleep() {
System.out.println(name + " 正在睡觉...");
}
// 获取信息
public String getInfo() {
return name + ",年龄: " + age;
}
}
// 子类:狗
public class Dog extends Animal {
private String breed; // 品种
public Dog(String name, int age, String breed) {
super(name, age);
this.breed = breed;
}
@Override
public void makeSound() {
System.out.println(name + " 汪汪叫!");
}
@Override
public void move() {
System.out.println(name + " 在地上跑来跑去");
}
// 特有方法
public void wagTail() {
System.out.println(name + " 摇尾巴表示友好");
}
@Override
public String getInfo() {
return super.getInfo() + ",品种: " + breed;
}
}
// 子类:猫
public class Cat extends Animal {
private boolean isIndoor; // 是否室内猫
public Cat(String name, int age, boolean isIndoor) {
super(name, age);
this.isIndoor = isIndoor;
}
@Override
public void makeSound() {
System.out.println(name + " 喵喵叫!");
}
@Override
public void move() {
System.out.println(name + " 优雅地踱步");
}
// 特有方法
public void purr() {
System.out.println(name + " 发出呼噜声");
}
@Override
public String getInfo() {
return super.getInfo() + (isIndoor ? ",室内猫" : ",户外猫");
}
}
// 子类:鸟
public class Bird extends Animal {
private boolean canFly; // 是否会飞
public Bird(String name, int age, boolean canFly) {
super(name, age);
this.canFly = canFly;
}
@Override
public void makeSound() {
System.out.println(name + " 啾啾叫!");
}
@Override
public void move() {
if (canFly) {
System.out.println(name + " 在天空中飞翔");
} else {
System.out.println(name + " 在地上蹦跳");
}
}
// 特有方法
public void buildNest() {
System.out.println(name + " 正在筑巢");
}
}
// 多态的使用示例
public class AnimalDemo {
public static void main(String[] args) {
// 创建不同类型的动物对象
Animal[] animals = {
new Dog("旺财", 3, "金毛"),
new Cat("咪咪", 2, true),
new Bird("小黄", 1, true)
};
// 多态的体现:同一个方法调用,不同的实现
System.out.println("=== 动物园表演开始 ===");
for (Animal animal : animals) {
System.out.println("\n" + animal.getInfo());
animal.makeSound(); // 多态:运行时决定调用哪个实现
animal.move(); // 多态:运行时决定调用哪个实现
animal.sleep(); // 调用父类方法
}
// 演示向下转型
System.out.println("\n=== 特殊表演 ===");
for (Animal animal : animals) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal; // 向下转型
dog.wagTail(); // 调用子类特有方法
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.purr();
} else if (animal instanceof Bird) {
Bird bird = (Bird) animal;
bird.buildNest();
}
}
}
// 多态的另一个例子:动物管理员
public static void feedAnimal(Animal animal) {
System.out.println("准备喂食: " + animal.getInfo());
animal.makeSound(); // 多态:不同动物不同叫声
System.out.println("喂食完成\n");
}
}
多态的优势
2. 类与对象的深入理解
2.1 类的定义和组成
**类(Class)**是对象的模板或蓝图,定义了对象的属性和行为。
类的组成要素
完整的类定义示例
public class Student {
// 静态变量:类变量,所有实例共享
private static int totalStudents = 0;
private static final String SCHOOL_NAME = "清华大学";
// 实例变量:每个对象独有
private String studentId; // 学号
private String name; // 姓名
private int age; // 年龄
private String major; // 专业
private double[] scores; // 成绩数组
// 静态初始化块
static {
System.out.println("Student类被加载");
}
// 实例初始化块
{
scores = new double[5]; // 默认5门课程
System.out.println("创建新的学生对象");
}
// 默认构造器
public Student() {
this("未知", "S000000", 18, "未定");
}
// 参数化构造器
public Student(String name, String studentId, int age, String major) {
this.name = name;
this.studentId = studentId;
this.age = age;
this.major = major;
totalStudents++; // 学生总数增加
}
// 实例方法:添加成绩
public void addScore(int courseIndex, double score) {
if (courseIndex >= 0 && courseIndex < scores.length) {
scores[courseIndex] = score;
} else {
throw new IndexOutOfBoundsException("课程索引超出范围");
}
}
// 实例方法:计算平均分
public double calculateAverage() {
double sum = 0;
int count = 0;
for (double score : scores) {
if (score > 0) { // 只计算已录入的成绩
sum += score;
count++;
}
}
return count > 0 ? sum / count : 0;
}
// 实例方法:获取学生信息
public String getStudentInfo() {
return String.format("学号: %s, 姓名: %s, 年龄: %d, 专业: %s, 平均分: %.2f",
studentId, name, age, major, calculateAverage());
}
// 静态方法:获取学生总数
public static int getTotalStudents() {
return totalStudents;
}
// 静态方法:获取学校名称
public static String getSchoolName() {
return SCHOOL_NAME;
}
// 静态方法:比较两个学生的平均分
public static int compareByAverage(Student s1, Student s2) {
return Double.compare(s1.calculateAverage(), s2.calculateAverage());
}
// Getter和Setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getStudentId() { return studentId; }
public int getAge() { return age; }
public void setAge(int age) {
if (age > 0 && age < 150) {
this.age = age;
} else {
throw new IllegalArgumentException("年龄必须在1-149之间");
}
}
public String getMajor() { return major; }
public void setMajor(String major) { this.major = major; }
// 重写Object类的方法
@Override
public String toString() {
return getStudentInfo();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Student student = (Student) obj;
return studentId.equals(student.studentId);
}
@Override
public int hashCode() {
return studentId.hashCode();
}
}
2.2 对象的创建和生命周期
对象创建过程
对象的使用示例
public class StudentDemo {
public static void main(String[] args) {
// 1. 创建对象
System.out.println("=== 创建学生对象 ===");
Student student1 = new Student("张三", "S001", 20, "计算机科学");
Student student2 = new Student("李四", "S002", 19, "软件工程");
Student student3 = new Student(); // 使用默认构造器
System.out.println("当前学生总数: " + Student.getTotalStudents());
System.out.println("学校名称: " + Student.getSchoolName());
// 2. 设置对象状态
System.out.println("\n=== 录入成绩 ===");
student1.addScore(0, 85.5); // 数学
student1.addScore(1, 92.0); // 英语
student1.addScore(2, 78.5); // 物理
student1.addScore(3, 88.0); // 化学
student1.addScore(4, 95.5); // 计算机
student2.addScore(0, 90.0);
student2.addScore(1, 87.5);
student2.addScore(2, 82.0);
student2.addScore(3, 91.5);
student2.addScore(4, 89.0);
// 3. 调用对象方法
System.out.println("\n=== 学生信息 ===");
System.out.println(student1.getStudentInfo());
System.out.println(student2.getStudentInfo());
System.out.println(student3.getStudentInfo());
// 4. 使用静态方法比较
System.out.println("\n=== 成绩比较 ===");
int comparison = Student.compareByAverage(student1, student2);
if (comparison > 0) {
System.out.println(student1.getName() + " 的平均分更高");
} else if (comparison < 0) {
System.out.println(student2.getName() + " 的平均分更高");
} else {
System.out.println("两人平均分相同");
}
// 5. 对象比较
System.out.println("\n=== 对象比较 ===");
Student student4 = new Student("王五", "S001", 21, "数学"); // 相同学号
System.out.println("student1.equals(student4): " + student1.equals(student4));
System.out.println("student1 == student4: " + (student1 == student4));
}
}
2.3 对象的内存模型
3. 消息传递机制
3.1 消息传递的概念
在面向对象编程中,消息传递是对象之间通信的方式。当一个对象需要另一个对象执行某个操作时,它会向目标对象发送一个消息。
消息的组成
3.2 消息传递的实现
// 消息发送者:银行客户
public class BankCustomer {
private String name;
private String customerId;
public BankCustomer(String name, String customerId) {
this.name = name;
this.customerId = customerId;
}
// 发送消息:存款
public void makeDeposit(BankAccount account, double amount) {
System.out.println(name + " 向账户发送存款消息");
// 消息:account.deposit(amount)
// 接收者:account对象
// 消息名:deposit
// 参数:amount
boolean result = account.deposit(amount);
if (result) {
System.out.println(name + " 存款成功");
} else {
System.out.println(name + " 存款失败");
}
}
// 发送消息:取款
public void makeWithdrawal(BankAccount account, double amount, String password) {
System.out.println(name + " 向账户发送取款消息");
// 消息:account.withdraw(amount, password)
boolean result = account.withdraw(amount, password);
if (result) {
System.out.println(name + " 取款成功");
} else {
System.out.println(name + " 取款失败");
}
}
// 发送消息:查询余额
public void checkBalance(BankAccount account, String password) {
System.out.println(name + " 向账户发送查询余额消息");
try {
// 消息:account.getBalance(password)
double balance = account.getBalance(password);
System.out.println(name + " 当前余额: " + balance);
} catch (SecurityException e) {
System.out.println(name + " 查询失败: " + e.getMessage());
}
}
}
// 消息传递示例
public class MessagePassingDemo {
public static void main(String[] args) {
// 创建对象
BankAccount account = new BankAccount("ACC001", "张三", "123456");
BankCustomer customer = new BankCustomer("张三", "CUST001");
System.out.println("=== 消息传递演示 ===");
// 客户向账户发送各种消息
customer.makeDeposit(account, 1000.0); // 存款消息
customer.checkBalance(account, "123456"); // 查询消息
customer.makeWithdrawal(account, 500.0, "123456"); // 取款消息
customer.checkBalance(account, "123456"); // 再次查询
customer.makeWithdrawal(account, 600.0, "wrong"); // 错误密码
}
}
3.3 消息传递的特点
4. 面向对象基本术语
4.1 核心术语表
| 术语 | 英文 | 定义 | 示例 |
|---|---|---|---|
| 类 | Class | 对象的模板或蓝图 | class Car { ... } |
| 对象 | Object | 类的实例 | Car myCar = new Car(); |
| 实例 | Instance | 对象的另一种称呼 | myCar是Car类的一个实例 |
| 属性 | Attribute/Field | 对象的数据成员 | private String color; |
| 方法 | Method | 对象的行为 | public void start() { ... } |
| 构造器 | Constructor | 创建对象的特殊方法 | public Car() { ... } |
| 继承 | Inheritance | 子类获得父类特性 | class SportsCar extends Car |
| 重写 | Override | 子类重新定义父类方法 | @Override public void start() |
| 重载 | Overload | 同名方法不同参数 | start() 和 start(boolean) |
| 多态 | Polymorphism | 同一接口多种实现 | Animal a = new Dog(); |
| 抽象 | Abstraction | 隐藏复杂性,突出本质 | abstract class Shape |
| 接口 | Interface | 行为规范的定义 | interface Drawable |
| 包 | Package | 类的命名空间 | package com.example; |
4.2 关系术语
4.3 访问控制术语
| 术语 | 含义 | 作用范围 |
|---|---|---|
| public | 公有的 | 所有类都可访问 |
| private | 私有的 | 仅本类可访问 |
| protected | 受保护的 | 本类、子类、同包类可访问 |
| default | 包私有的 | 同包类可访问 |
| static | 静态的 | 属于类而非实例 |
| final | 最终的 | 不可修改或继承 |
| abstract | 抽象的 | 不能实例化,需要子类实现 |
实践练习
练习1:设计一个图书管理系统
设计并实现一个简单的图书管理系统,要求:
- 创建Book类,包含书名、作者、ISBN、价格等属性
- 创建Library类,管理图书集合
- 实现添加图书、删除图书、查找图书等功能
- 使用封装原则保护数据
- 提供合适的构造器和方法
// 请在此处实现你的代码
public class Book {
// TODO: 实现Book类
}
public class Library {
// TODO: 实现Library类
}
练习2:继承关系设计
设计一个员工管理系统的继承体系:
- 创建Employee基类
- 创建Manager、Developer、Designer子类
- 每个子类有特有的属性和方法
- 实现多态的工资计算方法
- 演示向上转型和向下转型
练习3:多态应用
实现一个绘图系统:
- 创建Shape抽象类
- 创建Circle、Rectangle、Triangle子类
- 实现多态的面积计算和绘制方法
- 创建一个绘图管理器,统一处理不同形状
常见问题
Q: 什么时候使用继承,什么时候使用组合?
A:
- 使用继承:当存在明确的"is-a"关系时,如Dog is-a Animal
- 使用组合:当存在"has-a"关系时,如Car has-a Engine
- 一般建议优先考虑组合,因为它提供更好的灵活性
Q: 抽象类和接口有什么区别?
A:
- 抽象类可以有具体方法实现,接口只能有抽象方法(Java 8+支持默认方法)
- 类只能继承一个抽象类,但可以实现多个接口
- 抽象类可以有构造器,接口不能
- 抽象类表示"is-a"关系,接口表示"can-do"关系
Q: 如何理解多态的动态绑定?
A: 动态绑定是指在运行时根据对象的实际类型来决定调用哪个方法,而不是在编译时决定。这使得同一段代码可以处理不同类型的对象,提高了代码的灵活性。
总结
本章节深入讲解了面向对象的四大基本特征:
- 抽象:忽略无关细节,突出本质特征,是面向对象思维的基础
- 封装:数据和方法的结合,隐藏内部实现,提供统一接口
- 继承:代码重用和层次结构的建立,体现"is-a"关系
- 多态:同一接口的多种实现,提供运行时的灵活性
这四个特征相互关联,共同构成了面向对象编程的理论基础。理解和掌握这些概念对于后续学习面向对象分析设计、UML建模和设计模式都至关重要。
下一步
继续学习:003-类与对象
在下一章节中,我们将更深入地学习类与对象的高级特性,包括静态成员、内部类、对象的生命周期管理等内容。
更新时间: 2024-12-19 | 版本: v1.0.0

140

被折叠的 条评论
为什么被折叠?



