共计 1707 个字符,预计需要花费 5 分钟才能阅读完成。
传值和传址有什么区别?Java 是传值还是传址?
开始
Java 在传参时,是传值还是传址?
传值和传址
假设要将 A 传到 B。如果是传值,就意味着将 A 中存放的值复制一份给 B,B 存的是跟 A 一样的值,B 的改变,不会影响到 A。如果是传址,则是将 A 的地址复制给 B,改变 B 时,事实上会根据 B 中存放的地址找到 A,所以,B 的改变,也就代表 A 的改变。
传值和传址
Java 是传值
java 是传值。首先,你要明确这个概念,然后,我们来看看,为什么这样说?
在 java 中,没有指针这个概念。传参时,确实是将 A 的值复制了一份到 B,B 的改变并不会影响 A 中的值。说到这里,同学就该疑惑了,说这怎么可能呢?没关系,我们继续往下聊。
分三种情况来讨论这个问题。分别是基本类型,对象和数组,还有 String。
我们假设有数据 V,赋值给变量 A,要传参到变量 B。
基本类型
先来看基本类型。假设 V 是一个基本类型,那么,A 中存放的就是数据 V 本身。此时,复制一份到 B,意味着 B 中也存储了数据 V1。注意,这里特别注明 V1,代表的是 V 和 V1 的值相同,它们是同样的值,但不是同一个值。所以,对 B 的操作,不会影响到 A,这是比较容易理解的。
对象和数组
再来看对象和数组。假设 V 是一个对象或数组,那么,A 中存放的就是数据 V 在堆中的地址(V 也可以存放在其它内存空间中),我们称之为引用类型。此时,复制一份到 B,意味着 B 中也存储了数据 V 的地址。注意,这里,A 和 B 中的值确实也是一样的,但是,不是同一个值。那么,接下来我们对 B 操作,看看会发生什么。我们分两种情况来讨论。宝宝起名宝典
第一种情况,我们假设修改了对象 V 的某个属性或数组中的某个元素。此时,实际上做的是,根据 B 中存储的值,找到对应的地址,修改了对应地址中的值。这样,由于 A 和 B 存储的是同样的地址,所以,当再通过 A 去获取对应地址中的值时,就会发现,A 变量也改变了。
第二种情况,我们创建了一个新的对象或数组,并将其赋值给 B。那么,此时 A 会改变吗?我们说,不会,因为 A 和 B 原来只是存储了同样的值,并不是同一个值,所以,修改了 B 中的值,并不会影响到 A,A 还是指向它原来指向的对象。总结一下就是,当修改 B 本身的值时,不会影响 A,当修改的是 B 中地址对应的对象或数组时,会影响到 A。
String 常量池
最后,我们来看 String。我们知道,String 并不是一个基本类型,那么,它是不是应该和对象或数组的特性一样呢?不是,因为 String 用了一个特殊的机制,叫做常量池。当你新建一个 String 对象(通过字面量的方式)时,事实上,JVM 真正做的是,在常量池中找对应的值,找到就把地址给变量,找不到,就在常量池中新建一个值,再把地址给变量。这里,有两层含义。
第一,如下,a 和 b 存储的实际是同样的常量池地址。
第二,a 中存储的地址已经改变了,但是,常量池中,原先存放 hello 的位置中的值,还是没有改变,JVM 会在常量池中重新找一个地方,存放 hello world,并把地址给 a。
所以,当把 A 传给 B 时,通过字面量的方式修改 B 的值,事实上不会对 A 造成影响。而如果是通过 new 的方式,新建一个 String 对象赋值给 B,那就和对象或数组的情形一样了,一会对 A 造成影响。
举个栗子
基本类型时,A 好比是一张纸条,当要传值给 B 时,事实是把纸条上的内容抄给了 B,所以,B 去修改自己纸条上的内容,当然不会对 A 产生任何影响。
引用类型时,A 好比是一把仓库钥匙,当要传值给 B 时,事实上是把钥匙配了一把给 B。B 把自己钥匙扔掉,或者是盘几年,包浆了,都不会对 A 产生影响,但是,B 如果拿着钥匙打开仓库,把仓库里的东西都卖了,那当 A 打开仓库时,自然就会吓得一屁股坐到了地上。
常量池时,A 好比是一张纸条,当要传值给 B 时,事实是把纸条上的内容抄给了 B。听起来好像和基本类型是一样的,其实不然。因为纸条上写的是一个字典上对应的页码。当 B 修改自己的纸条时,当然不会对 A 有影响。而当 B 修改字典上对应页码的内容时,事实上,B 是不能这么干的。字典有防御功能,它会在字典中找到 B 需要的值,把相应的页码给 B。所以,这也不会对 A 造成影响。
结论
Java 是传值,基本类型时,复制的是值,引用类型时,复制的是引用,也就是地址。