Java值传递与引用传递深度解析
众所周知,Java存在8种基础数据类型。
不过,Java字节码中还有额外的两种基础数据类型,分别是reference type与returnAddress type。
想要学习Java 中的参数传递,就必须理解JVM是如何解释基础数据类型与引用数据类型的。
一切皆数据?一切皆对象?
我们先从最基础的赋值开始:
Object A = new Object();
new Object() 做了什么?
JVM将在GC堆中开辟一定大小的空间用以存放此Object,此Object是引用数据类型(注意不是reference类型)。
Object A 做了什么?
JVM将在虚拟机栈中此线程栈开辟出一个slot用以存放A,注意此处A为reference type。(slot可以理解为用以存放基础数据类型的线程私有的空间)
“ = ” 做了什么?
reference type 的A被赋值为先前new Object() 指令所产生的引用数据类型的一个对象。
案例分析
static String s1 = "abc" ;
public static void changeString(String s2){
s2="bcd";
}
changeString(s1);
运行以上代码,s1改变了吗?
答案是没有。
为什么呢?
因为JVM中传递的不是reference本身,而是reference的拷贝。
或者你可以理解为传递的是一个int类型的数据,不过这个int类型比较特殊,里面存放有一个对象的地址。
我们来一步一步分析上面的代码:
String s1 = "abc";
//JVM开辟了一定大小的空间用以存放一个String对象,我们假设这个空间的起始值为0x8000。那么reference类型的s1就被赋值为0x8000。
void changeString(String s2){
//JVM中传递的是基础数据类型reference ,此时开辟一个线程帧,为s2开辟一个slot,其值与s1相同,为0x8000
s2="bcd";
// 我们假设“bcd”的地址为0x9000,那么s2作为一个reference类型的数据将被赋值为0x9000
}
所以,函数changeString() 所做的只是新建了一个reference类型的数据s2,然后把“bcd”的地址赋值给了s2,对于s1和s1所指向的对象“abc”没有做任何事,我们当然也看不到任何改变了。
综上所述,JVM中传递的所有数据都是基础数据类型的数据,只是JVM会解析这是一个普通的数据类型还是 reference type 或 returnAddress type 。
所以,所有对形如s2这样的传入reference进行 = 的赋值,都不会改变s1的数据,即s1依然会指向原来的对象。
用不太严谨的话来说:Java中传递引用数据类型本质是传递其地址的拷贝。
所以,才说Java中只有值传递。
不过,值传递与引用传递并不能完全区分来看,将引用数据类型看做是传递引用对于理解Java、日常编程并无不可。