Java 按值传递和引用
Java Pass By Value and reference
我有以下代码。但是,我无法理解它关于按值传递和引用传递的行为。
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 passes by value.Tom
101
为什么输出是101
?我期待输出 102
.
你增加 x
一次,在给方法 ("Tom") 的狗上。
然后你创造了一只 新 狗,叫做 Taz。
然后您为第二只狗递增 x
。不影响原来的,两只狗都是101
至于为什么调用方法仍然引用 "Tom",即使您将局部变量更改为指向 "Taz":那是因为局部变量就是:局部于它所在的地方用来。调用者不关心你以后用它做什么,它自己的变量仍然指向"Tom".
因为Java不支持按引用传递或"out parameters",被调用函数无法更改调用函数中变量的值。
但请注意,对象不存储在变量中。只有指向它们的指针存储在那里。实际对象实例本身位于共享位置(程序堆内存)。所以调用的方法确实可以改变对象。但是它不能将不同的对象分配给调用函数。
要点:使局部变量 final
,尤其是方法参数。这样你就不能为两个不同的东西重用同一个变量,代码变得不那么混乱了。
你增加了 x
两次,但在不同的狗身上。看这段代码:
public static void foo(Dog d) {
++d.x;
d = new Dog("Taz");
++d.x;
}
最初,d
指的是名字为Tom
的狗,x=100
。这不是原始对象的副本——它是对同一对象的引用。 Java 按值传递引用,这与按值传递对象 或 按引用传递对象不同。重要的是要了解 main
中 aDog
的值不是 Dog
对象 - 它是对 Dog
对象的引用。该引用的值按值传递给您的 foo
方法...因此 d
的初始值与 aDog
的值相同。 d
变量本身(而不是其值所指的对象)的进一步更改 不会 更改 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。
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}
但是,您可以获得全新的副本,但您需要 return 它。
在 main() 中:更新此行
foo(aDog);
到
aDog = foo(aDog);
还将 foo() 更新为:
public static Dog foo(Dog d) {
...
return d;
}
当您像这样使用 new
关键字时 d = new Dog("Taz");
你创建了一个新的 Dog
,新的 Dog
叫做 Taz
并且有 x=101
,所以你不会为同一个 Dog d.x
递增 2 次
我有以下代码。但是,我无法理解它关于按值传递和引用传递的行为。
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 passes by value.Tom
101
为什么输出是101
?我期待输出 102
.
你增加 x
一次,在给方法 ("Tom") 的狗上。
然后你创造了一只 新 狗,叫做 Taz。
然后您为第二只狗递增 x
。不影响原来的,两只狗都是101
至于为什么调用方法仍然引用 "Tom",即使您将局部变量更改为指向 "Taz":那是因为局部变量就是:局部于它所在的地方用来。调用者不关心你以后用它做什么,它自己的变量仍然指向"Tom".
因为Java不支持按引用传递或"out parameters",被调用函数无法更改调用函数中变量的值。
但请注意,对象不存储在变量中。只有指向它们的指针存储在那里。实际对象实例本身位于共享位置(程序堆内存)。所以调用的方法确实可以改变对象。但是它不能将不同的对象分配给调用函数。
要点:使局部变量 final
,尤其是方法参数。这样你就不能为两个不同的东西重用同一个变量,代码变得不那么混乱了。
你增加了 x
两次,但在不同的狗身上。看这段代码:
public static void foo(Dog d) {
++d.x;
d = new Dog("Taz");
++d.x;
}
最初,d
指的是名字为Tom
的狗,x=100
。这不是原始对象的副本——它是对同一对象的引用。 Java 按值传递引用,这与按值传递对象 或 按引用传递对象不同。重要的是要了解 main
中 aDog
的值不是 Dog
对象 - 它是对 Dog
对象的引用。该引用的值按值传递给您的 foo
方法...因此 d
的初始值与 aDog
的值相同。 d
变量本身(而不是其值所指的对象)的进一步更改 不会 更改 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。
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}
但是,您可以获得全新的副本,但您需要 return 它。
在 main() 中:更新此行
foo(aDog);
到
aDog = foo(aDog);
还将 foo() 更新为:
public static Dog foo(Dog d) {
...
return d;
}
当您像这样使用 new
关键字时 d = new Dog("Taz");
你创建了一个新的 Dog
,新的 Dog
叫做 Taz
并且有 x=101
,所以你不会为同一个 Dog d.x
递增 2 次