在这段代码中,我将一个名称传递给一个方法,该方法修改字符串文字name
,而不是对象本身,当代码退出该方法时,对象(由哈希码标识)是相同的,但不是在方法中修改的name
。
我该怎么解释呢?
public class ObjectContentsPassByReferenceApp {
private static void modifyObject(Bus bus) {
bus.setName("SBS Transit");
}
public static void main(String args[]) {
Bus bus;
bus = new Bus();
bus.setName("Trans Island Bus");
System.out.println("Bus initially set to (hashcode): " + bus);
System.out.println("Bus name: " + bus.getName());
modifyObject(bus);
System.out.println("After calling modifyObject (hashcode): " + bus);
System.out.println("Bus name: " + bus.getName());
}
}
class Bus {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
运行结果:
Bus initially set to (hashcode): sg.java.concepts.passByReference.Bus@8d2ed0
Bus name: Trans Island Bus
After calling modifyObject (hashcode): sg.java.concepts.passByReference.Bus@8d2ed0
Bus name: SBS Transit
您没有覆盖hashCode
-因此它将使用java.lang.Object
中的实现,其中在对象的生命周期中不会变化。你也没有覆盖equals
…这意味着只有当a
和b
引用完全相同的对象时,a.equals(b)
才会为Bus a
和Bus b
返回true
,而不是具有相同名称的对象。
请注意,代码中的名称表明Java使用引用传递。它不会——它总是使用按值传递,但这些值总是原语或引用——而不是实际的对象。对于简单赋值也是如此。
在您的代码中,您正在创建一个单个Bus
对象。把它想象成一辆现实生活中的巴士。它上面有一个名字,上面有一个浮雕的序列号(后者是哈希码)。当您调用该方法时,这是告诉该方法如何获取到Bus
对象—它不是创建一个新的Bus
。该方法使用一个新名称来覆盖名称,但这对序列号没有任何影响,序列号与以前相同。
还要注意name
不是字符串字面值——它是字符串变量。它的初始值来自字符串字面值,但稍后更改值对原始字符串对象没有任何影响。
如果你想让哈希码依赖于一个变量(即name
),那么你需要覆盖hashCode
方法。一个简单的例子:
public class Bus {
private String name;
public int hashCode() {
return name.hashCode();
}
}
java.lang.Object
中的hashCode
实现不使用任何类型的反射来查找变量,因此您几乎总是需要重写hashCode
。
请注意,如果您覆盖hashCode
,则应该始终覆盖equals
,反之亦然。
有很多有用的工具可以帮助你实现hashCode
。查看Apache Commons lang,看看它们的HashCodeBuilder
类。