我实际上认为我很清楚Java中的值传递是如何工作的,因为那是我通过的SCJP证书的一部分.直到今天,当我在工作时发现了这样的方法:
public void toCommand(Stringbuffer buf) {
buf.append("blablabla");
}
Run Code Online (Sandbox Code Playgroud)
然后该方法的调用者使用如下函数:
StringBuffer buf = new StringBuffer();
toCommand(buf);
String str = buf.toString();
Run Code Online (Sandbox Code Playgroud)
现在我认为该代码会给str值"",但实际上它给了它来自mehod的值.这怎么可能?我觉得Java中没有像那样的东西?
无论哪种方式......在Java中编写这样的代码应该被视为一种不好的做法,对吧?因为我可以想象它会带来一些混乱.
我实际上花了一些时间来搜索这个,但我对这些消息来源的解释是,它不应该工作.我错过了什么?
http://www.yoda.arachsys.com/java/passing.html
http://javadude.com/articles/passbyvalue.htm
Run Code Online (Sandbox Code Playgroud)
塞巴斯蒂安
T.J*_*der 11
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"
Run Code Online (Sandbox Code Playgroud)
无偿的ASCII艺术:
+--------------------+ buf1--------->| | | === The Object === | | | buf2--------->| Data: | | * foo = "bar" | | * x = 27 | | | +--------------------+
另一种思考方式是JVM具有所有对象的主列表,由ID索引.我们创建一个object(buf1 = new StringBuffer();
),JVM为对象分配ID 42并buf1
为我们存储该ID .每当我们使用时buf1
,JVM从中获取值42并在其主列表中查找对象,并使用该对象.当我们这样做时buf2 = buf1;
,变量buf2
获得值42 的副本,因此当我们使用时buf2
,JVM会看到对象引用#42并使用相同的对象.这不是一个字面上的解释(虽然从平流层的角度来看,如果你把"JVM"看作"JVM和内存管理器和操作系统",它不是一百万英里的距离),但有助于思考实际上是什么对象引用.
在该背景下,您可以看到如何toCommand
获取引用(42或其他),而不是实际的StringBuffer
对象数据.因此,对它的操作在主列表中查找并改变其状态(因为它保存状态信息并允许我们更改它).调用者看到对象状态的更改,因为对象保持状态,引用只指向对象.
无论哪种方式......在Java中编写这样的代码应该被视为一种不好的做法,对吧?
一点也不,这是正常的做法.如果不这样做,就很难使用Java(或大多数其他OOP语言).相比于类似的图元对象是大int
和long
,所以他们是走动昂贵; 对象引用是基元的大小,因此它们很容易传递.此外,拥有事物的副本使得系统的各个部分难以进行交互.引用共享对象使其变得非常简单.