今天在写代码的时候遇到了一个参数传递的问题,虽然我知道问题出现在哪里,但是问题的原因似懂非懂,看了很久之前写的一篇博客,在网上搜索了几篇文章
看了下,终于彻底弄懂了这个问题的原因,写篇文章总结下。
首先我们得明白几个概念:按值传递,按引用传递,栈内存,堆内存的区别。
一按值传递:按值传递传递的是数值,传递的时候会在内存中另开辟一块空间来存储这个数值,如:
public static void main(String[] args){
double a = 3.0;
double b = 3.0;
Test test = new Test();
test.play(a,b);
System.out.println("a:"+a);
System.out.println("b:"+b);
}
public void play(double a,double b){
a = 0.0;
b = 0.0;
}
transformLon这个函数中的参数就是按值传递的,a,b传递进去后会在内存中另开辟一块内存空间来存储a,b的值,所以函数里面对a,b的更改不会引起函数外面a,b值的改变
a,b打印出来后的结果为: 可以看出a,b的值并未改变。
二按引用传递:按引用传递其实也是可以理解为按值传递,只不过他这个值不是直接的数值,而是存储数值的地址,但是我们操作修改的却是数值,而不是地址
故按值传递时,会改变前面传过来的对象。如:
public static void main(String[] args){
String array[] = new String[]{"hello world"};
Test test = new Test();
System.out.println("调用play方法前的array[0]:"+array[0]);
test.play(array);
System.out.println("调用play方法后的array[0]:"+array[0]);
}
public void play(String array[]){
array[0] = "gaga";
}
我们来看一下打印的结果: 可以看出array[0]的值在调用play方法前后是不一样的,与前面的按值传递效
果相反。
三栈内存和堆内存:
栈内存是用来保存存储基本数据类型的数据和引用变量及对象。java中的基本数据类型的数值都是保存在栈中,程序员不能够直接的设置堆或栈。栈的主要优点为存取
速度快,缺点为存在于栈的数据大小和生存周期是确定的,缺乏灵活性。堆用于存放所有由new关键字所创建对象指向的数值的地址,并不是这个对象的地址。如:
由new 所创建的包装数据类型,Double,String 等 对象的数值则是保存在堆中。在堆中分配的内存由java虚拟机的垃圾回收器(Garbage Collection)GC自动回收。
具体栈和堆的区别可以去看一下我之前写的一篇文章:java中堆和栈的区别。
介绍了完了按值传递,按应用传递以及栈内存和堆内存之后,我们从内存方面来分析按值传递和按引用传递的区别。
首先我们看一下按值传递时,代码的内存模型图:
我们看一下按值传递时,a的内存模型的变迁,可以看出当 定义 doube a = 3.0;时程序会在栈内存中分配一块空间,将数值3.0保存在这块空间中,当将a 作为参数传递
给play方法时,程序会在内存中另外开辟一块空间A,用来保存a的数值3.0,当在方法play中对参数a进行了修改,a的值并未改变,改变的是A内存中的数值,他由3.0变
成了0.0。A中内存的数值和a中内存的数值没有任何关系。
上面就是按值传递的原理。
接下来我们来了解一下,按引用传递时,代码的内存模型:
我们看下按引用传递时的内存模型,了解引用传递时的内存模型就必须了解栈内存和堆内存,之前已经介绍过了,就不在这里在赘述了。从这个模型我们可以知道
当new 出Array数组时,会在栈内存中分配一区域,array数组有多少个元素就会分配多少块区域 。假设array[0]对象分配的是“地址a”,array[0]的数值则是在堆内存
中开辟了一块区域用于保存,当array[0]作为参数传递到play方法当中时,程序会在栈内存中分配一块区域保存array[0]对象(不是array[0]的数值,堆内存中只能通过
new 关键字来开辟区域)即:地址a,当我们在play方法中修改array[0]对象的数值时,其实是修改堆中保存的数值,而不是array[0]对象。于是堆中的数值由gaga被
改变为hello world,于是当我们打印出array[0]修改前后的数值时,可以看出已经改变了。这就是按引用传递时的原理。
按引用传递,其实也是按值传递,只不过他传递的是数值的地址,而不是数值。我们只要记住,凡是通过new关键字创建出来的对象,进行传递时都是按引用传递
基本数据类型的传递都是按值传递。按应用传递传递的是栈中的地址,按值传递传递的是栈内存中的值,这就是按值传递和按引用传递的主要区别。
参考资料: