在java中,向方法传递对象似乎是通过引用(java是通过val)

在java中,向方法传递对象似乎是通过引用(java是通过val),java,android,eclipse,pass-by-reference,pass-by-value,Java,Android,Eclipse,Pass By Reference,Pass By Value,我认为,当您在Java中将对象传递给方法时,它们应该是按值传递的 这是我的密码: public class MyClass{ int mRows; int mCols; Tile mTiles[][]; //Custom class //Constructor public MyClass(Tile[][] tiles, int rows, int cols) { mRows = rows; mCols = col

我认为,当您在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];
            }
        }
    }
公共类MyClass{
int mRows;
int mCols;
平铺mTiles[][];//自定义类
//建造师
公共MyClass(Tile[][]Tile,int行,int列){
mRows=行;
mCols=cols;
mTiles=新瓷砖[mRows][mCols];
对于(int i=0;i
此时,对
mTiles
对象的任何更改都会反映回tiles对象。您有什么想法可以解决这个问题吗


谢谢大家。

这是通过val实现的,但当谈到对象时,传递的是引用的值,因此,如果对象是可变的(如示例中所示),则通过引用副本修改对象将修改参考底图对象

要解决这个问题,必须复制对象并传递新的参考值

在代码中,在“tiles”和“mTiles”中使用相同的参考值

或者类似的东西,所以你可以创建一个新的,而不是使用相同的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中,它将是“bye”

以下是一些链接:

最终:


以下是一些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是按值传递的。但它也有“按引用数组”!这就是为什么许多人认为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都将如下所示:

mTiles[i][j] = new Tile(tiles[i][j]);
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总是“通过引用存储”(而“克隆”函数对于任何包含数组的对象来说都只是一个坏笑话),而只有原语是实值,没有引用

因为我们知道逻辑:按引用存储x按值传递==“按值存储x按引用传递”(对于对象!)

虽然我们从学校就知道了:按值存储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(),测试结果都会是这样的:

现在我口袋里有了“主阵列”,我可以用它立刻统治所有对象!(这不是一件好事)

我总是对Java数组感到不安,但我说不出它是什么。现在我知道了,而且我感觉很好,因为从那以后我只使用数组作为对象的容器……但我发现自己很惊讶它们在PHP等脚本语言中是多么重要

不过,Java数组对于线程间的同步非常有用,因为您可以轻松地传递它们,并且仍然可以访问共享值。但是来自PHP或C++的程序员或其他地方的程序员可能确实遇到java java阵列的一些问题。 哦,我喜欢这篇文章:


更新:我找到了一个很好的方法来复制任何包含数组的对象,请参见我的评论:

已经有人问:我不确定我是否理解。这和pass-by-ref有什么区别。很好,谢谢。我相信我已经掌握了这个概念,但是,您的解决方案都不起作用……可能是因为我的类对象没有实现了这些方法中的任何一种。我确实尝试过使用你的第一个建议重载Tile构造函数,但没有成功。我非常沮丧,因为这太难了。克隆方法很容易添加到我的类中吗?然后我将与深度和浅层进行斗争…/叹气。谢谢。根据另一个答案,似乎对象的行为实际上,(真实的)Java的参数传递语义对于对象和基元类型是相同的,我的示例代码清楚地说明了这一点。此外,另一个答案没有说明对象和基元类型的行为不同。如果您认为这意味着不同,那么您显然不理解它在说什么。字符串是不可变的,因此与其他可变对象不同的保存方式。我不认为你的示例适用于可变对象。我真的很想相信你,因为这会让我的生活变得更轻松,我的程序不会像现在这样。请放心
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
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());
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