java只有值传递,即方法接收基本类型值或引用地址的副本;不可变类型(如string)安全,可变类型(如arraylist)修改会影响原对象,设计时应明确契约、防御拷贝、返回专用结果对象。

Java 中只有值传递,这个底层事实直接影响接口参数的设计逻辑——不是“要不要传对象”,而是“怎么传才安全、可控、意图明确”。理解这一点,能避免多数空指针、意外修改、线程不安全等问题。
明确区分可变与不可变参数
接口方法接收的永远是值(基本类型值 or 引用地址的副本),但能否修改内容取决于参数本身是否可变:
- 传 String、Integer、LocalDateTime 等不可变类型:放心使用,方法内任何“修改”(如
str.toUpperCase())都会生成新对象,原实参完全不受影响; - 传 StringBuilder、ArrayList、自定义 POJO 等可变类型:调用方必须意识到——方法内部调用
list.add()或user.setName()会直接改变原始对象状态; - 设计建议:若接口语义上不应修改入参,应在文档中声明“参数仅作读取”,必要时在方法开头做防御性拷贝(如
new ArrayList(inputList))。
避免让调用方承担“引用生命周期”责任
因为传的是引用地址的副本,接口无法控制原始引用何时被置为 null 或重新赋值。所以:
- 不要在接口中假设“传进来的 List 之后还会被其他地方使用”——它可能在你执行完后立刻被清空或丢弃;
- 若需长期持有某对象(如缓存、监听器注册),应显式复制关键字段或使用不可变封装(如
ImmutableList.copyOf(list)); - 对回调类接口(如
Consumer<t></t>),尤其注意:lambda 捕获的变量若后续被修改,可能引发意料外行为,建议捕获 final 或 effectively final 变量。
用泛型和契约约束替代“传什么类型”的模糊性
值传递机制下,类型信息不会丢失,但运行时擦除可能掩盖问题。因此接口设计要主动强化契约:
- 用泛型限定输入范围:比如
<t extends comparable>> void sort(List<t> data)</t></t>,比裸写List data更清晰表达“需要可比较”; - 对可能为 null 的引用参数,用
@NonNull/@Nullable注解明确约定,配合 IDE 和 Lint 工具提前拦截错误; - 复杂参数优先封装为不可变 DTO(如
SearchRequest),而不是塞一堆零散参数(String keyword, int page, int size, boolean fuzzy),既提升可读性,也天然规避部分误改风险。
返回值设计要匹配值传递的“单向性”
既然 Java 不支持真正的引用传递,就别指望通过参数“带回多个结果”。更自然的做法是:
- 返回专用结果对象(如
Result<user error></user>或ResponseEntity<user></user>),而非靠修改入参“输出”; - 若确实需多值返回,用 record(Java 14+)或简单静态内部类,例如:
record Validation(boolean ok, String msg) {}; - 避免设计类似
void process(Data data, ResultHolder holder)这种依赖外部 holder 修改的接口——它违背了值传递的直观性,也增加测试和维护成本。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











