我通过reference semantics
在Java Jon Skeet
上阅读了这篇优秀的article,其中他说明了
我们假设存在一个名为f的过程,该过程采用形式参数s。我们称该函数为其提供实际参数g。
主叫代码:
f( g )
功能:
procedure f( s ) begin -- body of the procedure end;
Java中的所有对象实例都在堆上分配,只能被访问 通过对象引用。如果我有以下内容:
StringBuffer g = new StringBuffer( "Hello" );
变量g不包含字符串“Hello”,它包含对包含字符串“Hello”的对象实例的引用(或指针)。
因此,如果我再调用f(g),f可以自由修改其形式参数s 使其指向另一个StringBuffer或将其设置为null。 函数f还可以通过附加“World”来修改StringBuffer。虽然这会更改StringBuffer的值,但StringBuffer的值不是实际参数g的值。
我的理解可能是错的。下面的程序确实改变了传递给方法的Stringbuffer
public class MutabilityStringBuffer {
public static void main(String[] args){
StringBuffer sb = new StringBuffer("hello");
System.out.println("String before append: "+ sb.toString());
addString(sb);
System.out.println("Sting after append "+ sb.toString());
String s = "hello";
System.out.println("String before append: "+ s);
addString(s);
System.out.println("Sting after append "+ s);
}
public static void addString(StringBuffer word){
word.append(" world!");
}
public static void addString(String word){
word+=" world!";
}
}
当然,Jon Skeet也不错。但是我看到Stringbuffer
可以通过传递给方法来改变,因为stringbuffer
是可变的,这与Skeet发布的内容有点矛盾。请在这里明确我的理解。
由于
答案 0 :(得分:2)
见评论
StringBuffer sb = new StringBuffer("hello"); // sb holds reference
System.out.println("String before append: "+ sb.toString()); // you print the value
addString(sb); // you use the reference to append to the StringBuffer
System.out.println("Sting after append "+ sb.toString()); // you print the value
String s = "hello"; // s holds a refernece
System.out.println("String before append: "+ s); // you print its value
addString(s); // // the word variable would hold a new reference inside the method
System.out.println("Sting after append "+ s); // you print its value
在这里
public static void addString(String word){
word+=" world!";
}
当您使用
重新分配时,传递给word
的引用的原始值会发生变化
word+=" world!";
它就是这样的
String word = [copy of value of the argument's reference];
word = word.toString() /* toString() is unnecessary, but just to make the point */ + " world";
其中String连接的结果是一个新的String对象,因此是一个新的引用。
以下
public static void addString(StringBuffer word){
word.append(" world!");
}
您访问word
引用的对象,在该对象上调用一个内部修改char[]
的方法。所以你改变了对象的值,而不是引用的值。更改引用看起来像
public static void addString(StringBuffer word){
word = new StringBuffer("Answer to the Ultimate Question of Life, the Universe, and Everything: ");
word.append("42");
}
append
对新的StringBuffer
对象执行,而不是作为参数传递的对象,证明对象是按值传递的。
答案 1 :(得分:1)
这已被讨论过死亡。无论如何,考虑到一些空闲时间,这是我的回答。它与我总是给出的响应相同,它链接到我总是链接到的wikipedia article on evaluation strategies。
我发现在描述Java中传递对象作为参数的语义行为(以及包括Python,C#等在内的许多其他语言)时,使用术语Call by (Object) Sharing可消除大多数歧义。这样做可以清楚地识别任何[im]可变性因素。也就是说,在方法中访问的对象与外部是同一个对象!在此调用/评估策略期间,没有创建,复制,克隆或以其他方式复制新对象 - 所以任何 mutable 更改都会使对象影响相同的“共享”对象。
使用Call by Value将对象的引用 - 传递给对象是一个实现细节,尽管在Java Language Specification (JLS)中有深入讨论。对于Java来说,更准确地说按引用的值调用对象 - 但这仍然是实现的 - 作为按值调用。 (但是,ECMAScript等某些语言在不讨论任何此类实现细节的情况下,实现完全相同的 Call by Sharing 语义。)
为了避免混淆,我使用Call by Reference仅表示被调用函数可以通过重新赋值修改调用者的变量绑定(与对象可变性正交) 参数。不幸的是,按参考呼叫在某些语言圈子中被赋予了另类 - 而且令人困惑的含义。