边实验边分析 - 破解Java参数传递的奥秘(值传递?引用传递?)
对于Java的这个知识点,我觉得大家可能会有时候会模糊,我在之前就因为对这个知识点的理解不到位,导致了在开发测试过程中的偏差,感觉自己的水平被同事误判了的,所以定下心来研究了下这个知识点,自己总结了一下,不过这个知识点其实没有一个标准的答案,全都在于自己的理解,理解到位了,则不会有大问题,理解不到位,可能就会出现看起来很低级的错误。
所以本篇文章也不是要说明到底是哪一种,只是分享一下我对这个问题的理解,方便大家对这个问题的理解,因为深究这个问题是没有意义的,全看大家的看法而已。
我们先来看一个例子,
private void changeInt(int localI){
localI = 1;
}
int i = 0;
changeInt(i);
Log.d("DemoActivity!", "i == " + i);
运行结果
D/DemoActivity!: i == 0
可以看到,i的值没有依照我们在changeInt方法中对其的改变而改变,我们来分析下这段行为
首先我们需要了解到一个重要的概念,也是这个问题点的一个核心的地方,就是Java在内存中存储变量时候遵循的规则
1.局部变量像基本类型和对象引用是在栈内存中创建的。
2.而真正的对象在堆内存上创建。
上述例子中,在changeInt方法执行的时候,按照上面的规则,localI是一个局部变量,并且是基本类型,所以其会存放在changeInt方法的方法栈中,并且会将i的值复制到localI中,这时候如果我们修改localI的值,等到该方法结束退出的时候,localI就被释放了,最终不会影响到i的值
我们可以使用debug,来证实一下上面的说法,
初始化i=0
localI为0
localI值改变为1
并未改变i的值
我们再来看另一个例子
class CoreType {
public int id;
public String displayName;
public String coreTypeString;
public CoreType(int id, String displayName, String coreTypeString) {
this.id = id;
this.displayName = displayName;
this.coreTypeString = coreTypeString;
}
@Override
public String toString() {
return displayName;
}
}
private void changeType(CoreType localType){
localType.id = 100;
localType.displayName = "123";
localType = null;
}
CoreType type = new CoreType(0, "", "");
type.displayName = "111";
changeType(type);
Log.d("DemoActivity!", "ct.id == " + type.id + " ct.name == " + type.displayName);
这次我们传入的是一个对象,这个对象里面有一个int值和一个String值的两个变量,我们给与其初始值,然后在changeType中去修改值,并且我们直接把这个对象置为null
运行结果
D/DemoActivity!: ct.id == 100 ct.name == 123
可以看到,这次的值都被替换掉啦,这是什么原因呢,我们来分析一下
首先创建了一个ct对象,其本身是存放在栈上的,因为其本身也是个局部变量,但是其存放的只是一个内存的引用地址,这个引用地址指向堆中,所以对象真正被存放在堆中,接下来我们调用changeType方法,传入了该对象,在方法中,系统一样会在栈上创建出localType对象,但这个localType对象的地址指向的内存其实是和type对象指向的地址是一样的,所以我们对localType中的属性的修改其实就等于对type对象的修改,而我们最后将localType置为null却是没有意义的,因为localType只是一个局部变量,把它置为null也只是将其与指向堆内存的地址切断了,但是并不会影响到type和其指向的堆内存的链接关系,当方法调用结束后,localType对象就从栈中释放了
我们来使用debug验证一下
内存地址为11921
localType的内存地址也为11921,并改变localType的值
将localType置为null
type内的值改变,但type不为null
可以看到,其地址指向的是同一块地方
其他的类型大家也可以尝试一下,基本类型会和第一个例子一样,而对象则会和第二个例子一样,所以关键点在于java在处理这两种类型的局部变量上的不同,基本类型直接存的是值的拷贝,而对象存放的是地址的拷贝,至于要说是是值传递还是引用传递的,那还真是看个人理解了,不过,如果是和c++比较的话,那我觉得Java应该是属于值传递的,因为c++有真正的引用传递,相信熟悉c++的同学知道,可以通过"&"符号来定义一个参数其为一个引用