在 java 中实现只读属性的核心做法是:将字段声明为 private,不提供对应的 setter 方法,仅暴露 getter 方法。这样外部代码只能读取该字段的值,无法直接修改它。如果希望属性完全不可变(包括对象内部状态也不可变),则需结合 final 关键字和不可变对象的设计模式。

基础只读属性如何实现
对于基本类型或简单的引用类型,只需定义 private 字段并只提供 getter。若字段是集合等可变对象,getter 返回的应是防御性拷贝,防止外部通过引用修改内部状态。

public class ReadOnlyExample {
private final String name;
private final List<String> items;
public ReadOnlyExample(String name, List<String> items) {
this.name = name;
// 防御性拷贝,防止外部修改传入的列表
this.items = new ArrayList<>(items);
}
public String getName() {
return name;
}
public List<String> getItems() {
// 返回新副本,确保调用者无法修改内部列表
return Collections.unmodifiableList(items);
}
}上述代码中,name 是基本类型的字符串,由于 String 本身是不可变的,直接返回即可。items 是 List,通过构造器初始化后,getItems() 返回的是 unmodifiableList 包装后的视图,任何尝试修改的操作都会抛出 UnsupportedOperationException。
立即学习“Java免费学习笔记(深入)”;
复杂对象中的只读属性处理
当属性是自定义的可变对象时,必须确保 getter 不暴露内部引用,或者返回对象的深拷贝/不可变视图。否则,即使没有 setter,外部仍可通过获取到的引用修改对象内容。

import java.util.Collections;
import java.util.List;
public class ComplexReadOnly {
private final Address address;
public ComplexReadOnly(Address address) {
this.address = address;
}
// 错误做法:直接返回可变对象引用
// public Address getAddress() { return address; }
// 正确做法:返回不可变视图或深拷贝
public Address getAddress() {
// 假设 Address 有 copy 方法
return address.copy();
}
}
class Address {
private String city;
public Address(String city) { this.city = city; }
public Address copy() { return new Address(this.city); }
}在 ComplexReadOnly 类中,address 是一个可变对象。如果直接返回 address 引用,外部代码可以调用 address.setCity(...) 来修改内部状态。因此,必须通过返回副本或使用不可变包装来保证只读语义。这种设计确保了封装的完整性,即使没有 setter,也能防止意外修改。
基于程序中某个输入、条件或结果的具体问题
当程序中存在多个只读属性且需要验证其一致性时,例如检查所有只读字段是否满足特定业务规则,可以通过遍历 getter 返回值进行校验。若某个只读属性依赖于其他计算结果,应确保在构造阶段完成计算,避免运行时状态不一致。


















