特别是我想知道arg是否总是被链接,或者它是否只是因为函数decloration是static
而被链接?
在编写成千上万行代码之前,我们很高兴知道它们是否有任何怪癖,期望它是单向的,但在1个边缘情况下它不是。
根据记录,我了解string = new String[2];
内的changeValue()
会创建一个新字符串,而且这些更改不会传播到main
string
的实例;
以下是一些示例代码,用于说明我所指的内容。
public class TestLink {
public static void changeValue(Object object) {
object.foo = "changed";
}
public static void changeValue(String[] string) {
string[0] = "changed";
}
public static void main(String[] args) {
String[] string = new String[2];
string[0] = "not changed";
changeValue(string);
if (string[0].equals("not changed")) {
System.out.println("not linked");
} else {
System.out.println("linked");
}
System.out.println(string[0]);
/*
output:
linked
changed
*/
}
}
答案 0 :(得分:4)
从问题中的代码来看,它听起来像你真正要求的是:"是否存在一个边缘情况,我将数组引用传递给方法并修改其内容,但是那个修改实际上并没有发生?"
不,没有。让我们提取一些代码并更改一些名称,以免误导:
public static void changeValue(String[] arg) {
arg[0] = "changed";
}
public static void main(String[] args) {
String[] foo = new String[2];
foo[0] = "not changed";
changeValue(foo);
if (foo[0].equals("not changed")) {
System.out.println("not linked");
} else {
System.out.println("linked");
}
System.out.println(foo[0]);
}
changeValue
接受数组引用(指向数组的值),并更改该数组的内容。如果代码编译(例如,所有类型等都是正确的)并且在运行时不会抛出(您没有将null
或零长度数组传递到changeValue
) ,然后不,没有边缘情况,那就不会起作用。
如果我们深入了解该代码中的内容,我们就会明白为什么答案是"否。"我发现图片可以提供帮助,所以让我们在这里投入一些ASCII艺术。
这一行:
String[] foo = new String[2];
在内存中给我们这个:
+----------+ foo(ref 5426)---->| String[] | +----------+ | 0: null | | 1: null | +----------+
我们有一个包含值的变量foo
。该值称为对象引用,因为它引用了一个对象(在本例中是一个数组,因此我们有时将其称为数组引用)。这只是告诉JVM对象所在位置的值。我将它显示为ref 5426
只是为了表明它是一个像任何其他值一样的值,而不是一些神奇的东西;我完全随机选择了5426,我们从未看到实际值,JVM将它隐藏起来。
然后你这样做:
foo[0] = "not changed";
给我们:
+-------------+ foo(ref 5426)---->| String[] | +-------------+ +---------------+ | 0: ref 1897 |---->| String | | 1: null | +---------------+ +-------------+ | "not changed" | +---------------+
例如,数组中的第一个条目包含值,该值是对字符串对象的引用。
然后我们这样做:
changeValue(foo);
从foo
获取值并将该值传递给changeValue
。 changeValue
接收参数arg
中的值。所以现在我们有了这个:
+-------------+ foo(ref 5426)--+->| String[] | | +-------------+ +---------------+ | | 0: ref 1897 |---->| String | | | 1: null | +---------------+ | +-------------+ | "not changed" | | +---------------+ arg(ref 5426)--+
由于main
尚未返回,foo
仍然存在,并且其值为ref 5426
。该值已复制到arg
,因此arg
也具有值ref 5426
。该值告诉JVM数组的位置。
然后你做:
arg[0] = "changed";
给了我们:
+-------------+ +---------------+ foo(ref 5426)--+->| String[] | | String | | +-------------+ +-----------+ +---------------+ | | 0: ref 2785 |---->| String | | "not changed" | | | 1: null | +-----------+ +---------------+ | +-------------+ | "changed" | | +-----------+ arg(ref 5426)--+
数组中的第一个条目不再指向旧字符串,您已使用指向新字符串的新值替换了该值。 (旧的字符串一直挂着,直到垃圾收集器摆脱它,但我们不再关心,我们没有任何引用它。)
changeValue
已完成,因此返回,arg
(参数)消失。
由于foo
仍然具有相同的值(ref 5426
),当您查找其中的第一个条目时,您会看到changeValue
放置的值。
因为有一个边缘情况,上面没有工作,所以必须是这些事情之一:
将foo
的值传递到changeValue
并将其作为arg
接收,因某些原因无效。
将foo
的值传递到changeValue
并将其作为arg
接收,并因某种原因制作副本
当我们使用它来获取数组并进行更改时,我们得到ref 5426
会有所不同。
没有任何边缘情况会发生任何这些事情。 : - )
答案 1 :(得分:0)
声明为static
的函数在此上下文中没有任何意义。你做的是你传递了一个String
数组。此函数内部函数与main
函数中的数组相同。因此,数组通过引用传递 *并且String
类型是一种特殊情况,传递它会创建新的String
,尽管它是非基本类型。原始类型仅按值传递。
您的边缘案例列于here,另外还有String
。 byte
,short
,int
,long
,float
,double
,boolean
,char
- 全部正如你所说的那样,这些都是“没有联系”的。这些不是通过引用传递 *。
* Java本身不通过引用传递。 引用是通过值传递的。 Is Java "pass-by-reference" or "pass-by-value"?
在Java中忘记通过引用传递。这种语言完全基于按值传递概念(取消链接,如您所说),但引用由值本身传递,从而创建您的链接行为。这个编辑的信用归于T.J.克劳德。为了方便起见,我只是使用术语通过引用传递。