对 Java 的值传递和不变性的困惑
在准备 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");
}
}
问题:是否只有不可变对象才会以这种方式运行,而可变对象可以通过值传递引用进行修改?我的理解是否正确,或者这种行为还有其他好处吗?
In preparation for the SCJP (or OCPJP as it's now known) exam, I'm being caught out by some mock questions regarding pass-by-(reference)value and immutability.
My understanding, is that when you pass a variable into a method, you pass a copy of the bits that represent how to get to that variable, not the actual object itself.
The copy that you send in, points to the same object, so you can modify that object if its mutable, such as appending to a StringBuilder. However, if you do something to an immutable object, such as incrementing an Integer, the local reference variable now points to a new object, and the original reference variable remains oblivious to this.
Consider my example here :
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");
}
}
Question : Is it only immutable objects that behave in such a manner, and mutable objects can be modified by pass-by-value references? Is my understanding correct or are there other perks in this behaviour?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在语言级别上,可变对象和不可变对象没有区别——不变性纯粹是类 API 的一个属性。
这个事实只是被自动装箱混淆了,自动装箱允许在包装类型上使用
++
,使其看起来像是对对象的操作 - 但事实并非如此,正如您自己所注意到的那样。相反,它是一种语法糖,用于将值转换为基元,递增该值,将结果转换回包装类型并将对该值的引用分配给变量。因此,区别实际上在于
++
运算符在原语和包装器上使用时所做的操作,后者与参数传递没有任何关系。There is no difference between mutable and immautable objects on the language level - immutability is purely a property of a class's API.
This fact is only muddled by autoboxing which allows
++
to be used on wrapper types, making it look like an operation on the object - but it's not really, as you've noticed yourself. Instead, it's syntactic sugar for converting the value to a primitive, incrementing that, converting the result back to the wrapper type and assigning a reference to that to the variable.So the distinction is really between what the
++
operator does when it's used on a primitive vs. a wrapper, which doesn't have anything to do with parameter passing.Java 本身不知道对象是否不可变。在每种情况下,您都会传递参数的值,它可以是引用或原始值。更改参数的值永远不会产生任何影响。
现在,需要澄清的是,此代码不会更改参数的值:
它会更改参数值所引用的对象内的数据,这是非常不同的。请注意,您没有为
localSb
分配新值。从根本上讲,您需要了解:
一旦你仔细思考这些事情,并在头脑中区分“变量”、“值”和“对象”的概念,事情就会变得更加清晰。
Java itself has no idea of whether an object is immutable or not. In every case, you pass the value of the argument, which is either a reference or a primitive value. Changing the value of the parameter never has any effect.
Now, to clarify, this code does not change the value of the parameter:
That changes the data within the object that the value of the parameter refers to, which is very different. Note that you're not assigning a new value to
localSb
.Fundamentally, you need to understand that:
Once you think about those things carefully, and separate in your mind the concepts of "variable", "value" and "object", things should become clearer.