我有以下代码。但是,我无法理解它在传递价值和参考方面的行为。
class Dog{
String name;
int x=100;
Dog(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
class Demo{
public static void main( String[] args ){
Dog aDog = new Dog("Tom");
foo(aDog);
if (aDog.getName().equals("Tom")) { //true
System.out.println( "Java passes by value."+ aDog.getName());
System.out.println( aDog.x);
} else if (aDog.getName().equals("Taz")) {
System.out.println( "Java passes by reference." );
}
}
public static void foo(Dog d) {
++d.x;
d = new Dog("Taz");
++d.x;
}
}
这将提供输出
Java按值传递.Tom
101
为什么输出101
?我期待输出102
。
答案 0 :(得分:4)
你要两次递增x
,但在不同的狗身上。看看这段代码:
public static void foo(Dog d) {
++d.x;
d = new Dog("Taz");
++d.x;
}
最初,d
指的是名称为Tom
且x=100
的狗。这不是原始对象的副本 - 它是对同一对象的引用。 Java按值传递引用,这与通过值传递对象或通过引用传递对象的不同。重要的是要了解aDog
中main
的值不是Dog
对象 - 它是对Dog
对象的引用。该引用的值按值传递到foo
方法...因此d
的初始值与aDog
的值相同。对d
变量本身(而不是其值所引用的对象)的进一步更改 not 更改aDog
变量。
所以,看看foo
的其余部分:
++d.x
之后,d
指的是名为Tom
的狗,x=101
。d = new Dog("Taz")
之后,d
指的是名为Taz
的狗,x=100
。++d.x
之后,d
指的是名为Taz
的狗,x=101
。调用代码只知道名为Tom
的狗,所以打印出Tom 101。
答案 1 :(得分:3)
你在给予方法的狗(“汤姆”)上增加x
一次。
然后你创建一个名为Taz的 new 狗。
然后为第二只狗增加x
。它不会影响原来的,两只狗都会在101.
至于为什么调用方法仍然引用“Tom”,即使您将局部变量更改为指向“Taz”:这是因为局部变量就是:它所使用的位置的本地变量。调用者不关心你以后用它做什么,它自己的变量仍然指向“Tom”。
因为Java不支持pass-by-reference或“out parameters”,所以被调用的函数无法改变调用函数中变量的值。
但请注意,对象不存储在变量中。只存在指向它们的指针。实际的对象实例本身位于共享位置(程序堆内存)。所以被调用的方法确实可以更改对象。但是它不能将不同的对象分配给调用函数。
外卖:制作局部变量final
,尤其是方法参数。然后你不能为两个不同的东西重用相同的变量,而且代码变得不那么混乱了。
答案 2 :(得分:2)
Java总是按值传递:
主要():
Dog aDog = new Dog("Tom"); // {name="Tom", x=100}
FOO():
++d.x; // {name="Tom", x=101}
d = new Dog("Taz"); // {name="Taz", x=100}
++d.x; // {name="Taz", x=101}
但是,您可以获得全新副本,但需要将其返回。
在main()中:更新此行
foo(aDog);
到
aDog = foo(aDog);
还将foo()更新为:
public static Dog foo(Dog d) {
...
return d;
}
答案 3 :(得分:-1)
当您使用new
这样的关键字d = new Dog("Taz");
时
您创建了一个新的Dog
,新的Dog
名为Taz
并且x=101
,因此您不会增加d.x
2同一只狗的时间