在Java里getter和setter方法如何设计_Java属性封装规范说明

Java中getter/setter需严格遵循JavaBeans规范并兼顾不可变性、线程安全与防御性编程:命名须准确(如isActive而非getActive),参数须校验与深拷贝,返回值须防泄漏,非必要字段不暴露访问器。

Java里getter和setter不是“写不写”的问题,而是“怎么写才不埋雷”的问题——字段类型、可见性、不可变性、线程安全

、继承关系,任意一个没对齐,后续就容易出 NullPointerExceptionConcurrentModificationException 或子类意外覆写行为。

getter/setter命名必须严格遵循JavaBeans规范

IDE自动生成的通常没问题,但手写或重构时容易错。JVM和主流框架(如Jackson、Spring BeanUtils)都依赖这套命名约定,一旦偏差,序列化或反射赋值就会静默失败。

  • get + 首字母大写的属性名:如 private String userName;getUserName(),不是 getUsername()getuserName()
  • 布尔类型优先用 is 前缀:如 private boolean active;isActive();若强行写成 getActive(),Jackson可能无法识别
  • 下划线或驼峰混合字段(如 max_retry_count)不能直接转:应映射为 getMaxRetryCount(),而非 getMax_retry_count()

setter里必须做参数校验和防御性拷贝

暴露了setter就等于把字段控制权部分交出去,不做校验等于邀请调用方传入 null、非法状态或可变对象引用。

public void setConfig(Map config) {
    if (config == null) {
        throw new IllegalArgumentException("config must not be null");
    }
    // 防御性拷贝,避免外部修改影响内部状态
    this.config = new HashMap<>(config);
}
  • 基本类型和不可变类(StringLocalDateTime)可直接赋值
  • 集合、数组、自定义可变对象必须深拷贝或包装为不可修改视图(Collections.unmodifiableList()
  • 不要在setter里触发复杂逻辑(如远程调用、持久化),那属于业务方法,不是setter职责

getter返回值要防止内部状态泄露

看似只读的方法,如果返回了可变对象引用,调用方就能绕过封装直接改你的私有字段。

private List tags = new ArrayList<>();

// ❌ 危险:返回原始引用
public List getTags() {
    return tags; // 外部可 add/remove,破坏封装
}

// ✅ 安全:返回不可修改副本
public List getTags() {
    return Collections.unmodifiableList(tags);
}
  • 集合类一律返回不可修改包装或新副本(new ArrayList(tags)
  • 数组用 Arrays.copyOf() 而非直接返回引用
  • 若字段是 Optional,getter应返回 Optional 而非解包后的值,否则空值语义丢失

哪些字段不该配getter/setter

不是所有私有字段都需要暴露访问器。加了反而破坏设计意图,还增加维护成本。

  • 纯计算字段(如 getFullName()firstNamelastName 拼接)——应作为普通方法,不对应真实字段
  • 仅构造时初始化的不可变字段(final)——只需getter,禁止setter;且getter不应返回可变内容的引用
  • 被注解标记为忽略序列化的字段(如 @JsonIgnore@Transient)——若同时提供setter,框架可能误用,引发数据不一致
  • 内部状态标志(如 isProcessing)——若只在本类内使用,不对外暴露,就不该有getter/setter

真正难的不是写出语法正确的getter/setter,而是在每次添加前问一句:这个访问是否必要?返回的是值还是危险的引用?校验边界是否覆盖了所有非法输入?这些判断比敲键盘花的时间多得多。