我一直以为Java使用pass-by-reference. 但是,我读了一篇博客文章,声称Java使用pass-by-value. 我不认为我明白作者所做的区别。
什么是解释?
我一直以为Java使用pass-by-reference. 但是,我读了一篇博客文章,声称Java使用pass-by-value. 我不认为我明白作者所做的区别。
什么是解释?
当前回答
区别,或可能只是我记得我曾经像原始海报一样的印象是这样的:Java总是通过价值,Java中的所有对象(除了原始对象)都是参考。
其他回答
我只是注意到你提到我的文章。
了解这一点的关键是,这样的事情
Dog myDog;
它不是狗;它实际上是指向狗。在Java中使用“参考”这个术语是非常误导性的,这引起了大多数混乱。
这意味着,当你有
Dog myDog = new Dog("Rover");
foo(myDog);
您基本上将创建的狗对象的地址转移到 foo 方法。
public void foo(Dog someDog) {
someDog.setName("Max"); // AAA
someDog = new Dog("Fifi"); // BBB
someDog.setName("Rowlf"); // CCC
}
让我们看看发生了什么。
这里是钥匙。
记住 myDog 是指标,而不是真正的狗,答案是 NO. myDog 仍然有值 42;它仍然指向原始狗(但请注意,由于“AAA”线,它的名字现在是“Max” - 仍然是相同的狗; myDog 的值没有改变)。
在 C++、Ada、Pascal 和支持 pass-by-reference 的其他语言中,您实际上可以更改已通过的变量。
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
int x = 1;
int y = 2;
swap(&x, &y);
该功能将指标传递给数据,并跟随这些指标访问和修改这些数据。
void swap(int[] x, int[] y) {
int temp = x[0];
x[0] = y[0];
y[0] = temp;
}
int[] x = {1};
int[] y = {2};
swap(x, y);
与某些其他语言不同,Java不允许您在 pass-by-value 和 pass-by-reference 之间选择。
所有论点都以价值为准。
一个方法通话可以通过两个类型的 valuesto 一个方法
原始值的副本(例如,类型值 int 和双值)对物体的参考副本。
当一个方法改变原始类型参数时,参数的变化不会影响呼叫方法的原始论点值。
如果你修改一个参考类型参数以便它指向另一个对象,只有参数指向新对象 - 呼叫器的变量中存储的参数仍然指向原始对象。
参考: JavaTM 如何编程(早期对象),第十版
不,不是通过参考。
Java 按 Java 语言规格通过值:
当方法或构造器被提及(第15条12),实际论点表达的值启动新创建的参数变量,每一个声明的类型,在执行方法或构造器的身体。
我从一本关于Java认证的书中取了这个代码和解释,并做了一些小变化,我认为这是一个很好的图像,通过对象的价值。在下面的代码中,重新分配g不会重新分配f!在条()方法的结尾,创建了两个Foo对象,一个是由当地变量f和一个是由当地(论点)变量g提到的。
package test.abc;
public class TestObject {
/**
* @param args
*/
public static void main(String[] args) {
bar();
}
static void bar() {
Foo f = new Foo();
System.out.println("Object reference for f: " + f);
f.setName("James");
doStuff(f);
System.out.println(f.getName());
//Can change the state of an object variable in f, but can't change the object reference for f.
//You still have 2 foo objects.
System.out.println("Object reference for f: " + f);
}
static void doStuff(Foo g) {
g.setName("Boo");
g = new Foo();
System.out.println("Object reference for g: " + g);
}
}
package test.abc;
public class Foo {
public String name = "";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
对象参考 f: test.abc.Foo@62f72617
在我所知道的范围内,Java只知道值的呼叫,这意味着对于原始数据类型,您将使用复印件工作,对于对象,您将使用对象的参考复印件工作。
public static void swap(StringBuffer s1, StringBuffer s2) {
StringBuffer temp = s1;
s1 = s2;
s2 = temp;
}
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer("Hello");
StringBuffer s2 = new StringBuffer("World");
swap(s1, s2);
System.out.println(s1);
System.out.println(s2);
}
这将人口Hello World而不是World Hello,因为在交换函数中,您使用复印件,这些复印件不会对主要的参考产生影响。
public static void appendWorld(StringBuffer s1) {
s1.append(" World");
}
public static void main(String[] args) {
StringBuffer s = new StringBuffer("Hello");
appendWorld(s);
System.out.println(s);
}
如果您将 StringBuffer 更改为 String,它只会产生 Hello 因为 String 是不可变的。
public static void appendWorld(String s){
s = s+" World";
}
public static void main(String[] args) {
String s = new String("Hello");
appendWorld(s);
System.out.println(s);
}
但是,你可以为 String 做一个插槽,这将使它能够与 Strings 一起使用:
class StringWrapper {
public String value;
public StringWrapper(String value) {
this.value = value;
}
}
public static void appendWorld(StringWrapper s){
s.value = s.value +" World";
}
public static void main(String[] args) {
StringWrapper s = new StringWrapper("Hello");
appendWorld(s);
System.out.println(s.value);
}
编辑:我认为这也是使用 StringBuffer 的理由,当涉及到“添加”两个线条时,因为你可以修改原始对象,你不能用像 String 这样的不可变的对象。