Java编程语言中最大的困惑之一是java是按值传递(Pass by Value)还是按引用传递(Pass by Reference)。在面试中经常提这个问题,但仍然看到应聘者对此感到困惑。
首先,我们应该理解通过值传递和通过引用传递的含义。
- 传递值:将方法参数值复制到另一个变量,然后传递复制的对象,它称为按值传递。
- 通过引用传递:将实际参数的别名或引用传递给方法,它称为按引用传递。
Java总是按值传递而不是通过引用传递,我们可以用一个简单的例子来证明它。
假设有一个类Balloon
,如下所示。
public class Balloon {
private String color;
public Balloon(){}
public Balloon(String c){
this.color=c;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
下面有一个简单的程序,使用通用方法交换两个对象,该类如下所示。
public class Test {
public static void main(String[] args) {
Balloon red = new Balloon("Red"); //memory reference 50
Balloon blue = new Balloon("Blue"); //memory reference 100
swap(red, blue);
System.out.println("red color="+red.getColor());
System.out.println("blue color="+blue.getColor());
foo(blue);
System.out.println("blue color="+blue.getColor());
}
private static void foo(Balloon balloon) { //baloon=100
balloon.setColor("Red"); //baloon=100
balloon = new Balloon("Green"); //baloon=200
balloon.setColor("Blue"); //baloon = 200
}
//Generic swap method
public static void swap(Object o1, Object o2){
Object temp = o1;
o1=o2;
o2=temp;
}
}
当执行上面的程序时,得到以下输出。
red color=Red
blue color=Blue
blue color=Red
从输出的前两行中,可以很明显swap
方法没有起什么作用。这是因为Java是按值传递的,这个swap()
方法测试可以与任何编程语言一起使用,以检查它是通过值传递还是通过引用传递。
让我们一步一步地分析程序的执行情况。
Balloon red = new Balloon("Red");
Balloon blue = new Balloon("Blue");
当使用new
运算符创建类的实例时,将创建实例,并且变量包含保存对象的内存的引用位置。假设“red”指向50
并且“blue”指向100
的内存位置,这些是两个Balloon
对象的内存位置。
现在,当调用swap()
方法时,会创建两个新变量o1
和o2
,分别指向50
和100
。
所以下面的代码片段解释了swap()
方法执行中发生的事情。
public static void swap(Object o1, Object o2){ //o1=50, o2=100
Object temp = o1; //temp=50, o1=50, o2=100
o1=o2; //temp=50, o1=100, o2=100
o2=temp; //temp=50, o1=100, o2=50
} //method terminated
请注意,上面更改o1
和o2
的值,但它们是“Blue”和“Red”引用位置的副本,因此实际上“Blue”和“Red”的值没有变化,因此输出也没有变化。
如果已经理解了这一点,可以很容易地理解混乱的原因。由于变量只是对象的引用,我们感到困惑的是传递了引用,因此java是通过引用传递的。但是传递了一个引用的副本,因此它通过了值。
现在让我们分析一下foo()方法的执行情况。
private static void foo(Balloon balloon) { //baloon=100
balloon.setColor("Red"); //baloon=100
balloon = new Balloon("Green"); //baloon=200
balloon.setColor("Blue"); //baloon = 200
}
第一行是重要的一行,当我们调用一个方法时,在引用位置的Object
上调用该方法。此时,balloon
指向100
,因此它的颜色变为Red
。
在下一行中,balloon
的引用更改为200
,并且执行的任何其他方法都发生在存储器位置200
处的对象上,并且对存储器位置100
处的对象没有任何影响。这解释了我们的程序输出的第三行打印:blue color=Red
。
变量是引用或指针,它的副本传递给方法,所以java总是按值传递。