[Java] 由swap方法引发的重思考
2013-11-03 11:25
204 查看
老生常谈的话题:Java 传参的方式到底是传值 还是传引用?
其实不同的角度理解都有道理,而接下来 先摆明我的立场:传值!
首先,来分析下Java中 "=" 的意义。
在"="的右边,是一个常量、表达式、或者对象(统统可以看成对象),在内存中会为它们分配空间。
而"="的左边,是一个引用(我们常说变量),保存的是右边对象的地址。
"="的实际意义就是 将右边对象的地址传给左边的引用,使得该引用指向了右边对象。
所以,Java里的引用可以看成是(C/C++所说的指针)。
另外,要明确一点:Java中都是通过使用引用来操作对象的。
为什么这么说呢?因为系统为"="右边的对象分配了空间,但是程序无法直接获取它,必须通过定义引用来得到。然后后面对该对象的操作都是通过操作该引用来实现。不管对于基本数据类型还是类对象,都是一样的处理流程。下面对基本数据类型和复杂的类对象进行说明:
(1)Java中创建基本数据类型变量的流程
编译器会首先创建一个变量名为a 的引用,然后在代码所在栈中查找是否有3这个常量值(如果没有,就在栈上开辟空间放入3),让a指向3。
接着编译器再创建一个变量名为b 的引用,在栈中发现已有3这个值,就让b也指向3。
这样,虽然a和b是不同的引用,但是都指向同一个值,a和b都是对这个值的引用。
(2)Java中创建复杂类型对象的流程
同样,编译器会首先在栈中创建一个对象变量名为person1的引用,然后在堆中实例化一个对象,让person1指向它。
接着,编译器再创建一个对象变量名为person2的引用,然后在堆中又实例化一个对象,让person2指向它。
当然,person1和person2是不同的对象变量,而且也指向不同的对象。
这些对象都是无法直接访问,都是通过定义了引用去指向它们。
------------------------------------------------------------------------------------------
预热结束了,开始直奔主题:为什么说Java传参都是传值呢?
因为Java中传递参数时,获得的形参其实是被操作对象的引用的一份副本(即形参是实参的拷贝),当然这份副本也是指向被操作对象的。从这种角度来说,Java的传参,就是传值的方式(传副本值)。
下面还是通过基本数据类型和复杂类对象进行说明。
(1)基本数据类型 传参分析
大家都知道这个方法是无效的。
main()方法中变量a是对常量3的引用,变量b是对常量5的引用。当a/b作为实参传递给badSwap方法时,传递的是a/b的拷贝(a'/b')。此时,a与a'指向3,b与b'指向5。经过badSwap方法处理后,改变的只是a'/b'的指向,并未影响到a/b。如图。
如果我们把传入的int型变量改为Object型结果也是一样的。改变的只是这份副本而已。就不分析了,下面分析复杂类对象的另一种情况。
(2)复杂类型的对象 传参分析
执行main方法,将得到以下输出:
结果:changeName方法成功改变了person的name值。为什么呢?
在main()方法中,person仅仅是对象的引用。当向changeName()传递person时,Java仅仅是传递了person这个引用的一个副本,即传向方法的引用实际上是原始引用的副本。这是一个传值操作(即副本值)。这样当Java传递对象引用的副本给方法后,就有两个引用指向了同一对象。
那为什么能成功改变person的值呢,如下图:
因为传值进来的是引用的副本,它也指向了原来的对象,虽然对副本的交换是无效的,但对副本所指向的对象的内部 进行的操作是会起作用的。图解很清晰了,引用所指向的对象的name属性指向了新的内存空间(存放着"adm")。
总结下:
1.Java中,使用引用来操作对象
2.Java中,传参实际上传递的是 副本值(即指向 被操作对象的引用 的副本)。
不错的参考:
http://blog.csdn.net/nameoccupied/article/details/8954367
http://blog.csdn.net/lcore/article/details/8712770
ps:Java中 传参方式 与 C/C++中 是不同的原理,不可混淆。比如参数是Object类型时,Java中对内部成员变量的修改有效;C/C++中则无效。因为C/C++中如果不通过指针/引用 传递参数(即不通过传址方式),就会创建完全崭新的对象。
其实不同的角度理解都有道理,而接下来 先摆明我的立场:传值!
首先,来分析下Java中 "=" 的意义。
在"="的右边,是一个常量、表达式、或者对象(统统可以看成对象),在内存中会为它们分配空间。
而"="的左边,是一个引用(我们常说变量),保存的是右边对象的地址。
"="的实际意义就是 将右边对象的地址传给左边的引用,使得该引用指向了右边对象。
所以,Java里的引用可以看成是(C/C++所说的指针)。
另外,要明确一点:Java中都是通过使用引用来操作对象的。
为什么这么说呢?因为系统为"="右边的对象分配了空间,但是程序无法直接获取它,必须通过定义引用来得到。然后后面对该对象的操作都是通过操作该引用来实现。不管对于基本数据类型还是类对象,都是一样的处理流程。下面对基本数据类型和复杂的类对象进行说明:
(1)Java中创建基本数据类型变量的流程
int a = 3; int b = 3;
编译器会首先创建一个变量名为a 的引用,然后在代码所在栈中查找是否有3这个常量值(如果没有,就在栈上开辟空间放入3),让a指向3。
接着编译器再创建一个变量名为b 的引用,在栈中发现已有3这个值,就让b也指向3。
这样,虽然a和b是不同的引用,但是都指向同一个值,a和b都是对这个值的引用。
(2)Java中创建复杂类型对象的流程
Person person1 = new Person("cb"); Person person2 = new Person("adm");
同样,编译器会首先在栈中创建一个对象变量名为person1的引用,然后在堆中实例化一个对象,让person1指向它。
接着,编译器再创建一个对象变量名为person2的引用,然后在堆中又实例化一个对象,让person2指向它。
当然,person1和person2是不同的对象变量,而且也指向不同的对象。
这些对象都是无法直接访问,都是通过定义了引用去指向它们。
------------------------------------------------------------------------------------------
预热结束了,开始直奔主题:为什么说Java传参都是传值呢?
因为Java中传递参数时,获得的形参其实是被操作对象的引用的一份副本(即形参是实参的拷贝),当然这份副本也是指向被操作对象的。从这种角度来说,Java的传参,就是传值的方式(传副本值)。
下面还是通过基本数据类型和复杂类对象进行说明。
(1)基本数据类型 传参分析
public void badSwap(int var1, int var2) { int temp = var1; var1 = var2; var2 = temp; } //调用badSwap方法 int main(...) { int a = 3, b = 5; badSwap(a, b); }
大家都知道这个方法是无效的。
main()方法中变量a是对常量3的引用,变量b是对常量5的引用。当a/b作为实参传递给badSwap方法时,传递的是a/b的拷贝(a'/b')。此时,a与a'指向3,b与b'指向5。经过badSwap方法处理后,改变的只是a'/b'的指向,并未影响到a/b。如图。
如果我们把传入的int型变量改为Object型结果也是一样的。改变的只是这份副本而已。就不分析了,下面分析复杂类对象的另一种情况。
(2)复杂类型的对象 传参分析
public void changeName(Person person) { person.name = "adm"; } public static void main(String [] args) { Person person = new Person("cb","Shanghai"); System.out.println("Name: " + person.name + ", Address: " +person.address); changeName(person); System.out.println("Name: " + person.name + ", Address: " +person.address); }
执行main方法,将得到以下输出:
Name: cb, Address: Shanghai Name: adm, Address: Shanghai
结果:changeName方法成功改变了person的name值。为什么呢?
在main()方法中,person仅仅是对象的引用。当向changeName()传递person时,Java仅仅是传递了person这个引用的一个副本,即传向方法的引用实际上是原始引用的副本。这是一个传值操作(即副本值)。这样当Java传递对象引用的副本给方法后,就有两个引用指向了同一对象。
那为什么能成功改变person的值呢,如下图:
因为传值进来的是引用的副本,它也指向了原来的对象,虽然对副本的交换是无效的,但对副本所指向的对象的内部 进行的操作是会起作用的。图解很清晰了,引用所指向的对象的name属性指向了新的内存空间(存放着"adm")。
总结下:
1.Java中,使用引用来操作对象
2.Java中,传参实际上传递的是 副本值(即指向 被操作对象的引用 的副本)。
不错的参考:
http://blog.csdn.net/nameoccupied/article/details/8954367
http://blog.csdn.net/lcore/article/details/8712770
ps:Java中 传参方式 与 C/C++中 是不同的原理,不可混淆。比如参数是Object类型时,Java中对内部成员变量的修改有效;C/C++中则无效。因为C/C++中如果不通过指针/引用 传递参数(即不通过传址方式),就会创建完全崭新的对象。
相关文章推荐
- 性能测试java协议使用httpclient方法引发的思考
- 性能测试java协议使用httpclient方法引发的思考
- 开车误闯红灯的补救方法 (以及由此引发的一些思考)
- 由【JAVA中参数传递问题】引发除了基本数据类型和引用类型的思考
- JVM处理Java数组方法的思考
- 由JavaScript中call()方法引发的对面向对象继承机制call的思考
- Java中由substring方法引发的内存泄漏
- java多态/重载方法——一个疑难代码引发的讨论
- 一个小错误引发思考最终得出数组转字符串的新方法
- 有关JVM处理Java数组方法的思考
- JAVA注解引发的思考
- 开车误闯红灯的补救方法 (以及由此引发的一些思考)
- 由Java中toString()方法引发的无意识的递归想到的
- [置顶] 【面试题】java装箱拆箱引发的思考
- 回帖整理: 关于TDD引发的流行方法的思考
- 关于java字节流的read()方法返回int型而非byte型的思考
- 黑马程序员_一道java选择题 引发: 类的创建和自身构造方法 加载的先后顺序
- java引发的思考----自己是面向对象的程序设计员吗?
- 对 Java 中 clone 方法的思考
- java方法重载深层思考