将对象传递给java中的方法似乎是通过引用(而Java是通过val)
Posted
技术标签:
【中文标题】将对象传递给java中的方法似乎是通过引用(而Java是通过val)【英文标题】:Passing object to method in java appears to be by reference (and Java is by val) 【发布时间】:2011-04-19 02:20:09 【问题描述】:我认为当您将对象传递给 Java 中的方法时,它们应该是按值传递的。
这是我的代码:
public class MyClass
int mRows;
int mCols;
Tile mTiles[][]; //Custom class
//Constructor
public MyClass(Tile[][] tiles, int rows, int cols)
mRows = rows;
mCols = cols;
mTiles = new Tile[mRows][mCols];
for (int i=0; i < mRows; i++)
for (int j=0; j < mCols; j++)
mTiles[i][j] = new Tile();
mTiles[i][j] = tiles[i][j];
此时,对mTiles
对象的任何更改都会反映回tiles 对象。任何想法如何解决这个问题?
谢谢大家。
【问题讨论】:
已经问过:***.com/questions/514478/… 【参考方案1】:这是通过 val,但是在谈论对象时,传递的是引用的值,因此,如果您的对象是可变的(如在您的示例中)通过引用副本修改对象,将修改底层对象.
要解决它,您必须复制对象并传递新的参考值。
在您的代码中,您在“tiles”和“mTiles”中使用相同的参考值
mTiles[i][j] = new Tile(); // <---this line is useless by the way
mTiles[i][j] = tiles[i][j] // because you then assign the same value here
你必须像这样创建一个新的:
mTiles[i][j] = new Tile(tiles[i][j]);
或者
mTiles[i][j] = tiles[i][j].clone();
或者
mTiles[i][j] = Tile.newFrom( tiles[i][j] );
或者类似的东西,所以你可以创建一个新的,而不是使用相同的 ref。
我希望这会有所帮助。
编辑
当你在pass-by-val中改变ref时,原来的不受影响,即:
String s = "hi";
changeIt(s);
....
void changeIt(String s)
s = "bye" // assigning to the copy a new ref value of "bye"
在这之后,原来的“hi”还是“hi”。在 pass-by-ref 中它将是“再见”
这里有一些链接:
http://www.javaranch.com/campfire/StoryPassBy.jsp
Can someone explain to me what the reasoning behind passing by "value" and not by "reference" in Java is?
Pass by reference or pass by value?
http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html
http://academic.regis.edu/dbahr/GeneralPages/IntroToProgramming/JavaPassByValue.htm
http://www.jguru.com/faq/view.jsp?EID=430996
最终:
http://www.google.com.mx/search?sourceid=chrome&ie=UTF-8&q=java+is+pass+by+value
【讨论】:
我不确定我是否理解。那和pass-by-ref有什么区别。 酷,谢谢。我相信我已经掌握了这个概念,但是,您的解决方案都不起作用……可能是因为我的类对象没有实现任何这些方法。我确实尝试重载 Tile 构造函数,以使用您的第一个建议但没有成功。我感到非常沮丧,这是如此困难。克隆方法是否易于添加到我的课程中?然后我要在深与浅的斗争中挣扎……/叹息。【参考方案2】:你不传递对象,你传递一个对对象的引用,它是按值复制的。
【讨论】:
【参考方案3】:以下是 Java 参数传递语义的一些诊断示例:
对于对象类型:
void changeIt(String s)
s = "bye";
String s = "hi";
changeIt(s);
System.out.println(s); // would print "bye" for call by reference
// but actually prints "hi"
对于原始类型:
void changeIt(int i)
i = 42;
int i = 0;
changeIt(i);
System.out.println(i); // would print "42" for call by reference
// but actually prints "0"
事实上,在这两个例子中,changeIt
方法中的赋值只影响各自方法的局部变量,实际打印的值将是“hi”和“0”。
编辑
而且由于 OP 仍然不相信我......这里有一个诊断示例,表明 Java 也是可变对象的按值调用。
public class Mutable
int field;
public Mutable(int field) this.field = field;
public void setField(int field) this.field = field;
public int getField() return field;
void changeIt(Mutable m, Mutable m2)
m = m2; // or 'm = new Mutable(42);' or 'm = null;'
Mutable m = new Mutable(0);
Mutable m2 = new Mutable(42);
changeIt(m, m2);
System.out.println(m.getField());
// would print "42" for call by reference
// but actually prints "0"
相比之下,此示例将为引用调用和按值调用语义给出相同的答案。它不能证明任何关于参数传递语义的东西。
void changeIt2(Mutable m)
m.setField(42);
Mutable m = new Mutable();
changeIt2(m);
System.out.println(m.getField());
// prints "42" for both call-by reference
// and call-by-value
相信我,我已经使用 Java 编程超过 10 年,并且我在大学阶段教过比较编程语言课程。
【讨论】:
谢谢。根据另一个答案,对象的行为似乎有所不同。 实际上,(真正的)Java 的参数传递语义对于对象和原始类型是相同的,我的示例代码清楚地证明了这一点。此外,另一个答案没有说明对象和原始类型的行为不同。如果你认为它暗示了这一点,那么你显然不明白它在说什么。 字符串是不可变的,因此其行为与其他可变对象不同。我认为您的示例不适用于可变对象。 我真的很想相信你,因为它会让我的生活变得更轻松,我不会在我的程序中处于我的位置。请随时帮助我纠正我的程序,因为它不能按照您所说的方式工作。我很确定另一个人在这方面是正确的,这意味着我并没有完全传递值,而是传递了引用的值。 奥斯卡的答案是正确的,我也是。问题是你仍然不明白我们如何才能同时是正确的。不,我不会为你编写代码。顺便说一句,Oscar 对问题的建议解决方案都是合理的,但不是复制粘贴的答案。【参考方案4】:事实上,Java 是按值传递的。但它也有“引用数组”!这就是为什么许多人认为 Java 对对象(至少对数组)是按引用传递,而对基元来说只是按值传递。
这是一个简短的测试:
String[] array = new String[10];
array[0] = "111";
ArrayList one = new ArrayList();
one.add(array);
ArrayList two = (ArrayList) one.clone(); //Alternate with: ArrayList two = one;
String[] stringarray1 = (String[]) one.get(0);
String[] stringarray2 = (String[]) two.get(0);
System.out.println("Array: "+one+" with value: "+stringarray1[0]);
System.out.println("Array: "+one+" with value: "+stringarray2[0]);
array[0] = "999";
String[] stringarray3 = (String[]) one.get(0);
String[] stringarray4 = (String[]) two.get(0);
System.out.println("Array: "+one+" with value: "+stringarray3[0]);
System.out.println("Array: "+two+" with value: "+stringarray4[0]);
无论你是克隆还是使用 =,System.out.print 都会是这样的:
Array: [[Ljava.lang.String;@addbf1] with value: 111
Array: [[Ljava.lang.String;@addbf1] with value: 111
Array: [[Ljava.lang.String;@addbf1] with value: 999
Array: [[Ljava.lang.String;@addbf1] with value: 999
这证明了克隆和数组是有害的组合,因为数组只存储指针!我仍然需要测试这是否也适用于无数组对象......因为这意味着 Java 总是“按引用存储”(并且“克隆”功能对于任何对象来说都只是一个糟糕的笑话包含数组),而只有基元是真实值,没有引用!
因为我们知道逻辑:storage-by-reference x pass-by-value == "storage by value x pass-by-reference"(对于对象!),
虽然我们从学校开始就已经知道:按值存储 x 按值传递(对于基元)
那么,我们是否都被我们的编程老师欺骗了(即使是在大学里)?也许吧,但他们至少没有犯任何逻辑错误……所以这不是谎言,只是错了。
编辑
我用一个类写了和上面一样的代码,首先是数据结构:
public class Foobar implements Cloneable
String[] array;
public Foobar()
this.array = new String[10];
public String getValue()
return array[0];
public String[] getArray()
return array;
public void setArray(String[] array)
this.array = array;
@Override
public Object clone()
try
Foobar foobar = (Foobar) super.clone();
foobar.setArray(array);
return foobar;
catch(Exception e)
return null;
现在是控制器:
String[] array = new String[10];
array[0] = "111";
Foobar foo1 = new Foobar();
foo1.setArray(array);
Foobar foo2 = foo1; //Alternation: Foobar foo2 = (Foobar) foo1.clone();
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue());
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue());
array[0] = "999";
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue());
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue());
测试结果总是这样——无论我使用 = 还是 clone():
Instance: [Ljava.lang.String;@42e816 with value: 111
Instance: [Ljava.lang.String;@42e816 with value: 111
Instance: [Ljava.lang.String;@42e816 with value: 999
Instance: [Ljava.lang.String;@42e816 with value: 999
现在我的口袋里有了“主阵列”,我可以立即统治所有物体! (这真的不是一件好事)
我一直对 Java 数组感到不安,但我说不出它是什么。现在我知道了,我感觉很好,因为从那以后我只使用数组作为对象的容器......只是发现自己非常惊讶它们在 php 等脚本语言中的重要性!
不过,Java 数组非常适合线程之间的同步,因为您可以轻松地传递它们并且仍然可以访问共享值。但是来自 PHP 或 C++ 或其他地方的程序员可能确实会遇到 Java 数组的一些问题。 ;D
哦,我喜欢这篇文章:http://javadude.com/articles/passbyvalue.htm
更新:我找到了一个很好的解决方案来复制任何包含数组的对象,请在此处查看我的评论:Bug in using Object.clone()
【讨论】:
以上是关于将对象传递给java中的方法似乎是通过引用(而Java是通过val)的主要内容,如果未能解决你的问题,请参考以下文章