[Java] 由swap方法引发的重思考

老生常谈的话题:Java 传参的方式到底是传值 还是传引用?

其实不同的角度理解都有道理,而接下来 先摆明我的立场:传值!


首先,来分析下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中,传参实际上传递的是 副本值(即指向 被操作对象的引用 的副本)。

不错的参考:


ps:Java中 传参方式 与 C/C++中 是不同的原理,不可混淆。比如参数是Object类型时,Java中对内部成员变量的修改有效;C/C++中则无效。因为C/C++中如果不通过指针/引用 传递参数(即不通过传址方式),就会创建完全崭新的对象。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值