在java中参数传递只有一种,就是值传递,没有引用传递。
如上,在类中,我们创建了一个changeNum()方法,用于改变传入参数的值(将其扩大二倍),但是在输出结果,实际都是2,该方法并没有改变其值。好的,由此我们引入程序的内存图并加以解释:
在java中参数传递只有一种,就是值传递,没有引用传递。
在java中参数传递只有一种,就是值传递,没有引用传递。
介绍:
值传递:就是在方法中形参接收的是一个数值,对数值进行的处理。在传递参数的过程中,将数值复制出一份(副本),副本传入形参中,进行处理,原参数并没有产生改变。
引用传递:在方法形参接收的是对象的地址值(也称之为引用),这种操作的结果会影响最终的结果,是通过地址找到原参数进行的一些列操作,并不是对原参数的副本进行的操作。这种参数在c语言中使用,实际就是指针的使用,&a,取到的是a的地址,指向的实际内存空间中a的值进行操作。
那么在java中为什么只有值传递,没有引用传递呢?
下面使用一组例子和内存分析图作下解释。
1.基本数据类型作参数传递
例子如下:
public class Test {
public static void main(String[] args) {
int num = 2;
System.out.println("改变之前的数值是:"+num);
changeNum(2);
System.out.println("改变之后的数值是:"+num);
}
//改变传入的参数数值的方法
public static void changeNum(int num){
num = num * 2;
}
}
改变之前的数值是:2
改变之后的数值是:2
解释:在内存中,num存储的数值是2,然后调用changeNum()方法,将num的值改为4,但是,改完的值另开辟了一块存储空间用于存储,并不是原存储空间的值(也不是在原存储空间中值上直接覆盖存储),调用完成,由于作用域的不同,我们在main中调用的num依旧是之前的num,其指向的还是原值。
2.对象做参数传递
例子如下
public class Test {
public static void main(String[] args) {
Person p = new Person();
System.out.println("更改之前的值为:"+p.num);
changeValue(p);
System.out.println("更改之后的值为:"+p.num);
}
//改变属性值的方法
public static void changeValue(Person p){
p.num=p.num*2;
}
}
class Person { //人类,仅有一个属性。
int num = 2;
}
更改之前的值为:2
更改之后的值为:4
如上,创建了一个类Test用于测试对象作为参数的传递方式,创建了一个类Person,里面仅有一个属性num,在Test类中,写了一个
changeValue(),传入的参数是Person类的而对象。
内存分析图:
解释:首先创建Person p 的引用 指向 堆内存中Person的对象,在栈内存中,保存的是堆内存中对象的地址,然后传入changValue()中,进行修改,
实际传入的是对象的地址值,直接对地址值对应的堆内存中的对象进行了操作,
在main方法,仍然是通过Person p这个引用去寻找堆内存中对象的值,实际已经改变。
下面,再来分析一个特别的例子,string和数组都比较特殊,都不是基本数据类型,而是引用数据类型,更为确切的说,String实际可以看作是一个类;数组属于引用类型,二者都是存储在堆内存中。
例子如下:
public class Test {
public static void main(String[] args) {
int[] ch = {0,0,0}; //int类型数组
String str = "before"; //字符串
System.out.println("更改之前的为:"+str+"----"+Arrays.toString(ch));
change(str,ch);
System.out.println("更改之后的为:"+str+"****"+Arrays.toString(ch));
}
//改变字符串和数组的方法
public static void change(String str,int[] ch){
str = "changed";
ch[1]=1;
}
}
更改之前的为:before----[0, 0, 0]
更改之后的为:before****[0, 1, 0]
这个例子中,change()方法中传入的参数是一个String和一个int类型数组。
内存分析图:
解释:
调用change()方法时,是将str和ch[ ]的堆空间地址传入,并且在栈内存中开辟了新的地址创建变量(即副本)存放传入的地址,在方法中,执行str = “changed”,这句等价于 str = new String("changed");实际上是在堆内存中重新开辟了一块内存,用于创建一个新的对象存储changed,并且将新内存地址赋值给str;第二个参数传入的也是ch[ ]的堆内存地址,方法中,对ch[1]进行操作,实际上可以看做是 对象名.变量 这个操作,即对地址指向的堆内存对象的属性直接操作,并没有改变其地址值,因此,在main方法中调用String str 和 int[ ] ch还是指向的原地址的对象,str并没有修改,而ch[ ]已经被修改。
总结:
在java中参数传递只有一种,就是值传递,没有引用传递。
在java中参数传递只有一种,就是值传递,没有引用传递。
在java中参数传递只有一种,就是值传递,没有引用传递。
只是,有一些操作看上去是引用传递改变的,实际传入的是地址值,然后指向内存空间对应对象进行的修改,总之,都是内存中进行的副本进行的修改,有的会直接导致原对象值得改变。