1

我正在阅读这篇关于 Java的优秀文章,他在其中指出 reference semanticsJon Skeet

我们假设存在一个名为 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!";
    }

}

当然,乔恩·斯基特不会错。但是我看到Stringbuffer可以通过将其传递给方法来更改它,因为它stringbuffer是可变的,这与 Skeet 发布的内容有点矛盾。请在这里澄清我的理解。

谢谢

4

2 回答 2

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,调用该对象上的方法,该方法在内部修改 a 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,而不是您作为参数传递的对象,证明对象是按值传递的。

于 2013-09-12T21:10:27.847 回答
1

这已经被讨论到死了。无论如何,给一些空闲时间,这是我的回应。这是我一直给出的相同回复,它链接到我一直链接到的关于评估策略的同一篇维基百科文章


我发现在描述 Java(以及许多其他语言,包括 Python、C# 等)中将对象作为参数传递的语义行为时,使用(对象)共享调用这个术语消除了大多数歧义。这样做清楚地确定了任何 [im] 可变性考虑。也就是说,方法内部访问的对象与外部是同一个对象在此调用/评估策略期间,没有创建、复制、克隆或以其他方式复制新对象 - 因此对对象所做的任何可变更改都会影响相同的“共享”对象

使用按值调用将对象的引用传递给对象是一个实现细节,尽管在Java 语言规范 (JLS)中对此进行了深入讨论。在 Java 的情况下,更“准确”地说Call by Value of the Reference - to the Object - 但这仍然被实现 - 作为Call by Value(然而,像ECMAScript这样的一些语言,在没有讨论任何此类实现细节的情况下,实现了完全相同的Call by Sharing语义。)

为避免混淆,我使用引用调用仅表示被调用函数可以通过重新分配参数来修改调用者的变量绑定(与对象可变性正交) 。不幸的是,引用引用在某些语言圈中被赋予了另一种和令人困惑的含义。

于 2013-09-12T21:19:00.687 回答