Java 基础 整理
Java 是值传递,为什么对象里的值还能改?
先区分对象和引用,再解释参数传递、重新赋值以及 equals 与 ==。
先回答:Java 到底是值传递,还是引用传递?
Java 始终是值传递。基本类型传递值的副本,引用类型传递引用值的副本。调用方和方法内的两个引用可以指向同一个对象,因此能通过引用修改对象;重新给形参赋值,不会把调用方变量也改掉。
先分清变量和对象
引用变量保存的是一个可以定位对象的引用值。把变量传给方法,并不是把这个变量本身交给方法,而是复制它保存的引用值。两个变量因此可能指向同一个对象。
方法内执行 user.name = "new",修改的是共享对象;执行 user = new User(),修改的是方法内那个局部变量所保存的引用。
一个小例子
static void change(List<String> values) {
values.add("A");
values = new ArrayList<>();
values.add("B");
}
List<String> list = new ArrayList<>();
change(list);
System.out.println(list); // [A]
add("A") 修改了原来的列表。之后的重新赋值只影响形参 values,最后添加的 B 属于另一个列表。
顺便说清 == 和 equals
- 比较基本类型时,== 比较值;涉及数值类型时,要留意类型转换规则。
- 比较引用时,== 判断是否指向同一个对象,不比较对象的业务内容。
- equals 的语义由类实现决定。Object 默认实现仍按对象身份判断,String 则按字符内容判断。
- 重写 equals 时通常必须同时重写 hashCode,否则放入 HashMap、HashSet 等集合后会破坏约定。
面试追问
如果把入参改成 Integer 或 String,为什么看起来改不动?这两个类的对象不可变,方法中常见的“修改”实际上是让局部引用指向另一个对象。不可变对象和参数传递是两个相关但不同的问题。
延伸阅读
以上为个人整理。涉及版本实现和线上使用时,请结合对应文档与实际测试判断。