5

在准备 SCJP(或现在已知的 OCPJP)考试时,我被一些关于传递(参考)值和不变性的模拟问题所吸引。

我的理解是,当您将变量传递给方法时,您传递的是表示如何获取该变量的位的副本,而不是实际的对象本身。

您发送的副本指向同一个对象,因此您可以修改该对象(如果它是可变的),例如附加到 StringBuilder。但是,如果您对不可变对象执行某些操作,例如递增 Integer,则局部引用变量现在指向一个新对象,而原始引用变量仍然没有注意到这一点。

在这里考虑我的例子:

public class PassByValueExperiment
{

    public static void main(String[] args)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("hello");
        doSomething(sb);
        System.out.println(sb);


        Integer i = 0;
        System.out.println("i before method call : " + i);
        doSomethingAgain(i);
        System.out.println("i after method call: " + i);
    }

    private static void doSomethingAgain(Integer localI)
    {
        // Integer is immutable, so by incrementing it, localI refers to newly created object, not the existing one
        localI++;
    }

    private static void doSomething(StringBuilder localSb)
    {
        // localSb is a different reference variable, but points to the same object on heap
        localSb.append(" world");
    }
}

问题:是否只有不可变对象以这种方式运行,而可变对象可以通过值传递引用进行修改?我的理解是正确的还是这种行为有其他好处?

4

2 回答 2

6

在语言级别上,可变对象和不可变对象之间没有区别——不可变性纯粹是类 API 的属性。

这个事实只是被允许++在包装器类型上使用的自动装箱弄糊涂了,使它看起来像对对象的操作 - 但事实并非如此,正如您自己注意到的那样。相反,它是一种语法糖,用于将值转换为基元、递增、将结果转换回包装器类型并将对该变量的引用分配给变量。

因此,区别实际上在于++运算符在原语和包装器上使用时所做的事情,这与参数传递没有任何关系。

于 2011-12-02T13:17:21.987 回答
5

Java 本身不知道对象是否不可变。在每种情况下,您都传递参数的,它可以是引用值,也可以是原始值。更改参数的值永远不会产生任何影响。

现在,澄清一下,这段代码不会改变参数的值:

localSb.append(" world");

这会改变参数值所引用的对象内的数据,这是非常不同的。请注意,您没有为localSb.

从根本上说,您需要了解:

  • 表达式的值(变量、参数、参数等)始终是引用或原始值。它永远不是一个对象。
  • Java总是使用按值传递的语义。自变量的值成为参数的初始值。

一旦你仔细考虑了这些事情,并在你的脑海中区分了“变量”、“值”和“对象”的概念,事情就会变得更加清晰。

于 2011-12-02T13:11:01.827 回答