Java里private和public如何选择_Java封装级别使用说明

必须用 private 修饰类的内部状态字段,以防止外部绕过业务逻辑篡改数据;非静态字段默认应为 private,构造器、getter/setter 和工具方法按需暴露,但字段本身不例外。

什么时候必须用 private

类的内部状态(比如字段)只要不被外部直接读写,就该默认设为 private。这不是为了“藏起来”,而是防止调用方绕过业务逻辑篡改数据。比如一个 BankAccount 类里的 balance 字段,如果公开成 public,别人就能直接写 account.balance = -1000000,跳过所有校验。

  • 所有非静态字段优先声明为 private
  • 构造器、getter/setter、工具方法按需暴露,但字段本身不例外
  • 子类继承时,private 成员不可见——这是设计意图,不是限制

public 方法该暴露哪些?

一个方法是否该是 public,取决于它是否构成该类对外承诺的契约。比如 String.trim()public,因为它是 String 类明确提供给所有调用方使用的功能;而 ArrayList 内部的 grow() 就是 private,它只服务自身扩容逻辑,换实现就可能消失。

  • 仅当该方法被其他类(尤其是不同包下的类)合理依赖时,才设为 public

  • 避免为“以后可能用到”提前暴露 public 方法
  • 接口方法必须是 public(Java 9+ 允许 private 接口方法,但那是另一回事)

别碰 protected 和包级访问的模糊地带

很多开发者把 protected 当作“比 private 松一点”的快捷方式,结果导致子类意外依赖父类实现细节。更常见的是误用包级访问(即不写修饰符),以为“同包内安全”,实际却让测试类、工具类、甚至未来新增的同包子类轻易穿透封装边界。

  • protected 应仅用于明确支持继承扩展的钩子方法(如 Swing 组件的 paintComponent()
  • 包级访问等价于“全包信任”,一旦包变大或职责混杂,维护成本陡增
  • 宁可加一层 public 的委托方法,也不开放 protected 字段

IDE 和 Lombok 会掩盖封装问题

IntelliJ 自动生成 getter/setter 时,默认生成 public 方法,但不会提醒你:这个字段真需要被外部修改吗?Lombok 的 @Data 更危险——它一键生成所有 public getter/setter,等于把所有字段都“合法裸奔”。曾经有项目用 @Data 修饰 DTO,结果前端传个负数 ID,后端毫无感知地存进数据库。

public class Order {
    private Long id;          // ✅ 应保持 private
    private BigDecimal total; // ✅ 同上

    // ❌ @Data 会自动生成 public void setTotal(BigDecimal t),但 total 应由业务方法计算得出
}
  • 用 Lombok 时,优先选 @Getter + 手动写业务 setter,或用 @Accessors(fluent = true) 配合私有化
  • IDE 生成 getter 前,先问一句:“谁真的需要读它?有没有更安全的只读视图?”
  • 单元测试应尽量走 public 接口,而不是反射访问 private 字段——后者是在测试实现,不是测试行为
封装不是靠关键字堆出来的,是靠每次声明字段或方法时,心里多问一句:“这个东西,到底该对谁负责?”