- 形参和实参
实参:实际参数,在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”,必须有确定的值
形参:形式参数,是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数,不需要有确定的值
public static void main(String[] args){ String name = 'xiaotw';//实际参数 Paramter pt = new Paramter(); pt.getParam(name); } public void getParam(String str){//形参 }
- 值传递和引用传递
值传递:是指在调用函数时将实际参数复制一份传递到函数中,方法接收的实参值的copy,会创建副本
引用传递:是指在调用函数时将实际参数的地址直接传递到函数中,方法接受的是实参所引用对象在堆中的地址,不会创建副本,修改形参会影响到实参
1
public static void main(String[] args){ int i = 10; int j =20; sout(i,j); System.out.println("a = " +i); System.out.println("b = " +j); } public void sout(int i ,int j){ i = 30; j = 40; System.out.println("c = " +i); System.out.println("d = " +j); }
结果是:
a = 10 j = 20 c= 30 d = 40
案列2
public static void main(String[] args) { Paramter pt = new Paramter(); User xiaotw= new User(); xiaotw.setName("Xiaotw"); xiaotw.setAge(21); pt.sout(xiaotw); System.out.println(" main user is " + xiaotw);} public void sout(User user) { user.setName("xiaotw-aj"); System.out.println("sout user is " + user);}
结果:
main user is User{name='Xiaotw', age=21} sout user is User{name='xiaotw-aj', age=21}
通过以上两个案列,可能有些小伙伴便会得出一个结论:在传递普通类型的时候是值传递,在传递对象类型的时候是引用传递。
但是,大家再看看下面写个案列
案列3:
public class Person { private String name; // 省略构造函数、Getter&Setter方法 } public static void main(String[] args) { Person xiaoZhang = new Person("小张"); Person xiaoLi = new Person("小李"); swap(xiaoZhang, xiaoLi); System.out.println("xiaoZhang:" + xiaoZhang.getName()); System.out.println("xiaoLi:" + xiaoLi.getName()); } public static void swap(Person person1, Person person2) { Person temp = person1; person1 = person2; person2 = temp; System.out.println("person1:" + person1.getName()); System.out.println("person2:" + person2.getName()); }
结果:
person1:小李 person2:小张 xiaoZhang:小张 xiaoLi:小李
大家肯定会想:两个引用类型的形参互换并没有影响实参啊!
swap 方法的参数 person1 和 person2 只是拷贝的实参 xiaoZhang 和 xiaoLi 的地址。因此, person1 和 person2 的互换只是拷贝的两个地址的互换罢了,并不会影响到实参 xiaoZhang 和 xiaoLi 。
为了打消大家的疑虑,我们来看看,我们真正的改变参数,看看会发生什么?
public static void main(String[] args) { Paramts pt = new Paramts (); Person xiaotw= new Person(); xiaotw.setName("xiaotw"); xiaotw.setAge(21); pt.sout(xiaotw); System.out.println(" main person is " + xiaotw);} public void sout(Person person ) { person = new Person (); person .setName("xiaotw-aj"); person .setAge(24); System.out.println("sout person is " + person );}
结果:
pass , person is Person {name='xiaotw-aj', age=24} main , person is Person {name='xiaotw', age=21}
来老规矩,上图
当我们在main中创建一个Person对象的时候,在堆中开辟一块内存,其中保存了name和age等数据。然后xiaotw持有该内存的地址0x67890(图1)
当尝试调用sout方法,并且xiaotw作为实际参数传递给形式参数Person的时候,会把这个地址0x67890交给person,这时,person也指向了这个地址。(图2)
然后在sout方法内对参数进行修改的时候,即 person = new Person ();,会重新开辟一块0x67891的内存,赋值给person 。后面对person 的任何修改都不会改变内存0x67890的内容(图3)。
以上的传递如果是引用传递的话,在执行Person person = new Person ();的时候,实际参数的引用也应该改为指向0x67890,但是实际上并没有,所以不是引用传递。
通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数。
在参数传递的过程中,实际参数的地址0x67890被拷贝给了形参,只是,在这个方法中,并没有对形参本身进行修改,而是修改的形参持有的地址中存储的内容,而不是地址。
所以,值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)