我实际上认为我对 Java 中传递值的实际工作方式有一个很好的了解,因为那是我通过的 SCJP 证书的一部分。直到今天,我在工作中发现了这样一个方法:
public void toCommand(Stringbuffer buf) {
buf.append("blablabla");
}
然后该方法的调用者使用如下函数:
StringBuffer buf = new StringBuffer();
toCommand(buf);
String str = buf.toString();
现在我认为该代码会给 str 赋值“”,但它实际上给了它方法中的值。这怎么可能?我认为事情在 Java 中不是那样工作的?
不管怎样……用 Java 编写这样的代码应该被认为是一种不好的做法,对吧?因为我可以想象它会带来一些困惑。
我实际上花了一些时间搜索这个,但我对这些消息来源的解释是,它不应该起作用。我错过了什么?
http://www.yoda.arachsys.com/java/passing.html
http://javadude.com/articles/passbyvalue.htm
塞巴斯蒂安
最佳答案
Java 是按值传递的。对象引用的值 是对象引用,而不是对象本身。因此 toCommand
方法接收到该值的副本,该副本是对该对象的引用——调用者引用的同一对象。
这与从两个变量引用一个对象时完全相同:
StringBuffer buf1;
StringBuffer buf2;
buf1 = new StringBuffer();
buf2 = buf1; // Still ONE object; there are two references to it
buf1.append("Hi there");
System.out.println(buf2.toString()); // "Hi there"
免费的 ASCII 艺术:
+--------------------+ buf1--------->| | | === The Object === | | | buf2--------->| Data: | | * foo = "bar" | | * x = 27 | | | +--------------------+
另一种理解方式是 JVM 有一个所有对象的主列表,由 ID 索引。我们创建一个对象 (buf1 = new StringBuffer();
),JVM 为该对象分配 ID 42 并将该 ID 存储在 buf1
中。每当我们使用 buf1
时,JVM 都会从中获取值 42 并在其主列表中查找对象,然后使用该对象。当我们执行 buf2 = buf1;
时,变量 buf2
得到值 42 的副本,所以当我们使用 buf2
,JVM 看到对象引用 #42 并使用相同的对象。这不是字面上的解释(尽管从平流层的角度来看,如果您将“JVM”理解为“JVM 和内存管理器和操作系统”,这不是一百万英里),但有助于思考对象引用实际上是什么。
在该背景下,您可以看到 toCommand
如何获取 reference(42 或其他),而不是实际的 StringBuffer
对象数据。因此对它的操作在主列表中查找它并改变它的状态(因为它保存状态信息并允许我们改变它)。调用者看到对象状态的变化,因为对象 保存状态,引用只是指向对象。
Either way... it should be considered a bad practice to write code like this in Java, right?
一点也不,这是正常的做法。如果不这样做,将很难使用 Java(或大多数其他 OOP 语言)。与 int
和 long
等基本类型相比,对象很大,因此移动它们的成本很高;对象引用是基元的大小,因此它们很容易传递。此外,拥有事物的副本会使系统的各个部分难以交互。拥有对共享对象的引用使其变得非常容易。
关于java - 更改 Java 中传递的参数(通过 ref、value 调用),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3165358/