1、封装的概述 (封装(Encapsulation) – 如何隐藏信息、保护数据)
- 封装 的具体实现:
- 1、将属性(数据 / 特征)和操作数据的方法(行为)整合在一个类中。
- 2、隐藏内部实现细节,仅通过受控接口 (如:public 方法) 访问数据。
- 用 private 修饰符,隐藏不愿意被外部操作的 field、方法、构造器。
- 用 public 修饰符,暴露愿意给外部调用的方法。
- 封装的核心:保护数据的 完整性和安全性,防止外部直接修改或破坏数据。
- 实现封装的关键在于,绝对不能让其它类中的方法,直接访问这个类的实例字段。
- 程序只能通过对象的方法与对象数据进行交互。
- 封装为对象赋予了“黑盒”特征,这是提高重用性和可靠性的关键。
- 示例:
- 对 4 个属性的访问进行了限制。
- 调用者只允许通过类中的 6 个方法来访问或修改钱包里的数据。
public class Wallet {
private String id;
private long createTime;
private BigDecimal balance;
private long balanceLastModifiedTime;
public Wallet() {
this.id = IdGenerator.getInstance().generate();
this.createTime = System.currentTimeMillis();
this.balance = BigDecimal.ZERO;
this.balanceLastModifiedTime = System.currentTimeMillis();
}
public String getId() {
return this.id;
}
public long getCreateTime() {
return this.createTime;
}
public BigDecimal getBalance() {
return this.balance;
}
public long getBalanceLastModifiedTime() {
return this.balanceLastModifiedTime;
}
public void increaseBalance(BigDecimal increasedAmount) {
if (increasedAmount.compareTo(BigDecimal.ZERO) < 0) {
throw new InvalidAmountException("...");
}
this.balance.add(increasedAmount);
this.balanceLastModifiedTime = System.currentTimeMillis();
}
public void decreaseBalance(BigDecimal decreasedAmount) {
if (decreasedAmount.compareTo(BigDecimal.ZERO) < 0) {
throw new InvalidAmountException("...");
}
if (decreasedAmount.compareTo(this.balance) > 0) {
throw new InsufficientAmountException("...");
}
this.balance.subtract(decreasedAmount);
this.balanceLastModifiedTime = System.currentTimeMillis();
}
}
- 分析:
- 从业务的角度,id、createTime 在创建钱包时就确定好了,之后不应该再被改动。
- 在 Wallet 类中,未暴露 id、createTime 两个属性的任何修改方法(如:set 方法 )。
- 对于钱包余额 balance 这个属性,从业务的角度来说,只能增或减,不会被重新设置。
- 在 Wallet 类中,只暴露 increaseBalance 和 decreaseBalance 方法,未暴露 set 方法。
- balanceLastModifiedTime 属性完全是跟 balance 属性的修改操作绑定在一起 的。
- 只有在 balance 修改时,balanceLastModifiedTime 属性才会被修改。
- 所以,把 balanceLastModifiedTime 属性的修改操作完全封装在 increaseBalance 和 decreaseBalance 两个方法中,不对外暴露任何修改这个属性的方法和业务细节。
- 这样也保证了 balance 和 balanceLastModifiedTime 两个数据的一致性。
2、封装的级别
- 1、类封装。
- 2、包级别封装:将具有共同特征的类放在同一个包下边。
3、类封装的分类
- 1、数据封装 :为了确保数据的完整性和安全性。以成员变量的形式呈现。
- 1、将数据(属性)的具体存储细节隐藏(如:私有属性 private)。
- 2、仅通过公开的方法(如:public 修饰的 getter/setter)提供对数据的受控访问。
public class BankAccount {
private double balance;
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public void withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
}
}
public double getBalance() {
return balance;
}
}
BankAccount account = new BankAccount();
account.deposit(1000);
account.withdraw(500);
System.out.println("余额: " + account.getBalance());
- 2、行为封装:以方法的形式 呈现。
- 将复杂操作的具体实现细节隐藏,仅暴露一个简单的接口(方法 或 函数)。
- 隐藏方法的实现步骤(如:算法、流程)。
- 通过方法名和参数定义功能,内部逻辑可自由修改。
- 常用于模块化复杂逻辑(如:工具类方法)。
- 方法内部的逻辑还是用 面向过程 的思想去实现的 。
- 封装行为,调用者只需知道方法是“做什么的”,无需关心方法是“怎么实现的”。
- 如:调用者只需调用 sort(),无需了解快速排序的具体实现。
- 排序算法的内部细节被隐藏,未来可替换为其他算法 (如:归并排序),不影响调用方代码。
public class MathUtils {
public static void sort(int[] array) {
quickSort(array, 0, array.length - 1);
}
private static void quickSort(int[] array, int low, int high) {
if (low < high) {
int pivot = partition(array, low, high);
quickSort(array, low, pivot - 1);
quickSort(array, pivot + 1, high);
}
}
private static int partition(int[] array, int low, int high) {
}
}
int[] numbers = {5, 3, 8, 1};
MathUtils.sort(numbers);
4、封装所需的支持
- 封装特性,需要编程语言本身提供一定的语法机制来支持。这个语法机制就是访问权限控制。
- 如:private、public 等关键字就是 Java 语言中的访问权限控制语法 。
访问控制符(是否可以访问) | 本类中 | 同包不同类(含无继承) | 不同包(有继承关系) | 不同包中(无继承关系) |
---|
private | 是 | × | × | × |
默认(什么都不写) | 是 | 是 | × | × |
proteted | 是 | 是 | 是 | × |
public | 是 | 是 | 是 | 是 |
5、封装的优点
- 1、增加复用。
- 2、单一职责,减少变动带来的风险。
- 3、只能通过暴露的方法来访问数据 。
- 4、隐藏类的实例细节,增加安全性、方便修改和实现。