☰
Java封装特性详解:原理、实践与设计模式
2026/10/14 8:45:09 网站建设 项目流程

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中标准的封装实现包括:

  1. 所有属性设为private
  2. 为每个属性提供public的getter方法
  3. 为需要修改的属性提供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 封装破坏情况

以下情况会破坏封装性:

  1. 在类中返回可变对象的引用

    public class MyClass { private List<String> items = new ArrayList<>(); public List<String> getItems() { return items; // 错误!外部可以直接修改内部列表 } }

    正确做法:

    public List<String> getItems() { return new ArrayList<>(items); // 返回副本 }
  2. 在构造方法中未进行参数校验

    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); } }

封装良好的类更容易测试,因为:

  1. 每个类职责单一
  2. 依赖关系清晰
  3. 可以单独测试每个方法

8. 封装设计原则与最佳实践

8.1 最小化访问原则

给类成员分配尽可能严格的访问级别:

  1. 优先使用private
  2. 只有需要被子类访问的成员使用protected
  3. 只有确实需要对外暴露的成员使用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 不变性设计

尽可能设计不可变对象,这是最强的封装形式:

  1. 所有字段设为final
  2. 不提供setter方法
  3. 如果字段是可变对象,返回防御性拷贝
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 对象拷贝开销

防御性拷贝会带来额外的内存分配和复制开销。解决方案:

  1. 对于确实需要频繁访问的大对象,考虑使用不可变对象
  2. 或者提供只读视图:
    public List<String> getItems() { return Collections.unmodifiableList(items); }

9.3 现代JVM优化

现代JVM对封装良好的代码优化更好,因为:

  1. 清晰的访问边界有助于JVM进行优化决策
  2. 不变性可以帮助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 可测试性

封装良好的代码更容易测试:

  1. 可以单独测试每个方法
  2. 可以mock依赖项
  3. 测试用例更聚焦

11.3 可读性

封装提升了代码的可读性:

  1. 每个类职责明确
  2. 方法命名反映业务意图
  3. 不需要关注实现细节即可理解代码

12. 封装与团队协作

12.1 接口契约

封装定义了清晰的接口契约:

  1. 团队成员只需要了解公开接口
  2. 可以并行开发不同模块
  3. 减少不必要的沟通成本

12.2 代码所有权

封装明确了代码所有权:

  1. 每个类有明确的所有者
  2. 修改需要经过所有者review
  3. 防止随意修改他人代码

12.3 API设计

良好的封装促进良好的API设计:

  1. 保持API简洁
  2. 向后兼容
  3. 文档友好

13. 封装与安全

13.1 数据保护

封装防止数据被非法访问或修改:

  1. 敏感数据设为private
  2. 提供受控的访问方法
  3. 在方法中加入安全校验

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 防御性编程

封装支持防御性编程实践:

  1. 参数校验
  2. 状态校验
  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 函数式编程影响

函数式编程强调不变性和纯函数,这与封装理念高度契合:

  1. 不可变数据结构
  2. 无副作用函数
  3. 明确的数据流

15.3 模块化发展

Java模块系统提供了更大粒度的封装单元,未来可能会有:

  1. 更细粒度的访问控制
  2. 更好的模块间隔离
  3. 更安全的API暴露机制

16. 实际案例分析

16.1 JDK中的封装

以ArrayList为例:

  1. 内部使用Object[]存储元素
  2. 扩容机制对用户透明
  3. 提供清晰的公开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大量使用封装来简化开发:

  1. 隐藏复杂的配置
  2. 提供声明式编程模型
  3. 封装基础设施代码
@Service public class UserService { @Autowired private UserRepository userRepository; // 依赖注入 @Transactional public User createUser(UserDTO userDTO) { // 业务逻辑... } }

16.3 企业应用中的封装

典型的三层架构体现了封装思想:

  1. 表示层:处理用户交互
  2. 业务逻辑层:封装业务规则
  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文档:

  1. Swagger/OpenAPI
  2. JavaDoc
  3. Spring REST Docs

17.3 自描述代码

通过封装和良好的命名,使代码自描述:

  1. 类名和方法名反映业务含义
  2. 方法单一职责
  3. 避免魔法数字和字符串

18. 封装与重构

18.1 封装驱动重构

当发现以下情况时需要重构以改进封装:

