Java使用按值传递的函数调用方式,这往往使我感到迷惑。因为在基础数据类型和对象的传递上,我就会纠结于到底是按值传递,还是按引用传递。其实经过学习,Java在任何地方,都一直发挥着按值传递的本色。
首先,让我们看一看基础数据类型是如何按值传递的。
public static void main(String[] args) { int a = 2; calculate(a); System.out.println(a); } public static void calculate(int param) { param += 3; System.out.println(param); }
输出结果为:
5 2
这样的结果根据按值传递,并不难理解。
其次,让我们来看看复杂的对象时如何保持自己的“贞操”,而仅仅是把引用这个“才艺”展示给人们。
public static void main(String[] args) { // 包装类型,不可变类型 Integer b = new Integer(2); calculate2(b); System.out.println(b); // 日期类型,目标方法改变对象值,当前日期2013-09-24 Date d1 = new Date(); claculate3(d1); System.out.println(d1); // 日期类型,目标方法仅读取对象值,当前日期2013-09-24 Date d2 = new Date(); calculate4(d2); System.out.println(d2); } public static void calculate2(Integer param) { param += 3; System.out.println(param); } public static void claculate3(Date date) { date.setDate(date.getDate() + 1); System.out.println(date); } public static void calculate4(Date date) { date = new Date(date.getYear(), date.getMonth(), date.getDate() + 1); System.out.println(date); }
输出结果为:
5 2 Wed Sep 25 20:24:32 CST 2013 Wed Sep 25 20:24:32 CST 2013 Wed Sep 25 00:00:00 CST 2013 Tue Sep 24 20:24:32 CST 2013
从结果可以看出:
一、包装类型作为不可变的对象,原有的Integer对象并没有改变,所以输出与基础类型的一样。
二、日期类型是可变对象,当仅仅是读取原有对象中的值时,没有问题,调用函数中的打印结果不改变,和当前日期保持一致,只是目标函数中打印的是新对象的值,所以两次打印结果不同;当在目标方法中将原有对象改变,那么两次打印的都是原有对象的值,所以结果相同,并且与初始的new Date()不一样。
所以说呢,基础数据类型按值传递很容易理解,但是对于对象,Java也是按值传递,只不过传递的时候不是直接传递对象,而是传递引用的值,引用指向的对象相同而已。
以我的水平就能写这么多,希望大牛们觉得不对的地方,给我指出来。。。
评论
在方法调用栈上首先创建了一个引用[暂且称其为 StackObject]指向与参数相同的对象地址,这时候区分两个测试方法内部过程可以理解为:
calculate3 是 改变StackObject 所指向的对象的属性,因为这个所指向的对象与传递到函数的参数对象是同一个,因此改变是受影响的。
calculate4 是 将 StackReferece 重新指向了一个新的对象地址,从此之后就它就与传递到函数的对象没有关系了,因此改变不会影响到传递的参数对象。
博主只要在相应的地方打印各个参数和变量对象的内存地址即可一目了然。
不知道这么理解对不对了~~
仁兄解释的很透彻,和我表达的意思一样,但是我写的太概念化。有机会还是要对JVM深入了解一下,谢谢!
博主的博文也写的很好。其实很凑巧,昨天下午我自己也刚好测试了关于 Integer 包装类型的方法参数传递,结果居然不是意想中的对象值改变,也让我小惊喜了一把,到晚上偶然看到你的博文,大惊咱两居然同一天在测试统一个问题,真是有缘,哈哈。
你俩就搞基吧
在方法调用栈上首先创建了一个引用[暂且称其为 StackObject]指向与参数相同的对象地址,这时候区分两个测试方法内部过程可以理解为:
calculate3 是 改变StackObject 所指向的对象的属性,因为这个所指向的对象与传递到函数的参数对象是同一个,因此改变是受影响的。
calculate4 是 将 StackReferece 重新指向了一个新的对象地址,从此之后就它就与传递到函数的对象没有关系了,因此改变不会影响到传递的参数对象。
博主只要在相应的地方打印各个参数和变量对象的内存地址即可一目了然。
不知道这么理解对不对了~~
仁兄解释的很透彻,和我表达的意思一样,但是我写的太概念化。有机会还是要对JVM深入了解一下,谢谢!
博主的博文也写的很好。其实很凑巧,昨天下午我自己也刚好测试了关于 Integer 包装类型的方法参数传递,结果居然不是意想中的对象值改变,也让我小惊喜了一把,到晚上偶然看到你的博文,大惊咱两居然同一天在测试统一个问题,真是有缘,哈哈。
在方法调用栈上首先创建了一个引用[暂且称其为 StackObject]指向与参数相同的对象地址,这时候区分两个测试方法内部过程可以理解为:
calculate3 是 改变StackObject 所指向的对象的属性,因为这个所指向的对象与传递到函数的参数对象是同一个,因此改变是受影响的。
calculate4 是 将 StackReferece 重新指向了一个新的对象地址,从此之后就它就与传递到函数的对象没有关系了,因此改变不会影响到传递的参数对象。
博主只要在相应的地方打印各个参数和变量对象的内存地址即可一目了然。
不知道这么理解对不对了~~
仁兄解释的很透彻,和我表达的意思一样,但是我写的太概念化。有机会还是要对JVM深入了解一下,谢谢!
添加了 hashcode 的打印之后,就比较明显了
在方法调用栈上首先创建了一个引用[暂且称其为 StackObject]指向与参数相同的对象地址,这时候区分两个测试方法内部过程可以理解为:
calculate3 是 改变StackObject 所指向的对象的属性,因为这个所指向的对象与传递到函数的参数对象是同一个,因此改变是受影响的。
calculate4 是 将 StackReferece 重新指向了一个新的对象地址,从此之后就它就与传递到函数的对象没有关系了,因此改变不会影响到传递的参数对象。
博主只要在相应的地方打印各个参数和变量对象的内存地址即可一目了然。
不知道这么理解对不对了~~