Java,按值传递,引用变量
Posted
技术标签:
【中文标题】Java,按值传递,引用变量【英文标题】:Java, pass-by-value, reference variables 【发布时间】:2010-10-04 16:00:18 【问题描述】:在以下示例中,我在理解 Java 的“按值传递”操作时遇到了问题:
public class Numbers
static int[] s_ccc = 7;
static int[] t_ccc = 7;
public static void calculate(int[] b, int[] c)
System.out.println("s_ccc[0] = " + s_ccc[0]); // 7
System.out.println("t_ccc[0] = " + t_ccc[0]); // 7
b[0] = b[0] + 9;
System.out.println("\nb[0] = " + b[0]); // 16
c = b;
System.out.println("c[0] = " + c[0] + "\n"); // 16
public static void main(String[] args)
calculate(s_ccc, t_ccc);
System.out.println("s_ccc[0] = " + s_ccc[0]); // 16
System.out.println("t_ccc[0] = " + t_ccc[0]); // 7
我知道因为 s_ccc 是一个引用变量,所以当我将它提供给方法 calculate() 并在方法中对其元素进行一些更改时,即使在我离开该方法后更改仍然存在。我认为 t_ccc 也应该如此。又是 一个引用变量,我把它交给方法calculate(),在方法中我把t_ccc的引用改为s_ccc的引用。现在 t_ccc 应该是一个指向数组的引用变量,该数组有一个 int 类型的元素等于 16。但是当方法 calculate() 离开时,似乎 t_ccc 指向它的旧对象。为什么会这样?改变不应该也保留吗?毕竟是引用变量。
问候
【问题讨论】:
您的意思是calculate(s_ccc, t_ccc) 而不是calculate(s_ccc, s_ccc)? 是的,完全正确!我的意思是计算(s_ccc,t_ccc)。对不起。 @gemm:你能修改一下你的帖子吗? 【参考方案1】:在较早的问题“Is Java pass by reference?”中有关于 Java 如何传递变量的扩展讨论。 Java 确实按值传递对象引用。
在您的代码中,数组(即对象)的引用被传递到calculate()
。这些引用是按值传递的,这意味着对b
和c
值的任何更改仅在方法内可见(它们实际上只是s_ccc
和t_ccc
的副本)。这就是为什么main()
中的t_ccc
从未受到影响。
为了强化这个概念,一些程序员将方法参数声明为final
变量:
public static void calculate(final int[] b, final int[] c)
现在,编译器甚至不允许您更改b
或c
的值。当然,这样做的缺点是您不能再在您的方法中方便地操作它们。
【讨论】:
【参考方案2】:这里有一个简单的理解方式。
Java 总是传递参数的副本。如果参数是原始类型(例如整数),则被调用的方法会获取原始值的副本。如果参数是引用类型,则被调用的方法会获取引用的副本(不是所引用事物的副本)。
当您的main
方法开始时,s_ccc
和t_ccc
中的每一个都引用一个不同的数组。
就是这种情况,括号表示变量,方括号表示实际的数组结构:
(s_ccc) ---> [7]
(t_ccc) ---> [7]
假设你的意思是calculate(s_ccc, t_ccc)
,那么在calculate
方法的开头:
(s_ccc) ---> [7] <---(b)
(t_ccc) ---> [7] <---(c)
局部变量b
和c
分别是全局变量s_ccc
和t_ccc
的副本。
仍在calculcate
内,在b[0] = b[0] + 9
完成后:
(s_ccc) ---> [16] <---(b)
(t_ccc) ---> [7] <---(c)
b
引用的数组中的零(唯一)位置已被修改。
calculate
中的赋值 c = b
会产生这种情况:
(s_ccc) ---> [16] <---(b)
^------(c)
(t_ccc) ---> [7]
局部引用变量c
,现在包含与b
相同的引用。这对全局引用变量t_ccc
没有影响,它仍然引用与以前相同的数组。
当calculate
退出时,它的局部变量(在图表的右侧)消失了。 calculate
中没有使用全局变量(左侧),因此它们不受影响。最后的情况是:
(s_ccc) ---> [16]
(t_ccc) ---> [7]
c_ccc
和 t_ccc
均未更改;每个仍然引用与之前相同的数组calculate
。调用calculate
更改了s_ccc
引用的数组的内容,使用了对该数组的复制引用(在b
中)。
局部变量c
开始是作为t_ccc
的副本,并在calculate
中进行了操作,但这既没有改变t_ccc
本身,也没有改变它的引用数据。
【讨论】:
【参考方案3】:该方法按值接收变量。这些值无法更改(就方法的调用者而言),但其中包含的值可以(如果它是一个对象,或者在本例中是一个数组)。
所以当你改变数组内部的值 b[0] 时,可以在方法外部看到变化。然而这条线
c = b;
会在方法内部改变 c 的值,但是这种改变不会在方法外部看到,因为 c 的值是按值传递的。
【讨论】:
【参考方案4】:calculate(s_ccc, s_ccc);
行表明您实际上并没有对 t_ccc 做任何事情。
但是,如果此行已读取calculate(s_ccc, t_ccc);
,则效果保持不变。
这是因为您在此处为 c 分配了一个新值:c = b;
为引用参数分配新值时,引用会丢失。
这就是通过引用更改变量和分配新值之间的区别。
【讨论】:
【参考方案5】:进入calculate()
,b
指向s_ccc
和c
指向t_ccc
。如您所见,更改b[0]
将因此更改s_ccc
。
但是,任务
c = b;
只有c
指向b
指向的同一个对象,即s_ccc
。它不会将b
指向的内容复制到c
指向的内容。因此,t_ccc
保持不变。如果您在calculate():
末尾添加了以下行
c[0] = c[0] + 5;
s_ccc[0]
将是 21。
【讨论】:
以上是关于Java,按值传递,引用变量的主要内容,如果未能解决你的问题,请参考以下文章