1. 封装特性基础解析
Java的封装特性是面向对象编程三大特性之一(封装、继承、多态)。简单来说,封装就是把对象的属性和操作属性的方法绑定在一起,形成一个独立的功能模块。就像我们日常使用的手机,你不需要知道内部电路如何工作,只需要通过屏幕和按键就能完成所有操作——这就是封装思想的现实体现。
封装的核心在于"隐藏实现,暴露接口"。在Java中,这主要通过访问修饰符来实现:
- private:仅本类可见
- protected:本包及子类可见
- public:全局可见
- 默认(不写修饰符):本包可见
一个标准的Java封装示例:
public class BankAccount { private double balance; // 私有属性 public void deposit(double amount) { // 公开方法 if(amount > 0) { balance += amount; } } public double getBalance() { // 公开方法 return balance; } }关键提示:良好的封装应该做到"属性私有化,方法公有化"。所有属性原则上都应该用private修饰,然后通过public的getter/setter方法进行访问控制。
2. 封装的核心价值与应用场景
2.1 数据保护与验证
封装最直接的价值是保护数据不被非法修改。通过将属性设为private,可以确保所有对属性的访问都必须通过我们定义的方法,从而可以在方法中加入验证逻辑。
例如修改上面的BankAccount类:
public void withdraw(double amount) { if(amount > 0 && amount <= balance) { balance -= amount; } else { System.out.println("取款金额不合法"); } }2.2 实现细节隐藏
封装允许我们隐藏类的内部实现细节。比如我们存储余额的变量从double改为BigDecimal以提高精度,外部调用者完全不需要知道这个变化:
public class BankAccount { private BigDecimal balance; // 内部实现变更 public void deposit(double amount) { // 方法签名不变,内部实现调整 if(amount > 0) { balance = balance.add(BigDecimal.valueOf(amount)); } } }2.3 降低耦合度
良好的封装减少了类与类之间的直接依赖。调用者只需要知道类提供了哪些方法,不需要关心内部如何实现。这使得代码更易于维护和修改。
3. 封装的实现细节
3.1 标准封装模式
Java中标准的封装实现包括:
- 所有属性设为private
- 为每个属性提供public的getter方法
- 为需要修改的属性提供public的setter方法
完整示例:
public class Person { private String name; private int age; public String getName() { return name; } public void setName(String name) { if(name != null && !name.trim().isEmpty()) { this.name = name; } } public int getAge() { return age; } public void setAge(int age) { if(age >= 0 && age <= 150) { this.age = age; } } }3.2 更灵活的封装控制
有时候我们不需要为所有属性提供getter/setter。比如一个只读属性:
public class Circle { private final double radius; // final修饰 public Circle(double radius) { this.radius = radius; } public double getRadius() { return radius; } // 不提供setRadius方法 public double getArea() { return Math.PI * radius * radius; } }3.3 方法封装
不仅属性需要封装,方法也可以封装。将复杂的算法拆分为多个private方法,只暴露一个public入口:
public class TaxCalculator { public double calculateTax(Income income) { double taxableIncome = calculateTaxableIncome(income); double taxRate = determineTaxRate(taxableIncome); return taxableIncome * taxRate; } private double calculateTaxableIncome(Income income) { // 计算应纳税所得额 } private double determineTaxRate(double taxableIncome) { // 确定税率 } }4. 封装的高级应用
4.1 不变类设计
通过封装可以实现不可变对象,这是并发编程中的重要技术:
public final class ImmutablePoint { private final int x; private final int y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } // 不提供setter方法 }4.2 Builder模式
当对象构造复杂时,可以使用Builder模式在保持封装性的同时提供灵活的构造方式:
public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; public static class Builder { // 必填参数 private final int servingSize; private final int servings; // 可选参数 private int calories = 0; public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val) { calories = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; } }4.3 防御性拷贝
对于可变对象的引用,需要进行防御性拷贝以防止封装被破坏:
public class Period { private final Date start; private final Date end; public Period(Date start, Date end) { this.start = new Date(start.getTime()); // 防御性拷贝 this.end = new Date(end.getTime()); if(this.start.compareTo(this.end) > 0) { throw new IllegalArgumentException(); } } public Date getStart() { return new Date(start.getTime()); // 返回拷贝 } public Date getEnd() { return new Date(end.getTime()); } }5. 封装实践中的常见问题
5.1 过度封装问题
封装不是越严格越好,过度封装会导致:
- 类变得难以使用
- 性能下降(频繁的方法调用)
- 代码复杂度增加
应该根据实际需求决定封装程度。比如工具类的方法通常设计为public static,因为它们本来就是为直接调用而设计的。
5.2 封装破坏情况
以下情况会破坏封装性:
在类中返回可变对象的引用
public class MyClass { private List<String> items = new ArrayList<>(); public List<String> getItems() { return items; // 错误!外部可以直接修改内部列表 } }正确做法:
public List<String> getItems() { return new ArrayList<>(items); // 返回副本 }在构造方法中未进行参数校验
public class Student { private String name; private int age; public Student(String name, int age) { this.name = name; // 应该校验name不为null this.age = age; // 应该校验age范围 } }
5.3 封装与继承的冲突
子类继承会破坏父类的封装,因为子类可以访问父类的protected成员。设计时需要权衡:
- 如果确定属性需要被子类访问,使用protected
- 如果不希望子类修改实现细节,使用private并提供protected方法供子类调用
6. 现代Java中的封装增强
6.1 Records类型
Java 14引入的Records提供了一种简洁的不可变数据封装方式:
public record Point(int x, int y) { // 编译器自动生成: // private final int x; // private final int y; // 构造函数 // getter方法 // equals/hashCode/toString }6.2 密封类(Sealed Classes)
Java 17的密封类可以控制哪些类可以继承它,进一步增强封装性:
public sealed class Shape permits Circle, Rectangle, Square { // 只有Circle、Rectangle、Square可以继承Shape } public final class Circle extends Shape { private double radius; // ... }6.3 模块系统(Java Module)
Java 9引入的模块系统提供了更高层次的封装控制,可以控制包级别的可见性:
module my.module { exports com.example.publicapi; // com.example.internal 包对外不可见 }7. 实际项目中的封装实践
7.1 领域驱动设计中的封装
在DDD中,聚合根负责维护其内部对象的完整性:
public class Order { private String orderId; private List<OrderItem> items = new ArrayList<>(); private OrderStatus status; public void addItem(Product product, int quantity) { if(status != OrderStatus.DRAFT) { throw new IllegalStateException("只能在草稿状态添加商品"); } items.add(new OrderItem(product, quantity)); } public void submit() { if(items.isEmpty()) { throw new IllegalStateException("订单不能为空"); } status = OrderStatus.SUBMITTED; } // 其他业务方法... }7.2 微服务中的封装
微服务架构强调强封装,服务内部实现对外完全隐藏:
@Service public class UserServiceImpl implements UserService { @Autowired private UserRepository userRepository; @Override public UserDTO getUserById(Long id) { User user = userRepository.findById(id) .orElseThrow(() -> new UserNotFoundException(id)); return convertToDTO(user); } private UserDTO convertToDTO(User user) { // 内部转换逻辑,对外隐藏实现细节 } }7.3 封装与测试
良好的封装实际上有助于单元测试:
public class ShoppingCartTest { @Test public void shouldCalculateTotalCorrectly() { ShoppingCart cart = new ShoppingCart(); cart.addItem(new Product("A", 10.0), 2); cart.addItem(new Product("B", 5.0), 3); assertEquals(35.0, cart.getTotal(), 0.001); } }封装良好的类更容易测试,因为:
- 每个类职责单一
- 依赖关系清晰
- 可以单独测试每个方法
8. 封装设计原则与最佳实践
8.1 最小化访问原则
给类成员分配尽可能严格的访问级别:
- 优先使用private
- 只有需要被子类访问的成员使用protected
- 只有确实需要对外暴露的成员使用public
8.2 迪米特法则(LoD)
一个对象应该对其他对象有最少的了解:
- 只与直接朋友通信
- 不要与陌生对象通信
- 自己的事情自己做
违反示例:
// 违反LoD public void printUserCity(User user) { System.out.println(user.getAddress().getCity()); }遵守LoD的写法:
public void printUserCity(User user) { System.out.println(user.getCity()); } // User类中 public String getCity() { return address.getCity(); }8.3 组合优于继承
优先使用组合而非继承来复用代码,可以更好地保持封装性:
// 不推荐 public class Stack extends ArrayList { public void push(Object o) { add(o); } public Object pop() { return remove(size()-1); } } // 推荐 public class Stack { private final List list = new ArrayList(); public void push(Object o) { list.add(o); } public Object pop() { return list.remove(list.size()-1); } }8.4 不变性设计
尽可能设计不可变对象,这是最强的封装形式:
- 所有字段设为final
- 不提供setter方法
- 如果字段是可变对象,返回防御性拷贝
public final class ImmutablePerson { private final String name; private final List<String> hobbies; public ImmutablePerson(String name, List<String> hobbies) { this.name = name; this.hobbies = Collections.unmodifiableList(new ArrayList<>(hobbies)); } public List<String> getHobbies() { return hobbies; // 返回不可变列表 } }9. 封装性能考量
9.1 方法调用开销
封装带来的方法调用会有一定性能开销,但在大多数情况下可以忽略。JVM会通过以下方式优化:
- 内联优化:将简单的方法调用直接替换为方法体
- JIT编译:热点代码会被编译为机器码
只有在极端性能敏感的场景才需要考虑减少封装层级。
9.2 对象拷贝开销
防御性拷贝会带来额外的内存分配和复制开销。解决方案:
- 对于确实需要频繁访问的大对象,考虑使用不可变对象
- 或者提供只读视图:
public List<String> getItems() { return Collections.unmodifiableList(items); }
9.3 现代JVM优化
现代JVM对封装良好的代码优化更好,因为:
- 清晰的访问边界有助于JVM进行优化决策
- 不变性可以帮助JVM进行更激进的优化
10. 封装与设计模式
10.1 工厂模式
封装对象创建过程:
public class PizzaFactory { public static Pizza createPizza(PizzaType type) { switch(type) { case MARGHERITA: return new MargheritaPizza(); case PEPPERONI: return new PepperoniPizza(); default: throw new IllegalArgumentException(); } } }10.2 策略模式
封装算法族,使它们可以互相替换:
public interface DiscountStrategy { double applyDiscount(double originalPrice); } public class ShoppingCart { private DiscountStrategy discountStrategy; public void setDiscountStrategy(DiscountStrategy strategy) { this.discountStrategy = strategy; } public double calculateTotal(List<Item> items) { double total = items.stream().mapToDouble(Item::getPrice).sum(); return discountStrategy.applyDiscount(total); } }10.3 外观模式
封装复杂子系统,提供简化接口:
public class ComputerFacade { private CPU cpu; private Memory memory; private HardDrive hardDrive; public void start() { cpu.freeze(); memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE)); cpu.jump(BOOT_ADDRESS); cpu.execute(); } }10.4 观察者模式
封装对象间的依赖关系:
public class WeatherStation { private List<WeatherObserver> observers = new ArrayList<>(); public void addObserver(WeatherObserver observer) { observers.add(observer); } public void setTemperature(float temperature) { observers.forEach(o -> o.update(temperature)); } }11. 封装与代码可维护性
11.1 变更隔离
良好的封装将变更限制在局部:
- 修改私有实现不会影响调用方
- 可以安全地重构内部实现
- 添加新功能不会破坏现有代码
11.2 可测试性
封装良好的代码更容易测试:
- 可以单独测试每个方法
- 可以mock依赖项
- 测试用例更聚焦
11.3 可读性
封装提升了代码的可读性:
- 每个类职责明确
- 方法命名反映业务意图
- 不需要关注实现细节即可理解代码
12. 封装与团队协作
12.1 接口契约
封装定义了清晰的接口契约:
- 团队成员只需要了解公开接口
- 可以并行开发不同模块
- 减少不必要的沟通成本
12.2 代码所有权
封装明确了代码所有权:
- 每个类有明确的所有者
- 修改需要经过所有者review
- 防止随意修改他人代码
12.3 API设计
良好的封装促进良好的API设计:
- 保持API简洁
- 向后兼容
- 文档友好
13. 封装与安全
13.1 数据保护
封装防止数据被非法访问或修改:
- 敏感数据设为private
- 提供受控的访问方法
- 在方法中加入安全校验
13.2 不变性与线程安全
封装良好的不可变对象天然线程安全:
public final class ImmutableValue { private final int value; public ImmutableValue(int value) { this.value = value; } public int getValue() { return value; } public ImmutableValue add(int delta) { return new ImmutableValue(value + delta); } }13.3 防御性编程
封装支持防御性编程实践:
- 参数校验
- 状态校验
- 异常处理
14. 封装的反模式
14.1 上帝对象
一个类做太多事情,破坏了封装:
- 类过于庞大
- 包含不相关功能
- 难以维护和测试
14.2 过度暴露
暴露太多内部细节:
- 太多public方法和属性
- 返回内部可变对象引用
- 暴露实现细节
14.3 虚假封装
看似封装,实则没有真正隐藏实现:
public class FakeEncapsulation { private List<String> items; public List<String> getItems() { return items; // 返回内部引用 } }15. 封装演进与未来趋势
15.1 从JavaBeans到Records
JavaBeans规范强调getter/setter,而Records提供了更简洁的封装方式:
// JavaBeans风格 public class Point { private int x; private int y; // getter/setter... } // Records风格 public record Point(int x, int y) {}15.2 函数式编程影响
函数式编程强调不变性和纯函数,这与封装理念高度契合:
- 不可变数据结构
- 无副作用函数
- 明确的数据流
15.3 模块化发展
Java模块系统提供了更大粒度的封装单元,未来可能会有:
- 更细粒度的访问控制
- 更好的模块间隔离
- 更安全的API暴露机制
16. 实际案例分析
16.1 JDK中的封装
以ArrayList为例:
- 内部使用Object[]存储元素
- 扩容机制对用户透明
- 提供清晰的公开API
public class ArrayList<E> { private static final int DEFAULT_CAPACITY = 10; private Object[] elementData; private int size; public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { // 内部扩容逻辑... } }16.2 Spring框架中的封装
Spring大量使用封装来简化开发:
- 隐藏复杂的配置
- 提供声明式编程模型
- 封装基础设施代码
@Service public class UserService { @Autowired private UserRepository userRepository; // 依赖注入 @Transactional public User createUser(UserDTO userDTO) { // 业务逻辑... } }16.3 企业应用中的封装
典型的三层架构体现了封装思想:
- 表示层:处理用户交互
- 业务逻辑层:封装业务规则
- 数据访问层:封装持久化细节
// 业务逻辑层 public class OrderService { private OrderRepository orderRepository; private InventoryService inventoryService; public Order placeOrder(OrderRequest request) { // 验证库存 // 计算价格 // 创建订单 // 更新库存 } }17. 封装与文档
17.1 Javadoc规范
良好的封装需要配套的文档:
/** * 表示银行账户的类 */ public class BankAccount { /** * 获取当前余额 * @return 账户余额,单位为元 */ public double getBalance() { return balance; } }17.2 API文档生成
封装良好的代码可以自动生成清晰的API文档:
- Swagger/OpenAPI
- JavaDoc
- Spring REST Docs
17.3 自描述代码
通过封装和良好的命名,使代码自描述:
- 类名和方法名反映业务含义
- 方法单一职责
- 避免魔法数字和字符串
18. 封装与重构
18.1 封装驱动重构
当发现以下情况时需要重构以改进封装:
- 类被频繁修改以适应不同调用者
- 多个调用者直接操作类的内部状态
- 类承担了不相关的职责
18.2 提取方法
将大方法拆分为小方法,每个方法封装一个逻辑:
// 重构前 public void processOrder(Order order) { // 验证逻辑... // 计算逻辑... // 保存逻辑... // 通知逻辑... } // 重构后 public void processOrder(Order order) { validateOrder(order); calculateOrder(order); saveOrder(order); notifyOrder(order); }18.3 提取类
当类变得太大时,提取新类来封装相关功能:
// 重构前 public class Customer { private String name; private String address; // 客户相关方法... private List<Order> orders; // 订单相关方法... } // 重构后 public class Customer { private String name; private String address; private CustomerOrders orders; // 只保留客户相关方法 } public class CustomerOrders { private List<Order> orders; // 订单相关方法... }19. 封装与设计原则
19.1 单一职责原则(SRP)
封装帮助实现SRP:
- 每个类只做一件事
- 将相关功能封装在一起
- 将不相关功能分离
19.2 开闭原则(OCP)
封装是实现OCP的基础:
- 对扩展开放
- 对修改关闭
- 通过封装内部实现,可以安全地添加新功能
19.3 接口隔离原则(ISP)
封装帮助实现ISP:
- 客户端不应依赖它不需要的接口
- 将大接口拆分为小接口
- 每个接口封装一组相关方法
20. 封装与代码质量
20.1 可维护性
封装提升代码可维护性:
- 变更影响范围小
- 问题定位容易
- 修改风险低
20.2 可扩展性
良好的封装使系统更易扩展:
- 可以安全添加新功能
- 现有代码不受影响
- 扩展点明确
20.3 可重用性
封装良好的组件更易重用:
- 清晰的接口
- 独立的职责
- 低耦合度
21. 封装与架构设计
21.1 分层架构
封装是分层架构的基础:
- 每层有明确职责
- 层与层之间通过接口通信
- 可以独立修改某一层
21.2 六边形架构
六边形架构强调核心业务逻辑的封装:
- 内部是核心领域
- 外部是适配器
- 通过端口交互
21.3 微服务架构
微服务是更大粒度的封装:
- 每个服务封装一组功能
- 服务间通过API通信
- 内部实现完全独立
22. 封装与领域驱动设计
22.1 聚合根
聚合根封装内部对象的访问:
public class Order { private String id; private List<OrderItem> items; public void addItem(Product product, int quantity) { // 封装业务规则 if(isDiscontinued(product)) { throw new IllegalStateException("不能添加已下架商品"); } items.add(new OrderItem(product, quantity)); } }22.2 值对象
值对象强调不变性和封装:
public final class Address { private final String street; private final String city; public Address(String street, String city) { this.street = street; this.city = city; } // 没有setter方法 }22.3 领域服务
封装不适合放在实体中的业务逻辑:
public class TransferService { public void transfer(Account from, Account to, Money amount) { // 封装转账逻辑 } }23. 封装与函数式编程
23.1 不可变数据
函数式编程强调不可变性,与封装理念一致:
public final class ImmutableList<T> { private final List<T> items; public ImmutableList(List<T> items) { this.items = List.copyOf(items); } public ImmutableList<T> add(T item) { List<T> newItems = new ArrayList<>(items); newItems.add(item); return new ImmutableList<>(newItems); } }23.2 纯函数
纯函数是封装的最佳实践:
- 不修改外部状态
- 相同输入总是相同输出
- 无副作用
23.3 高阶函数
高阶函数封装行为:
public <T> List<T> filter(List<T> list, Predicate<T> predicate) { List<T> result = new ArrayList<>(); for(T item : list) { if(predicate.test(item)) { result.add(item); } } return result; }24. 封装与并发编程
24.1 线程封闭
封装是实现线程安全的重要手段:
public class Person { private final String name; // final保证安全发布 private final int age; public Person(String name, int age) { this.name = name; this.age = age; } }24.2 同步封装
将同步逻辑封装在类内部:
public class Counter { private int count; private final Object lock = new Object(); public void increment() { synchronized(lock) { count++; } } }24.3 并发集合
java.util.concurrent包提供了线程安全的集合实现,封装了复杂的并发控制:
public class Cache { private final ConcurrentMap<String, Object> map = new ConcurrentHashMap<>(); public void put(String key, Object value) { map.put(key, value); } }25. 封装与设计演进
25.1 从贫血模型到充血模型
贫血模型:
// 数据类 public class Order { public Long id; public List<OrderItem> items; public BigDecimal total; } // 服务类 public class OrderService { public void calculateTotal(Order order) { // 计算逻辑 } }充血模型(更好的封装):
public class Order { private Long id; private List<OrderItem> items; private BigDecimal total; public void calculateTotal() { // 计算逻辑 } }25.2 从过程式到面向对象
过程式代码:
public class OrderProcessor { public void process(Order order) { validate(order); calculate(order); save(order); } }面向对象(更好的封装):
public class Order { public void process() { validate(); calculate(); save(); } }25.3 从CRUD到领域模型
CRUD风格:
public class OrderService { public void createOrder(OrderDTO dto) { Order order = new Order(); // 设置各个字段... orderRepository.save(order); } }领域模型(更好的封装):
public class Order { public static Order create(Customer customer, List<Product> products) { Order order = new Order(); // 封装创建逻辑... return order; } }26. 封装与代码异味
26.1 特征嫉妒
当一个类过多地访问另一个类的内部数据,可能是封装不良的信号:
// OrderProcessor过多访问Order内部数据 public class OrderProcessor { public void process(Order order) { if(order.items.size() > MAX_ITEMS) { throw new ValidationException(); } // ... } } // 更好的封装 public class Order { public void validate() { if(items.size() > MAX_ITEMS) { throw new ValidationException(); } } }26.2 基本类型偏执
过度使用基本类型而不是封装领域概念:
// 不好的封装 public class Person { private String name; private String street; private String city; private String zipCode; } // 更好的封装 public class Person { private String name; private Address address; } public class Address { private String street; private String city; private ZipCode zipCode; }26.3 数据类
只有数据没有行为的类通常是封装不良的表现:
// 数据类 public class Rectangle { public double width; public double height; } // 更好的封装 public class Rectangle { private double width; private double height; public double area() { return width * height; } }27. 封装与测试驱动开发(TDD)
27.1 TDD促进良好封装
TDD自然地引导出良好的封装:
- 测试只关注公开接口
- 迫使你思考如何设计可测试的接口
- 避免过度暴露实现细节
27.2 封装使测试更容易
良好的封装:
- 使单元测试更聚焦
- 减少测试间的耦合
- 使mock更容易
27.3 测试作为封装验证
测试可以验证封装是否合理:
- 如果测试需要访问太多内部状态,可能是封装不良
- 如果测试过于脆弱(经常因内部改变而失败),可能是封装不足
28. 封装与代码审查
28.1 审查封装性
代码审查时应关注:
- 是否有不必要的public成员
- 是否返回了可变内部状态引用
- 是否有足够的数据保护
28.2 常见封装问题
审查时常见的封装问题:
- 数据类(只有getter/setter)
- 暴露实现细节
- 违反迪米特法则
28.3 改进建议
发现封装问题时可以建议:
- 将public改为private并提供合适的方法
- 使用防御性拷贝
- 提取方法或类来更好地封装逻辑
29. 封装与性能优化
29.1 封装与内联
现代JVM可以优化简单的方法调用:
// 即使这样写,JVM也会优化 public class Point { private int x; public int getX() { return x; } } // 实际执行时可能被优化为直接字段访问29.2 封装与缓存
良好的封装可以安全地实现缓存:
public class ExpensiveCalculation { private volatile Result cachedResult; public Result calculate() { Result result = cachedResult; if(result == null) { synchronized(this) { result = cachedResult; if(result == null) { result = doExpensiveCalculation(); cachedResult = result; } } } return result; } }29.3 封装与延迟加载
封装支持安全的延迟加载:
public class LazyValue<T> { private volatile T value; private final Supplier<T> supplier; public T get() { T result = value; if(result == null) { synchronized(this) { result = value; if(result == null) { result = supplier.get(); value = result; } } } return result; } }30. 封装与设计决策
30.1 何时放松封装
在某些情况下可以适当放松封装:
- 性能关键路径
- 内部工具类
- 测试专用方法(使用@VisibleForTesting等注解)
30.2 封装与YAGNI
平衡封装与YAGNI原则:
- 不要过度设计封装
- 但基本的封装应该从一开始就做好
- 可以在需要时增强封装
30.3 封装与KISS
保持封装的简单性:
- 不要为了封装而封装
- 使用最简单的可行方案
- 在简单性和封装性之间找到平衡
31. 封装与设计文档
31.1 类职责文档
每个类应该有清晰的职责说明:
/** * 负责订单的生命周期管理 * - 创建新订单 * - 计算订单总价 * - 处理订单状态转换 */ public class OrderManager { // ... }31.2 方法契约文档
公开方法应该有明确的契约:
/** * 添加商品到订单 * @param product 要添加的商品,不能为null * @param quantity 数量,必须大于0 * @return 更新后的订单总价 * @throws IllegalArgumentException 如果参数无效 * @throws OrderClosedException 如果订单已关闭 */ public BigDecimal addItem(Product product, int quantity) { // ... }31.3 封装边界文档
在架构图中明确标注封装边界:
- 模块边界
- 包边界
- 类边界
32. 封装与代码生成
32.1 Lombok注解
Lombok可以简化封装代码:
@Getter @Setter @ToString public class Person { private String name; private int age; }32.2 记录类型
Java 16+的记录类型自动生成封装代码:
public record Point(int x, int y) {}32.3 IDE生成
IDE可以生成getter/setter等封装代码,但需要谨慎使用:
- 不要盲目生成所有getter/setter
- 考虑是否真的需要暴露某个属性
- 可以在生成后手动调整
33. 封装与设计质量指标
33.1 耦合度
良好的封装降低耦合度:
- 类之间通过接口而非实现交互
- 减少类之间的直接依赖
- 模块间依赖清晰
33.2 内聚性
封装提高内聚性:
- 类内部元素紧密相关
- 每个类有单一明确的职责
- 功能相关的代码组织在一起
33.3 可维护性指数
封装良好的代码有更高的可维护性指数:
- 圈复杂度低
- 代码重复少
- 模块化程度高