形参是方法定义时括号内的占位变量,仅在方法内有效;实参是调用时传入的具体值或引用。java只支持值传递:基本类型传值副本,引用类型传地址副本,形参修改不影响实参变量本身,但可改变其指向对象的状态。

形参和实参不是一回事,但有明确对应关系
形参是方法定义时写在括号里的变量名,比如 public void printName(String name) 中的 name;它不占实际数据,只起占位和类型声明作用。实参是调用时真正传进去的值,比如 printName("张三") 中的 "张三" ——这个字符串对象已经存在堆内存里,实参就是把它“送过去”。形参在方法开始执行时才分配栈空间,方法结束就立刻释放,生命周期很短。
Java只有值传递,但“值”的含义分两种
这是最容易混淆的点:Java **没有引用传递**,所有参数都是按值传递。区别在于——
- 基本类型(int、char、boolean等):实参的“值”就是具体数字或真假,形参拿到的是这个值的完整副本。改形参,不影响实参变量本身。
- 引用类型(String、ArrayList、自定义类等):实参的“值”其实是对象在堆中的地址(比如 0x7f8a...),形参拿到的是这个地址的副本。两个变量指向同一块堆内存,所以通过形参修改对象内部状态(如 list.add()、obj.name = "新名字"),实参看到的就是变化后的结果。
注意:如果在方法里让形参指向新对象(比如 list = new ArrayList()),这只是改变了形参变量存的地址,原实参变量仍指着老对象,不受影响。
内存视角看一次典型调用
以 void update(User u) 调用 update(new User("李四")) 为例:
- new User("李四") 在堆中创建对象,假设地址为 0x1000;
- main 方法栈帧里有个变量 u(实参),它存储的值是 0x1000;
- 调用 update 时,JVM 把 0x1000 这个数值复制一份,放进 update 方法的栈帧,作为形参 u 的初始值;
- 此时两个 u 都存着 0x1000,都能读写堆中那个 User 对象;
- 若在 update 中执行 u.setName("王五"),堆中对象被改了,main 里的 u 读出来也是"王五";
- 若执行 u = new User("赵六"),只是 update 栈帧里的 u 改成指向新地址(如 0x2000),main 的 u 依然指着 0x1000。
写方法时怎么避免意外副作用
多数时候我们希望方法不悄悄改外面的对象,尤其处理集合或可变类时:
- 对入参集合做只读操作,或明确注释“本方法会修改传入列表”;
- 需要内部修改又不想影响外部,可以主动克隆:比如传入 List,方法开头写 List
copy = new ArrayList(original) ; - 优先使用不可变类型(如 String、LocalDateTime、Collections.unmodifiableList);
- 方法返回新对象而非修改旧对象,更符合函数式习惯,也更安全。
理解这点,就不会再问“为什么我传了个 ArrayList 进去,外面也被清空了”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