  1. 类被频繁修改以适应不同调用者
  2. 多个调用者直接操作类的内部状态
  3. 类承担了不相关的职责

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:

  1. 每个类只做一件事
  2. 将相关功能封装在一起
  3. 将不相关功能分离

19.2 开闭原则(OCP)

封装是实现OCP的基础:

  1. 对扩展开放
  2. 对修改关闭
  3. 通过封装内部实现,可以安全地添加新功能

19.3 接口隔离原则(ISP)

封装帮助实现ISP:

  1. 客户端不应依赖它不需要的接口
  2. 将大接口拆分为小接口
  3. 每个接口封装一组相关方法

20. 封装与代码质量

20.1 可维护性

封装提升代码可维护性:

  1. 变更影响范围小
  2. 问题定位容易
  3. 修改风险低

20.2 可扩展性

良好的封装使系统更易扩展:

  1. 可以安全添加新功能
  2. 现有代码不受影响
  3. 扩展点明确

20.3 可重用性

封装良好的组件更易重用:

  1. 清晰的接口
  2. 独立的职责
  3. 低耦合度

21. 封装与架构设计

21.1 分层架构

封装是分层架构的基础:

  1. 每层有明确职责
  2. 层与层之间通过接口通信
  3. 可以独立修改某一层

21.2 六边形架构

六边形架构强调核心业务逻辑的封装:

  1. 内部是核心领域
  2. 外部是适配器
  3. 通过端口交互

21.3 微服务架构

微服务是更大粒度的封装:

  1. 每个服务封装一组功能
  2. 服务间通过API通信
  3. 内部实现完全独立

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 纯函数

纯函数是封装的最佳实践:

  1. 不修改外部状态
  2. 相同输入总是相同输出
  3. 无副作用

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自然地引导出良好的封装:

  1. 测试只关注公开接口
  2. 迫使你思考如何设计可测试的接口
  3. 避免过度暴露实现细节

27.2 封装使测试更容易

良好的封装:

  1. 使单元测试更聚焦
  2. 减少测试间的耦合
  3. 使mock更容易

27.3 测试作为封装验证

测试可以验证封装是否合理:

  1. 如果测试需要访问太多内部状态,可能是封装不良
  2. 如果测试过于脆弱(经常因内部改变而失败),可能是封装不足

28. 封装与代码审查

28.1 审查封装性

代码审查时应关注:

  1. 是否有不必要的public成员
  2. 是否返回了可变内部状态引用
  3. 是否有足够的数据保护

28.2 常见封装问题

审查时常见的封装问题:

  1. 数据类(只有getter/setter)
  2. 暴露实现细节
  3. 违反迪米特法则

28.3 改进建议

发现封装问题时可以建议:

  1. 将public改为private并提供合适的方法
  2. 使用防御性拷贝
  3. 提取方法或类来更好地封装逻辑

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 何时放松封装

在某些情况下可以适当放松封装:

  1. 性能关键路径
  2. 内部工具类
  3. 测试专用方法(使用@VisibleForTesting等注解)

30.2 封装与YAGNI

平衡封装与YAGNI原则:

  1. 不要过度设计封装
  2. 但基本的封装应该从一开始就做好
  3. 可以在需要时增强封装

30.3 封装与KISS

保持封装的简单性:

  1. 不要为了封装而封装
  2. 使用最简单的可行方案
  3. 在简单性和封装性之间找到平衡

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 封装边界文档

在架构图中明确标注封装边界:

  1. 模块边界
  2. 包边界
  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等封装代码,但需要谨慎使用:

  1. 不要盲目生成所有getter/setter
  2. 考虑是否真的需要暴露某个属性
  3. 可以在生成后手动调整

33. 封装与设计质量指标

33.1 耦合度

良好的封装降低耦合度:

  1. 类之间通过接口而非实现交互
  2. 减少类之间的直接依赖
  3. 模块间依赖清晰

33.2 内聚性

封装提高内聚性:

  1. 类内部元素紧密相关
  2. 每个类有单一明确的职责
  3. 功能相关的代码组织在一起

33.3 可维护性指数

封装良好的代码有更高的可维护性指数:

  1. 圈复杂度低
  2. 代码重复少
  3. 模块化程度高

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询