只读属性是封装中保护核心数据最直接有效的方式之一——它从接口层面切断外部篡改可能,让关键字段“可看不可改”,本质是通过不提供setter、配合private final字段、不可变类型、防御性拷贝等协同设计实现编译期杜绝写入。

只读属性是封装中保护核心数据最直接有效的方式之一——它从接口层面切断外部篡改的可能,让关键字段“可看不可改”。
只读属性的本质是访问契约的单向约束
声明为 private 的字段本身已不可见,但仅靠私有化还不够。真正的保护来自不提供 setter 方法,使外部完全失去写入入口。比如用户 ID、创建时间、订单编号这类一旦生成就不应变更的数据,必须走只读路径:
- 字段声明为
private final String orderNo;,强制在构造时初始化 - 只提供
public String getOrderNo() { return orderNo; } - 不生成任何
setOrderNo(...),连编译期都无法调用
只读 ≠ 静态不变,而是逻辑上不可变
有些字段虽未用 final 修饰,但业务规则要求其只读(如账户初始余额)。这时仍应只暴露 getter,并在 setter 上抛出明确异常或直接省略:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 避免用 public 字段“假装只读”,那只是表面安全
- 不要在 setter 中写
throw new UnsupportedOperationException()—— 这属于运行时防御,不如编译期杜绝 - 若需派生值(如“是否已激活”),应另设
isActive()方法,而非暴露原始状态字段
配合类型与构造设计,堵死绕过漏洞
只读性不是靠方法缺失来维持的,而是整套设计协同的结果:
立即学习“Java免费学习笔记(深入)”;
- 敏感字段类型优先选不可变类:用
LocalDateTime替代Date,用ImmutableList包装集合返回值 - getter 中对集合类做防御性拷贝或返回不可修改视图:
Collections.unmodifiableList(roles) - 禁止通过反射或序列化绕过:在关键类中重写
readObject,校验只读字段是否被非法重置
大厂实践中只读属性的典型场景
真实系统里,只读属性不是技术炫技,而是明确表达业务语义:
-
userId:注册后固定,后续所有操作都基于此标识,绝不允许更新 -
createdAt:由系统自动生成,代表事实发生时间,人工修改即破坏审计可信度 -
transactionId:分布式唯一标识,重复或篡改将导致幂等失效和账务错乱 -
status:某些状态机中,初始状态只读,后续只能通过confirm()、cancel()等受控方法变更

















