Appearance
🔒 封装
封装概述
封装(Encapsulation)是面向对象编程的三大特性之一,它隐藏了对象的内部实现细节,只对外提供必要的接口。
封装的目的
- 隐藏实现细节:保护对象的内部状态
- 提高安全性:防止外部直接访问和修改
- 便于维护:修改内部实现不影响外部使用
- 提高可读性:清晰的接口,易于理解
封装的实现
使用访问修饰符
java
public class BankAccount {
// private:私有成员,只能在类内访问
private String accountNumber;
private double balance;
// public:公共方法,提供访问接口
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
if (initialBalance >= 0) {
this.balance = initialBalance;
} else {
this.balance = 0;
}
}
// Getter 方法:获取账户号码(只读)
public String getAccountNumber() {
return accountNumber;
}
// Getter 方法:获取余额(只读)
public double getBalance() {
return balance;
}
// 存款方法:提供受控的修改接口
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("存款成功,金额: " + amount);
} else {
System.out.println("存款金额必须大于 0");
}
}
// 取款方法:提供受控的修改接口
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("取款成功,金额: " + amount);
return true;
} else {
System.out.println("取款失败:余额不足或金额无效");
return false;
}
}
}访问修饰符详解
private(私有)
只能在类内访问:
java
public class Example {
private int privateVar = 10;
private void privateMethod() {
System.out.println("私有方法");
}
public void publicMethod() {
System.out.println(privateVar); // ✅ 可以访问
privateMethod(); // ✅ 可以调用
}
}
// 在其他类中
Example e = new Example();
// e.privateVar; // ❌ 编译错误
// e.privateMethod(); // ❌ 编译错误default(包访问)
同一个包内可以访问:
java
package com.example;
public class Example {
int defaultVar = 10; // 默认访问修饰符
void defaultMethod() {
System.out.println("默认方法");
}
}
// 同一个包内的其他类
package com.example;
public class AnotherClass {
public void method() {
Example e = new Example();
System.out.println(e.defaultVar); // ✅ 可以访问
e.defaultMethod(); // ✅ 可以调用
}
}protected(受保护)
包内和子类可以访问:
java
package com.example;
public class Parent {
protected int protectedVar = 10;
protected void protectedMethod() {
System.out.println("受保护方法");
}
}
// 子类(可以访问)
package com.example;
public class Child extends Parent {
public void method() {
System.out.println(protectedVar); // ✅ 可以访问
protectedMethod(); // ✅ 可以调用
}
}public(公共)
所有地方都可以访问:
java
public class Example {
public int publicVar = 10;
public void publicMethod() {
System.out.println("公共方法");
}
}
// 任何地方都可以访问
Example e = new Example();
System.out.println(e.publicVar); // ✅ 可以访问
e.publicMethod(); // ✅ 可以调用Getter 和 Setter 方法
基本 Getter/Setter
java
public class Person {
private String name;
private int age;
// Getter 方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
// Setter 方法
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}带验证的 Setter
java
public class Student {
private String name;
private int age;
private double score;
public void setName(String name) {
if (name != null && !name.trim().isEmpty()) {
this.name = name;
} else {
throw new IllegalArgumentException("姓名不能为空");
}
}
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
} else {
throw new IllegalArgumentException("年龄必须在 0-150 之间");
}
}
public void setScore(double score) {
if (score >= 0 && score <= 100) {
this.score = score;
} else {
throw new IllegalArgumentException("分数必须在 0-100 之间");
}
}
}只读属性
java
public class Circle {
private final double radius; // final 修饰,不可修改
private final double area; // 计算属性
public Circle(double radius) {
this.radius = radius;
this.area = Math.PI * radius * radius;
}
// 只提供 Getter,不提供 Setter
public double getRadius() {
return radius;
}
public double getArea() {
return area;
}
}数据验证
输入验证
java
public class Product {
private String name;
private double price;
private int stock;
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("产品名称不能为空");
}
if (name.length() > 100) {
throw new IllegalArgumentException("产品名称不能超过 100 个字符");
}
this.name = name;
}
public void setPrice(double price) {
if (price < 0) {
throw new IllegalArgumentException("价格不能为负数");
}
this.price = price;
}
public void setStock(int stock) {
if (stock < 0) {
throw new IllegalArgumentException("库存不能为负数");
}
this.stock = stock;
}
}业务规则验证
java
public class BankAccount {
private double balance;
private static final double MIN_BALANCE = 0;
private static final double MAX_BALANCE = 1000000;
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("存款金额必须大于 0");
}
if (balance + amount > MAX_BALANCE) {
throw new IllegalArgumentException("账户余额不能超过 " + MAX_BALANCE);
}
balance += amount;
}
public void withdraw(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("取款金额必须大于 0");
}
if (balance - amount < MIN_BALANCE) {
throw new IllegalArgumentException("余额不足");
}
balance -= amount;
}
}封装的好处
1. 数据保护
java
// 不封装的问题
public class BadExample {
public int age;
}
// 使用
BadExample e = new BadExample();
e.age = -100; // ❌ 可以设置无效值
// 封装的好处
public class GoodExample {
private int age;
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
} else {
throw new IllegalArgumentException("无效的年龄");
}
}
}2. 实现细节隐藏
java
public class Stack {
private List<Integer> list = new ArrayList<>(); // 内部使用 ArrayList
public void push(int value) {
list.add(value);
}
public int pop() {
if (list.isEmpty()) {
throw new RuntimeException("栈为空");
}
return list.remove(list.size() - 1);
}
public boolean isEmpty() {
return list.isEmpty();
}
}
// 使用方不需要知道内部使用 ArrayList
// 以后可以改为 LinkedList,不影响使用方3. 易于维护
java
public class User {
private String firstName;
private String lastName;
// 旧版本:返回组合字符串
public String getName() {
return firstName + " " + lastName;
}
// 新版本:可以修改返回格式,不影响使用方
public String getName() {
return lastName + ", " + firstName; // 修改格式
}
}完整示例
java
public class EncapsulationExample {
public static void main(String[] args) {
// 银行账户示例
BankAccount account = new BankAccount("123456", 1000);
System.out.println("账户号码: " + account.getAccountNumber());
System.out.println("初始余额: " + account.getBalance());
account.deposit(500);
System.out.println("存款后余额: " + account.getBalance());
account.withdraw(200);
System.out.println("取款后余额: " + account.getBalance());
// 尝试无效操作
account.withdraw(2000); // 余额不足
account.deposit(-100); // 无效金额
}
}
class BankAccount {
private String accountNumber;
private double balance;
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
if (initialBalance >= 0) {
this.balance = initialBalance;
} else {
this.balance = 0;
}
}
public String getAccountNumber() {
return accountNumber;
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("存款成功,金额: " + amount);
} else {
System.out.println("存款金额必须大于 0");
}
}
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("取款成功,金额: " + amount);
return true;
} else {
System.out.println("取款失败:余额不足或金额无效");
return false;
}
}
}最佳实践
1. 使用 private 修饰成员变量
java
// ✅ 好的做法
public class GoodExample {
private String name;
private int age;
}
// ❌ 不好的做法
public class BadExample {
public String name; // 应该使用 private
public int age;
}2. 提供必要的 Getter/Setter
java
// ✅ 好的做法:提供受控访问
public class GoodExample {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
if (name != null && !name.isEmpty()) {
this.name = name;
}
}
}3. 在 Setter 中添加验证
java
// ✅ 好的做法:添加验证
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
} else {
throw new IllegalArgumentException("年龄无效");
}
}4. 使用 final 保护不可变属性
java
// ✅ 好的做法:使用 final
public class ImmutableExample {
private final String name; // 不可变
private final int age; // 不可变
public ImmutableExample(String name, int age) {
this.name = name;
this.age = age;
}
// 只提供 Getter,不提供 Setter
public String getName() {
return name;
}
}常见错误
1. 忘记封装
java
// ❌ 错误:直接暴露字段
public class BadExample {
public String name;
public int age;
}
// ✅ 正确:使用封装
public class GoodExample {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}2. Setter 中没有验证
java
// ❌ 错误:没有验证
public void setAge(int age) {
this.age = age; // 可以设置任何值
}
// ✅ 正确:添加验证
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
} else {
throw new IllegalArgumentException("年龄无效");
}
}下一步
掌握了封装后,可以继续学习:
💡 提示:封装是面向对象编程的基础,良好的封装可以保护数据,提高代码的可维护性和安全性
