Java中的按值调用(call by value)
前言
最近对Java中实参与形参之间的传递关系进行了较为深入的研究,发现Java中实际上只有按值调用,并没有类似C++、Pascal语言中的按引用调用。下面我将直接通过代码给大家尽量清楚的讲解Java中实参与形参的传递机制。
什么是引用
在C++中,我们将实参传递给形参时可以使用“引用”使得形参和实参是同一个变量,改变形参对应的实参也会改变。这是因为“引用”是直接把实参的地址给形参,如此一来目标变量就有两个名称(形参是别名)。就像一个人在学校里用一个正式的名字,在家里用小名一样(本质上是同一个人)。
测试代码一
import java.lang.String;
public class Array {
//数组传递是值传递,将原来的地址复制后传递给形参,即实参和形参指向同一个地址。所以可以通过函数修改main中的数组
public static void main(String[] args)
{
int[][] a = {{1,2,3},{4,5,6}};
int[] b = {1,1,1},c = {2,2,2};
String str = "h ell o";
for(int i = 0;i < a.length;i++)
{
for(int j = 0;j < a[i].length;j++)
System.out.print(a[i][j] + " ");
System.out.println();
}
System.out.print("string before:");
System.out.println(str);
System.out.println();
changeArrayString(a,str);
for(int i = 0;i < a.length;i++)
{
for(int j = 0;j < a[i].length;j++)
System.out.print(a[i][j] + " ");
System.out.println();
}
System.out.print("string after:");
System.out.println(str);
exchangeArray(b,c);
System.out.println();
System.out.print("arrays in main:");
for(int i = 0;i < 3;i++)
{
System.out.print(b[i] + " " + c[i] + " ");
}
}
private static void changeArrayString(int[][] a,String str)
{
for(int i = 0;i < a.length;i++)
for(int j = 0;j < a[i].length;j++)
a[i][j] = 0;
str = str.replace(" ", "");//去掉string中的空格,并返回一个新的string
System.out.println("str in function:" + str);
// str = "hi";//这里相当于str = new String("hi");使得str重新指向了一个新的对象的地址
// System.out.println("new str in function:" + str);
}
private static void exchangeArray(int[] a,int[] b)
{
int[] temp;
temp = a;
a = b;
b = temp;
System.out.print("arrays in function:");
for(int i = 0;i < 3;i++)
{
System.out.print(a[i] + " " + b[i] + " ");
}
}
}
运行结果一
1 2 3
4 5 6
string before:h ell o
str in function:hello
0 0 0
0 0 0
string after:h ell o
arrays in function:2 1 2 1 2 1
arrays in main:1 2 1 2 1 2
测试代码二
import java.lang.String;
public class Array {
public static void main(String[] args) {
Array hello = new Array();
User user2 = new User();
user2.setName("li");
hello.pass2(user2);
System.out.println("main:"+user2.getName());
}
public void pass2(User user) {
// user = new User();
user.setName("java new");
System.out.println("The name is :" + user.getName());
}
}
class User{
String name;
String getName()
{
return this.name;
}
void setName(String str)
{
name = str;
}
}
运行结果二
The name is :java new
main:java new
分析
从运行结果一可知:方法可以修改main()中的数组内容,这就说明在Java中传递数组类型的数据是直接把地址传递给方法。那这是不是就说明数组类型的数据是“按引用传递”呢?进一步分析exchangeArray(b,c)调用的结果——数组b,c内容没有交换,可知exchangeArray方法的参数a和b被初始化为两个数组的副本,这个方法交换的是这两个副本。当exchangeArray方法结束后参数变量a和b被丢弃。原来的变量b和c保持不变。
从运行结果二可知:方法可以改变一个对象参数的值(方法得到的是对象引用的副本,原来的对象引用和这个副本都引用同一个对象,这样改变引用的副本所引用的对象的值也就改变了原来对象引用所引用的对象)。另外,在测试代码一中试图对string对象进行修改,然而从运行结果一来看,changeArrayString方法并没有改变string对象str的值。那么这是否与我们从测试二中得出的结论矛盾呢?毕竟str是String类的对象。其实并不矛盾,因为在changeArrayString方法中str = str.replace(" ", “”);这句右边调用的replace方法最后返回的是一个新的字符串,也就是说str引用的内容已经发生变化了不再是原来main中实参引用的副本了。
总结
在Java中只有按值传递,对于基本数据类型来说传递的是原来值的副本;对于对象、数组、接口来说传递的是引用的副本。