------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
昨天在论坛上看到个人问了个这样的问题。
public class Test
{
public static void main(String[] args)
{
String string = "Hello";
test(string);
System.out.println(string);
}
public static void test(String str)
{
str = "World";
}
}
为什么输出的是Hello而不是World?
说到这个问题就不得不提到java的参数传递机制。
一般来说参数传递的方式有两种:值传递和引用传递。
但是在JAVA中不存在引用传递,JAVA编程语言只有值传递。
JAVA中的参数传递,不论是值传递还是引用传递,其本质上传递的都是副本(其实就是值,我看有的帖子上称为副本,可能是值与值传递容易弄混,这里也称为副本)。
——当传递的参数类型是基本数据类型时,那么传过来的就是这个参数的一个副本,也就是原始参数的值。如果在函数中,副本中的值进行了变动,原始参数的值并不会随着副本的值而改动。
——当传递的参数类型是引用数据类型时,那么传过来的也是这个参数的一个副本,只不过这个副本是原始参数的地址的值。如果在函数中,副本中的地址没有进行变动,而是改变了地址中的某个值,那么函数内的变动会影响原始参数。如果在函数中,副本中的地址发生了改变,比如new了个新的对象,那么副本就指向了这个新的对象,而原始参数不会因为函数内的变动而变动。
下面分别从两种传递方式的内存模型讨论下java中的参数传递。
基本数据类型:
public class Demo1
{
public static void main(String[] args) {
int num = 3;
System.out.println("调用add方法前num=" + num);
test(num);
System.out.println("调用add方法后num=" + num);
}
public static void test(int num) {
num = 5;
}
}
运行结果:
调用add方法前num=3
调用add方法后num=3
内存:当执行int num = 3;程序在栈内存中开辟了一个新内存(如地址为0x13001),并存放了3这个数值。当执行到test方法时,程序又开辟了一个新的内存(如地址为0x13002),并将num的值3传了进来,此时num的值是3。当执行到num=5;后,0x13002地址中的值变成了5。
也就是说地址0x13002是函数中副本的地址,无论副本怎么改变,原始参数都不会发生变化。
引用数据类型:
public class Demo1
{
public static void main(String[] args) {
String[] array = new String[] {"Hello"};
System.out.println("调用reset方法前array中的第0个元素的值是:" + array[0]);
reset(array);
System.out.println("调用reset方法后array中的第0个元素的值是:" + array[0]);
}
public static void reset(String[] param) {
param[0] = "World";
}
}
运行结果:
调用reset方法前array中的第0个元素的值是:Hello调用reset方法后array中的第0个元素的值是:World
内存:当程序执行到String[] array = new String[] {"Hello"};时,程序在堆内存中new了一个新的对象地址为0x13001,并把这个地址给了栈内存中开辟了一个空间的array。当春训运行到reset方法时,程序如基本数据类型那样同样在内存中开辟了一个新空间存放param,并把array中存放的地址0x13001给了param,也就是说此时param也指向了0x13001这个地址。之后执行赋值语句param[0] = "World";,是对0x13001这二个地址中的值进行修改。所以原始参数中0x13001的值也进行了变动。
也就是说地址0x13001是作为一个值被传递进函数的,只要这个值没有改变(如不new新对象),通过这个地址对参数进行修改,原始参数的值也会改变的。
总结:无论是基本数据类型还是引用数据类型,进行的都是值传递,只不过值的形式不同一个传的是值,一个传的是地址。
最后再来看看一开始的问题。
test方法中传递的是一个字符串的地址,按理来说只要函数中不new新字符串,原始字符串中的值就会随着函数中的变动进行变化。
然而函数中的str = "World";相当于new了个新的字符串(str=new String("World");)。所以函数中的改动并没有影响原始参数。