数组在Java中不是基本类型,但它们也不是对象,因此它们是通过值传递还是通过引用传递?它是否取决于数组所包含的内容,例如引用或基本类型?


数组实际上是对象,因此传递的是引用(引用本身是按值传递的,不明白吗?)简单的例子:

// assuming you allocated the list
public void addItem(Integer[] list, int item) {
    list[1] = item;
}

您将看到调用代码对列表的更改。但是你不能改变引用本身,因为它是通过值传递的:

// assuming you allocated the list
public void changeArray(Integer[] list) {
    list = null;
}

如果传递一个非空列表,在方法返回时它将不是空的。


Java中的所有东西都是通过值传递的。对于数组(它只是一个对象),数组引用是按值传递的(就像对象引用是按值传递的)。

当你将一个数组传递给其他方法时,实际上该数组的引用被复制了。

通过该引用对数组内容的任何更改都会影响原始数组。 但是将引用更改为指向新数组并不会更改原方法中现有的引用。

请参阅这篇文章:Java是“引用传递”还是“值传递”?

请看这个工作示例:

public static void changeContent(int[] arr) {

   // If we change the content of arr.
   arr[0] = 10;  // Will change the content of array in main()
}

public static void changeRef(int[] arr) {
   // If we change the reference
   arr = new int[2];  // Will not change the array in main()
   arr[0] = 15;
}

public static void main(String[] args) {
    int [] arr = new int[2];
    arr[0] = 4;
    arr[1] = 5;

    changeContent(arr);

    System.out.println(arr[0]);  // Will print 10.. 
  
    changeRef(arr);

    System.out.println(arr[0]);  // Will still print 10.. 
                                 // Change the reference doesn't reflect change here..
}

不,那是不对的。数组是Java中的特殊对象。这就像传递其他对象一样,你传递的是引用的值,而不是引用本身。这意味着,在被调用例程中更改数组的引用将不会反映在调用例程中。


你的问题建立在一个错误的前提上。

数组在Java中不是基本类型,但它们也不是对象……”

事实上,Java中的所有数组都是objects1。每个Java数组类型都有Java .lang.Object作为它的超类型,并继承Object API中所有方法的实现。

... 那么它们是通过值传递还是通过引用传递呢?它是否取决于数组所包含的内容,例如引用或基本类型?

简单回答:1)通过值传递,2)没有区别。

再答:

像所有Java对象一样,数组是通过值传递的…但是这个值是数组的引用。因此,当你在被调用方法中为数组的单元格赋值时,你将赋值给调用者看到的相同数组对象。

这不是引用传递。真正的引用传递涉及传递变量的地址。使用真正的引用传递,被调用的方法可以赋值给它的局部变量,这将导致调用方中的变量被更新。

但在Java中不是这样。在Java中,被调用的方法可以更新数组的内容,它可以更新数组引用的副本,但它不能更新调用方中保存调用方数组引用的变量。因此……Java提供的不是引用传递。

这里有一些链接,解释了引用传递和值传递之间的区别。如果你不理解我上面的解释,或者你倾向于不同意这些术语,你应该阅读它们。

http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/topic/com.ibm.xlcpp8a.doc/language/ref/cplr233.htm http://www.cs.fsu.edu/~myers/c++/notes/references.html

相关SO问题:

Java是“引用传递”还是“值传递”?

历史背景:

短语“引用传递”最初是“引用调用”,它被用来区分FORTRAN的参数传递语义(引用调用)和ALGOL-60的参数传递语义(值调用和名称调用)。

In call-by-value, the argument expression is evaluated to a value, and that value is copied to the called method. In call-by-reference, the argument expression is partially evaluated to an "lvalue" (i.e. the address of a variable or array element) that is passed to the calling method. The calling method can then directly read and update the variable / element. In call-by-name, the actual argument expression is passed to the calling method (!!) which can evaluate it multiple times (!!!). This was complicated to implement, and could be used (abused) to write code that was very difficult to understand. Call-by-name was only ever used in Algol-60 (thankfully!).

更新

实际上,algore -60的名称调用类似于将lambda表达式作为参数传递。问题在于,这些非精确lambda表达式(它们在实现级别被称为“thunks”)可以间接地修改调用过程/函数作用域内变量的状态。这也是他们难以理解的部分原因。(例如,请参阅维基百科上詹森设备的页面。)


1. 在链接的问答(Java中的数组及其在内存中的存储方式)中,没有任何内容声明或暗示数组不是对象。


有点像骗人的房产…甚至在Java中引用也是按值传递的,因此对引用本身的更改是在被调用的函数级别上进行的。编译器和/或JVM通常会将值类型转换为引用。


Java中的所有东西都是通过值传递的。

对于数组,引用被复制到一个新的引用中,但请记住,Java中的所有内容都是通过值传递的。

看看这篇有趣的文章,了解更多信息…


关于数组的详细讨论在http://docs.oracle.com/javase/specs/jls/se5.0/html/arrays.html#27803。这表明Java数组是对象。这些对象的类在10.8中定义。

语言规范http://docs.oracle.com/javase/specs/jls/se5.0/html/classes.html#40420的8.4.1节描述了如何将参数传递给方法。由于Java语法源自C和c++,因此行为是相似的。与C语言一样,基本类型是按值传递的。当传递一个对象时,对象引用(指针)是按值传递的,这与C语言按值传递指针的语法是一致的。见4.3.1,http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.3,

实际上,这意味着在方法中修改数组的内容会反映在调用作用域中的数组对象中,但在方法中为引用重新分配新值不会对调用作用域中的引用产生影响,这正是C中指向结构的指针或c++中的对象的指针所期望的行为。

At least part of the confusion in terminology stems from the history of high level languages prior to the common use of C. In prior, popular, high level languages, directly referencing memory by address was something to be avoided to the extent possible, and it was considered the job of the language to provide a layer of abstraction. This made it necessary for the language to explicitly support a mechanism for returning values from subroutines (not necessarily functions). This mechanism is what is formally meant when referring to 'pass by reference'.

When C was introduced, it came with a stripped down notion of procedure calling, where all arguments are input-only, and the only value returned to the caller is a function result. However, the purpose of passing references could be achieved through the explicit and broad use of pointers. Since it serves the same purpose, the practice of passing a pointer as a reference to a value is often colloquially referred to a passing by reference. If the semantics of a routine call for a parameter to be passed by reference, the syntax of C requires the programmer to explicitly pass a pointer. Passing a pointer by value is the design pattern for implementing pass by reference semantics in C.

Since it can often seem like the sole purpose of raw pointers in C is to create crashing bugs, subsequent developments, especially Java, have sought to return to safer means to pass parameters. However, the dominance of C made it incumbent on the developers to mimic the familiar style of C coding. The result is references that are passed similarly to pointers, but are implemented with more protections to make them safer. An alternative would have been the rich syntax of a language like Ada, but this would have presented the appearance of an unwelcome learning curve, and lessened the likely adoption of Java.

简而言之,在Java中为对象(包括数组)传递参数的设计本质上是服务于按引用传递的语义意图,但是是用按值传递引用的语法实现的。