面向对象编程(OOP) 是一个将现实世界抽象为一系列对象的编程范式,这些对象通过消息传递机制来互相交流和协作。
OOP 的主要特性包括四个基本概念:封装(Encapsulation)、继承(Inheritance)、多态(Polymorphism)以及抽象(Abstraction)。
封装(Encapsulation)
封装是一种将数据(属性)和行为(方法)绑定在一起的方法。
通过封装,可以隐藏对象的具体实现细节,仅暴露出有限的接口供外界访问。
优势
封装的优势:
- 增强安全性:隐藏内部实现细节,防止外部直接访问对象内部的数据,减少因误用导致的错误
这里我编写了一个 UserCredentials 类,来进行演示一下 增强安全性,分别体现在什么地方,代码如下:
**
/**
* @author BNTang
* @description 用户凭证类
*/
public class UserCredentials {
// 私有属性,外部无法直接访问
private String username;
private String password;
// 公有的构造函数,用于初始化用户名和密码
public UserCredentials(String username, String password) {
this.username = username;
this.password = password;
}
// 公有的方法,用于验证密码是否正确
public boolean authenticate(String inputPassword) {
return inputPassword != null && inputPassword.equals(this.password);
}
// 获得用户名的公有方法
public String getUsername() {
return this.username;
}
// 重置密码的方法,增加安全性校验
public void resetPassword(String oldPassword, String newPassword) {
if (authenticate(oldPassword)) {
this.password = newPassword;
System.out.println("密码重置成功。");
} else {
System.out.println("旧密码不正确,密码重置失败。");
}
}
// 私有的设置密码方法,外部无法访问
private void setPassword(String password) {
this.password = password;
}
}
在我提供的 UserCredentials 类的代码中,隐藏内部实现细节、防止外部直接访问对象内部的数据以及减少因误用导致的错误的概念都得到了实现。
- 隐藏内部实现细节:
**
private void setPassword(String password) {
this.password = password;
}
setPassword 方法是私有的 (private),意味着它只能在类内部被调用。外部代码不能直接调用此方法来设置密码,这正是隐藏内部实现细节的体现。
- 防止外部直接访问对象内部的数据
**
private String username;
private String password;
这段代码中,用户名 (username) 和密码 (password) 被声明为私有变量 (private),这意味着它们不能从类的外部直接访问,只能通过类提供的公有方法(如构造方法、getUsername、authenticate 和 resetPassword 方法等)来间接访问或修改。这种机制有效地保护了类的内部数据。
- 减少因误用导致的错误
**
public void resetPassword(String oldPassword, String newPassword) {
if (authenticate(oldPassword)) {
this.password = newPassword;
System.out.println("密码重置成功。");
} else {
System.out.println("旧密码不正确,密码重置失败。");
}
}
在 resetPassword 方法中,通过 authenticate 方法校验旧密码是否正确,只有在旧密码正确的情况下才允许用户设置新密码。这样的设计减少了因为外部代码错误使用(如直接设置密码而不进行旧密码验证)导致的安全问题,同时也确保了类内部数据的完整性和安全性。
- 提高复用性:封装后的对象可以作为一个黑盒被重复使用,无需关心对象内部的复杂逻辑
- 封装后的对象作为一个黑盒被重复使用体现在:
**
UserCredentials adminCredentials = new UserCredentials("admin", "adminPass");
UserCredentials userCredentials = new UserCredentials("user", "userPass");
// 在不同场景中重复使用对象:
if (adminCredentials.authenticate("adminPass")) {
// 执行管理员操作
}
if (userCredentials.authenticate("userPass")) {
// 执行用户操作
}
adminCredentials 和 userCredentials 是 UserCredentials 的实例,在创建它们之后可以多次使用其 authenticate 方法来验证密码,这里的实例就像是提供认证功能的黑盒,使用者不必关心里面的逻辑是怎样的。
- 无需关心对象内部的复杂逻辑体现在 :
**
private String username;
private String password;
private void setPassword(String password) {
this.password = password;
}
由于 username 和 password 属性被声明为私有的,外部代码不能直接访问或修改它们。设置密码的逻辑被隐藏在 setPassword 方法中,而这个方法也是私有的。外部代码需要通过公有方法如构造函数或 resetPassword 这些公有接口进行操作,因此外部代码不必关心如何存储或验证密码的内部逻辑,只需调用这些公有方法即可实现功能。
- 易于维护:封装的代码更易理解与修改,修改内部实现时不会影响到使用该对象的代码
- 封装的代码更易理解与修改体现在:
**
public boolean authenticate(String inputPassword) {
return inputPassword != null && inputPassword.equals(this.password);
}
public void resetPassword(String oldPassword, String newPassword) {
if (authenticate(oldPassword)) {
this.password = newPassword;
System.out.println("密码重置成功。");
} else {
System.out.println("旧密码不正确,密码重置失败。");
}
}
在 authenticate 和 resetPassword 这两个公有方法中,封装的代码很易于理解:一个用于验证密码,一个用于重新设置密码。如果我们需要修改密码的存储逻辑,只需修改这些方法的内部逻辑,而无需修改方法的签名或其他使用这些方法的代码。
- 修改内部实现时不会影响到使用该对象的代码体现在:
**
private String username;
private String password;
因为 username 和 password 是私有属性,所以它们对外部代码是不可见和不可访问的。我们可以在不改变任何使用 UserCredentials 对象的代码的情况下,自由改变这些属性的内部表示方法(比如对密码进行加密存储)。因为任何这样的改变都会被 UserCredentials 类的公共接口所封装和抽象化,从而不会泄露出去或者影响到依赖于这些公共接口的代码。
- 接口与实现分离:提供清晰的接口,使得对象之间的依赖关系只基于接口,降低了耦合度
- 提供清晰的接口体现在:
**
public boolean authenticate(String inputPassword);
public void resetPassword(String oldPassword, String newPassword);
public String getUsername();
这些公共方法形成了 UserCredentials 类的接口,它为外部代码提供了清晰的通信协议,明确了可以进行的操作。使用这个类的代码只需要知道这些方法的声明和预期行为,不需要了解它们背后的具体实现。
- 使得对象之间的依赖关系只基于接口体现在:
**
UserCredentials credentials = new UserCredentials("username", "password");
boolean valid = credentials.authenticate("password");
只要 authenticate 方法的接口保持不变,外部代码就可以正常工作,完全无须关心 UserCredentials 内部是如何处理认证逻辑的。
- 降低了耦合度体现在:
**
private void setPassword(String password) {
// 假设这里改用了一种新的加密方式来设置密码
this.password = encryptPassword(password);
}
即使改变了 setPassword 方法的内部实现(如加密),由于这个方法是私有的,外部代码不会受到影响。这种隔离提高了系统的模块化,使得各个部分可以独立变化而不互相干扰,从而降低了耦合度。
- 隐藏实现细节,简化接口:用户只需知道对象公开的方法,不必了解其内部的复杂过程
应用场景
封装的应用场景:
- 类的设计:在类定义时,通常将属性私有化(private),通过公共的方法(public methods)来访问和修改这些属性
- 模块化组件:在设计模块化的系统时,每个组件都通过封装来定义自己的行为和接口,使得系统更易于组合和扩展
- 库和框架的开发:开发者提供库和框架时,会通过封装隐藏复杂逻辑,只暴露简洁的 API 接口给其他开发者使用
- 隔离变化:将可能变化的部分封装起来,变化发生时,只需修改封装层内部,不影响外部使用