java是值传递还是引用传递?
java到底是值传递(Pass by value)还是引用传递(Pass by reference),被这个问题困扰了很久,一直也没有去深入了解问题的本质。最近看了一篇文章后觉得理解又加深了一点。但还是存在一些疑惑,于是继续深究下去。
1、为什么会有这样的困惑?
首先,为什么会产生这样的疑惑呢?下面就用例子来说明一下疑惑的是如何产生的。我们知道java数据类型分为基本类型(Primitive Types)和引用类型(Reference Types)两种,对于这两种类型我们分开讨论。
1.1 基本类型
对于基本类型的参数传递,我想以下的代码运行结果都比较清楚。
/**
* Created by andy on 2016/12/10.
*/
public class PassByValueOfPrimitive {
public static void main(String[] args) {
int num = 1;
System.out.println("调用change()方法前 : num = " + num);
change(num);
System.out.println("调用change()方法后 : num = " + num);
}
private static void change(int arg) {
arg = 2;
System.out.println("在change()方法内部参数 : arg = " + arg);
}
}
运行结果:
调用change()方法前 : num = 1
在change()方法内部参数 : arg = 2
调用change()方法后 : num = 1
我们看到num的值在调用前后并没有发生改变,对于基本类型的参数传递还是比较好理解。接下来看一下引用类型的参数是如何传递的。
1.2 引用类型
首先分析以下代码的运行结果,然后再看后面的答案。
/**
* Created by andy on 2016/12/10.
*/
public class PassByValueOfReference {
public static void main(String[] args) {
Dog d = new Dog();
d.setName("旺财");
System.out.println("调用change()方法前 : d.name = " + d.getName());
change(d);
System.out.println("调用change()方法后 : d.name = " + d.getName());
}
private static void change(Dog dog) {
dog.setName("小强");
System.out.println("在change()方法内部参数 : d.name = " + dog.getName());
}
public static class Dog {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
运行结果:
调用change()方法前 : d.name = 旺财
在change()方法内部参数 : d.name = 小强
调用change()方法后 : d.name = 小强
怎么回事?“旺财”咋变成“小强”了呢?为什么和基本类型的结果不同呢?正是这个结果困扰了我好久,百思不得其解。保留疑问,继续往下看>>>
2、求解的过程
在查找原因的过程中,逐渐发现我对一些概念的理解不到位。那么我们就先要清楚以下几组概念,然后再做分析。
2.1 值和引用
值指的是变量存储的数据。
引用指的是数据在内存中的地址 。
2.2 值传递(Pass by value)VS 引用传递(Pass by reference)
值传递:将实参的值拷贝传递给形参,无论在方法内怎么改变形参的值(因为形参是局部变量),实参的值是不会改变的。
引用传递:将实参在内存中的地址拷贝传递给形参。形参相当于是实参的“别名”,对形参的操作其实就是对实参的操作,在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
2.3 java中的变量和值
java虚拟机规定了两种变量类型即:基本类型(Primitive Types)和引用类型(Reference Types),与此对应java有两种值类型即:基本值(Primitive Values)和引用值(Reference Values)。
2.4 原因分析
明白以上概念后,我们来分析一下java中传递引用类型的参数时为什么会发生1.2那样的结果。首先java的引用类型变量存放的是对象的引用地址,即此时变量保存的值就是对象的引用地址。将引用类型变量作为参数传递时,由值传递的概念可知其实是将对象的地址传递给了形参,即形参和实参保存的是同一个对象的地址。这样对形参做的任何改变也会反应到实参所指向的对象。到这里也就解释了1.2的结果是如何产生的。
3、结论
经过以上的分析可以确定java确实是值传递。只是java传递引用类型的参数时变量中保存的就是对象的引用地址,这确实困扰了很多人,因为这些引用也是按照值传递的。
在比较或分析问题的时候首先要弄清楚概念然后再往下进行。因为概念给出了对应名词的限定和使用范围,只有在弄清概念的基础上才能深入分析了解问题的本质。而不是根据自己的理解和猜测去从字面上理解,这样只会让自己越来越迷惑。
参考
1、java参数传递(值传递还是引用传递)
2、Java String对象以“引用”方式被传递
3、C++ 值传递、指针传递、引用传递详解