前言
之前一直有疑惑,Java到底是按值传递还是按引用传递,大家都知道基本数据类型是按值进行传递,那么对象是怎么传递的呢?
概念
我们先下简单过一下,这两个概念:
按值调用(call by value):表示方法接收的是调用者提供的值。
按引用调用(call by reference):表示方法接收的是调用者提供的变量地址。
按值调用,也就是说方法得到的所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量内容。
基本数据类型
我们都知道基本数据类型是按值传递,下面写一个简单的demo:changeValue()方法试图改变一个参数的值:
public static void changeValue(double x) { //doesn’t work
x = 2 * x;
System.out.println("x==" + x);
}
public static void main(String[] args) {
double number = 10;
changeValue(number);
System.out.println("number==" + number);
}
结果如下:number的值仍然为10,并没有改变。
x==20.0
number==10.0
具体的执行流程如下,如下图所示,对值参数的修改没有保留下来:
- x被初始化为number值的一个拷贝(也就是10);
- x被乘于2后等于20,但是number仍然是10;
- 这个方法结束后,参数变量x不再使用。
对象引用
上面讲了基本数据类型的参数不能被改变,而对象引用作为参数就不同了,可以很容易实现对参数的改变,如下所示:
public static void main(String[] args) {
Basic basic = new Basic(1, "hello");
Basic basic1 = change(basic);
System.out.println(basic.toString());
}
static Basic change(Basic x) { //work
x.setA(2);
x.setS("world");
System.out.println(x.toString());
return x;
}
结果:我们可以看到Basic对象的值变了。
2====world
2====world
具体的执行流程如下,如下图所示,对对象参数的修改保留了下来:
- x被初始化为basic值的拷贝,这里是一个对象的引用;
- change方法应用于这个对象引用。x和basic同时引用的那个Basic对象的内容改变成了(2,world);
- 方法结束后,参数变量x不再使用。当然,变量basic继续引用那个修改后的(2,world)对象。
why?
方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用了同一个对象。那么我们可以认为Java对象采用的是引用调用了吗??别急,继续看下,我们写一个交换x,y的swap方法,如果是引用调用,那么理论上应该baisc和basic1的值交换的效果。
public static void main(String[] args) {
Basic basic = new Basic(1, "hello");
Basic basic1 = new Basic(2,"world");
swap(basic,basic1);
System.out.println(basic);
System.out.println(basic1);
}
static void swap(Basic x, Basic y) {
Basic temp = x;
x = y;
y = temp;
}
结果是什么?what?竟然值没有发生改变…,因为方法并没有改变存储在变量basic和basic1中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝。最终,白费力气,在方法结束时参数变量x和y被丢弃了。原来的变量basic和basic1仍然引用了这个方法调用之前所引用的对象。
实际上,对象引用是按值传递的,不过"值"是对应的引用。
1====hello
2====world
小结
不管是基本数据类型还是对象引用其实都是值传递,传递是值的拷贝,基本数据类型是其本身值的拷贝,对象的引用的拷贝,拷贝的引用并不会改变原引用。