Java参数传递问题
2015-07-22 18:46
232 查看
下面通过内存模型的方式来讨论一下Java中的参数传递。
内存模型涉及到两种类型的内存:栈内存(stack)和堆内存(heap)。
基本类型作为参数传递时,传递的是这个值的拷贝:
这段代码运行的结果如下:
程序运行的结果也说明这一点,改变参数param的值,原值num不会改变。
通过内存模型来分析:
1、当执行了int num = 30;这句代码后,程序在栈内存中开辟了一块地址为AD8500的内存,里边放的值是30。
2、执行到add()方法时,程序在栈内存中又开辟了一块地址为AD8600的内存,将num的值30传递进来,此时这块内存里边放的值是30,执行param = 100;后,AD8600中的值变成了100。
3、 地址AD8600中用于存放param的值,和存放num的内存没有任何关系,无论\怎么改变param的值,实际改变的是地址为AD8600的内存中的值,而AD8500中的值并未改变,所以num的值也就没有改变。
以上是基本类型参数的传递方式,下来我们讨论一下对象作为参数传递的方式:
运行的结果如下:
当对象作为参数传递时,传递的是对象的引用,也就是对象的地址。
1、当程序执行了String[] array = new String[] {“huixin”}后,程序在栈内存中开辟了一块地址编号为AD9500内存空间,用于存放array[0]的引用地址,里边放的值是堆内存中的一个地址,假设为BE2500,可以理解为有一个指针指向了堆内存中的编号为BE2500的地址。堆内存中编号为BE2500的这个地址中存放的才是array[0]的值:huixin。
2、当程序进入reset方法后,将array的值,也就是对象的引用BE2500传了进来。这时,程序在栈内存中又开辟了一块编号为AD9600的内存空间,里边放的值是传递过来的值,即AD9600。可以理解为栈内存中的编号为AD9600的内存中有一个指针,也指向了堆内存中编号为BE2500的内存地址。这样一来,栈内存AD9500和AD9600(即array[0]和param的值)都指向了编号为BE2500的堆内存。
3、在reset方法中将param的值修改为hello, world!后,改变对象param的值实际上是改变param这个栈内存所指向的堆内存中的值。param这个对象在栈内存中的地址是AD9600,里边存放的值是BE2500,所以堆内存BE2500中的值就变成了hello,world!。程序放回main方法之后,堆内存BE2500中的值仍然为hello,world!,main方法中array[0]的值时,从栈内存中找到array[0]的值是BE2500,然后去堆内存中找编号为BE2500的内存,里边的值是hello,world!。所以main方法中打印出来的值就变成了hello,world!
无论是基本类型作为参数传递,还是对象作为参数传递,实际上传递的都是值,只是值的的形式不用而已。第一个示例中用基本类型作为参数传递时,将栈内存中的值30传递到了add方法中。第二个示例中用对象作为参数传递时,将栈内存中的值BE2500传递到了reset方法中。当用对象作为参数传递时,真正的值是放在堆内存中的,传递的是栈内存中的值,而栈内存中存放的是堆内存的地址,所以传递的就是堆内存的地址。这就是它们的区别。
(意思就是说:参数传递始终是传递栈内存中的值,而对于基本类型,其值就在栈内存中,而对于引用类型,其值存在于堆内存中,栈内存中只是存放其地址,因而,对于引用类型,改变方法参数,会改变原参数本身!!!)
补充一下,在Java中,String是一个引用类型,但是在作为参数传递的时候表现出来的却是基本类型的特性,即在方法中改变了String类型的变量的值后,不会影响方法外的String变量的值。关于这个问题,可以参考如下两个地址:
http://freej.blog.51cto.com/235241/168676
http://dryr.blog.163.com/blog/static/58211013200802393317600/
我觉得是这两篇文章中提到的两个原因导致的,一个是String实际上操作的是char[],可以理解为String是char[]的包装类。二是给String变量重新赋值后,实际上没有改变这个变量的值,而是重新new了一个String对象,改变了新对象的值,所以原来的String变量的值并没有改变。
内存模型涉及到两种类型的内存:栈内存(stack)和堆内存(heap)。
基本类型作为参数传递时,传递的是这个值的拷贝:
public class ParameterTransfer { public static void main(String[] args) { int num = 30; System.out.println("调用add方法前num=" + num); add(num); System.out.println("调用add方法后num=" + num); } public static void add(int param) { param = 100; } }
这段代码运行的结果如下:
调用add方法前num=30 调用add方法后num=30
程序运行的结果也说明这一点,改变参数param的值,原值num不会改变。
通过内存模型来分析:
1、当执行了int num = 30;这句代码后,程序在栈内存中开辟了一块地址为AD8500的内存,里边放的值是30。
2、执行到add()方法时,程序在栈内存中又开辟了一块地址为AD8600的内存,将num的值30传递进来,此时这块内存里边放的值是30,执行param = 100;后,AD8600中的值变成了100。
3、 地址AD8600中用于存放param的值,和存放num的内存没有任何关系,无论\怎么改变param的值,实际改变的是地址为AD8600的内存中的值,而AD8500中的值并未改变,所以num的值也就没有改变。
以上是基本类型参数的传递方式,下来我们讨论一下对象作为参数传递的方式:
public class ParameterTransfer { public static void main(String[] args) { String[] array = new String[] {"huixin"}; System.out.println("调用reset方法前array中的第0个元素的值是:" + array[0]); reset(array); System.out.println("调用reset方法后array中的第0个元素的值是:" + array[0]); } public static void reset(String[] param) { param[0] = "hello, world!"; } }
运行的结果如下:
调用reset方法前array中的第0个元素的值是:huixin 调用reset方法后array中的第0个元素的值是:hello, world!
当对象作为参数传递时,传递的是对象的引用,也就是对象的地址。
1、当程序执行了String[] array = new String[] {“huixin”}后,程序在栈内存中开辟了一块地址编号为AD9500内存空间,用于存放array[0]的引用地址,里边放的值是堆内存中的一个地址,假设为BE2500,可以理解为有一个指针指向了堆内存中的编号为BE2500的地址。堆内存中编号为BE2500的这个地址中存放的才是array[0]的值:huixin。
2、当程序进入reset方法后,将array的值,也就是对象的引用BE2500传了进来。这时,程序在栈内存中又开辟了一块编号为AD9600的内存空间,里边放的值是传递过来的值,即AD9600。可以理解为栈内存中的编号为AD9600的内存中有一个指针,也指向了堆内存中编号为BE2500的内存地址。这样一来,栈内存AD9500和AD9600(即array[0]和param的值)都指向了编号为BE2500的堆内存。
3、在reset方法中将param的值修改为hello, world!后,改变对象param的值实际上是改变param这个栈内存所指向的堆内存中的值。param这个对象在栈内存中的地址是AD9600,里边存放的值是BE2500,所以堆内存BE2500中的值就变成了hello,world!。程序放回main方法之后,堆内存BE2500中的值仍然为hello,world!,main方法中array[0]的值时,从栈内存中找到array[0]的值是BE2500,然后去堆内存中找编号为BE2500的内存,里边的值是hello,world!。所以main方法中打印出来的值就变成了hello,world!
无论是基本类型作为参数传递,还是对象作为参数传递,实际上传递的都是值,只是值的的形式不用而已。第一个示例中用基本类型作为参数传递时,将栈内存中的值30传递到了add方法中。第二个示例中用对象作为参数传递时,将栈内存中的值BE2500传递到了reset方法中。当用对象作为参数传递时,真正的值是放在堆内存中的,传递的是栈内存中的值,而栈内存中存放的是堆内存的地址,所以传递的就是堆内存的地址。这就是它们的区别。
(意思就是说:参数传递始终是传递栈内存中的值,而对于基本类型,其值就在栈内存中,而对于引用类型,其值存在于堆内存中,栈内存中只是存放其地址,因而,对于引用类型,改变方法参数,会改变原参数本身!!!)
补充一下,在Java中,String是一个引用类型,但是在作为参数传递的时候表现出来的却是基本类型的特性,即在方法中改变了String类型的变量的值后,不会影响方法外的String变量的值。关于这个问题,可以参考如下两个地址:
http://freej.blog.51cto.com/235241/168676
http://dryr.blog.163.com/blog/static/58211013200802393317600/
我觉得是这两篇文章中提到的两个原因导致的,一个是String实际上操作的是char[],可以理解为String是char[]的包装类。二是给String变量重新赋值后,实际上没有改变这个变量的值,而是重新new了一个String对象,改变了新对象的值,所以原来的String变量的值并没有改变。
相关文章推荐
- java进阶
- spring中关于定时器的配置
- 在Struts2使用集合对象保存表单提交参数
- java.lang.IllegalStateException: WRITER
- java可变参数使用总结
- Java 敏感字验证
- java注解入门(含源码下载)
- Struts2的声明式异常处理
- 基于注解的 Spring MVC 简单入门
- java注解再学习
- 使用Struts2的iterator标签遍历复杂Map种类
- java NIO原理和应用
- Java基础学习总结——Java对象的序列化和反序列化
- java exception使用的性能影响,用数据说话
- java查看jar源代码
- Spring与Mybatis整合环境搭建
- java统计图echarts
- org.eclipse.e4.core.di.InjectionException:org.eclipse.swt.SWTException: Widget is disposed
- Spring声明式事务管理之xml事务管理
- 个人学习Java的一些心得!