Java只有值传递,传的是对象引用的副本而非引用本身;因此修改对象属性生效,但重新赋值不影响原变量,String因不可变性表现类似基本类型。

Java里对象参数到底是传值还是传引用
Java只有值传递,没有引用传递——这是结论,不是说法。哪怕你传的是一个 new ArrayList(),JVM 也只把那个对象的引用值(即内存地址的拷贝)传进去,不是把引用本身传过去。
为什么修改对象属性生效,但重新赋值却不影响原变量
因为形参拿到的是引用的副本,它和实参指向同一个堆内存地址,所以通过这个副本去调用 add()、set() 等方法,改的是同一块对象数据;但如果你在方法里写了 list = new ArrayList(),只是让这个副本指向了新地址,原变量还稳稳指着老地方。
常见错误现象:
– 方法里 obj.setName("xxx") 生效,但 obj = new Person() 后原变量没变
– 误以为“对象是引用传递”,结果在方法里 map.clear() 意外清空了外部 map
- 使用场景:需要在方法内修改对象状态(如填充 list、更新 bean 字段)时安全;但想彻底替换对象时必须靠返回值
- 参数差异:
String虽是对象,但不可变,任何“修改”本质都是新建对象,所以表现像基本类型传值 - 性能影响:传对象引用值开销极小(通常 4 或 8 字节),比深拷贝强太多;别为了“避免修改”而手动 clone,除非真有并发或隔离需求
怎么安全地避免方法内意外修改原始对象
不是靠“传引用/传值”机制来防,而是靠设计约束和显式复制。
立即学习“Java免费学习笔记(深入)”;
- 如果入参是工具类方法(如
parseJson(User user)),文档或注释明确写清“不修改入参”,比纠结传递机制更实际 - 真要隔离,用不可变对象:比如把
User改成所有字段final+ 无 setter,或用Records(Java 14+) - 必要时浅拷贝:对简单 POJO 可用
BeanUtils.copyProperties(src, dest);注意ArrayList的new ArrayList(original)是浅拷贝,内部元素仍共享引用 - 别依赖
clone():它默认是浅拷贝,且要求类实现Cloneable,容易抛CloneNotSupportedException
基本类型和对象参数在字节码层面其实一样
javac 编译后,int 和 Object 都是往栈帧局部变量表里存一个值——前者是数值本身,后者是引用值(即地址)。JVM 不区分“传的是什么”,只管“传了一个值”。所谓“对象传引用”,是开发者对这个值含义的解读,不是语言规范定义的行为。
容易被忽略的地方:很多人卡在“为什么 Integer i = 100; modify(i); 不生效”,其实是因为 Integer 在 -128~127 有缓存,i++ 会触发自动装箱成新对象,本质还是值传递导致副本脱离原变量——不是“包装类特殊”,而是所有对象都这样。








