如何判断是值传递还是引用传递
值传递
方法被调用的时候,实参将值传递给函数的形参相当于函数拥有了这个实参的副本,函数利用传递来的形参可以进行计算,或者 将传递的值重新赋值,但是因为重新赋值操作的也是传递而来的副本,所以不会影响原始的值
public static void valueCrossTest(int age,float weight){
System.out.println("传入的age:"+age);
System.out.println("传入的weight:"+weight);
age=33;
weight=89.5f;
System.out.println("方法内重新赋值后的age:"+age);
System.out.println("方法内重新赋值后的weight:"+weight);
}
//测试
public static void main(String[] args) {
int a=25;
float w=77.5f; // 是指向值得变量,不是指向地址得引用变量
valueCrossTest(a,w);
System.out.println("方法执行后的age:"+a);
System.out.println("方法执行后的weight:"+w);
}
传入的age:25
传入的weight:77.5
方法内重新赋值后的age:33
方法内重新赋值后的weight:89.5
方法执行后的age:25
方法执行后的weight:77.5
为什么副本传进去之后,最终不影响实参的值
因为:
- 在程序运行的时候,首先程序运行时,调用mian()方法,此时JVM为main()方法往虚拟机栈中压入一个栈帧,即为当前栈帧,用来存放main()中的局部变量表(包括参数)、操作栈、方法出口等信息,如a和w都是mian()方法中的局部变量,因此可以断定,a和w是躺着mian方法所在的栈帧中
- 而当执行到valueCrossTest()方法时,java虚拟机也为其往栈中压入一个栈,即为当前栈帧,用来存放valueCrossTest()中的局部变量等信息,因此age和weight是躺着valueCrossTest方法所在的栈帧中,而他们的值是从a和w的值copy了一份副本而得
- age和weight的重新赋值,只是改变了当前栈帧(valueCrossTest方法所在栈帧)里的内容,当方法执行结束之后,这些局部变量都会被销毁,mian方法所在栈帧重新回到栈顶,成为当前栈帧,再次输出a和w时,依然是初始化时的内容。
引用传递
引用就是真是的值,实参传递给方法调用中的形参的值是地址,在方法体内形参和实参指向的是同一块地址,对形参操作会影响指向的地址的内容
引用传递分两种情况
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static void PersonCrossTest(Person person){
System.out.println("传入的person的name:"+person.getName());
person.setName("我是张小龙");
System.out.println("方法内重新赋值后的name:"+person.getName());
}
//测试
public static void main(String[] args) {
Person p=new Person();
p.setName("我是马化腾");
p.setAge(45);
PersonCrossTest(p);
System.out.println("方法执行后的name:"+p.getName());
}
传入的person的name:我是马化腾
方法内重新赋值后的name:我是张小龙
方法执行后的name:我是张小龙
在堆中存放了new Person()实例,p为引用类型变量存放在栈帧中的局部变量表中,值为地址存放在栈中经过PersonCrossTest()方法的时候,p的值是地址,此时在方法中操作,操作的是同一个对象。
可以看出person经过了personCrossTest()方法执行之后,内容发送了变化,对形参的操作改变了实际对象的内容,但这不能说明是引用传递,实际上产生这种情况是,实参传递给形参的是地址,只是在一个副本中(一个新的栈帧),在这个副本中形参指向了实参指向的地址,因此对其操作产生了影响,如果此时在方法中重新创建了一个对象,让形参指向它,此时形参指向的地址和实参指向的地址就不一样
比如,在personCrossTest()方法中添加person=new Person();
public static void PersonCrossTest(Person person){
System.out.println("传入的person的name:"+person.getName());
person=new Person();
person.setName("我是张小龙");
System.out.println("方法内重新赋值后的name:"+person.getName());
}
public static void main(String[] args) {
Person p=new Person();
p.setName("我是马化腾");
p.setAge(45);
PersonCrossTest(p);
System.out.println("方法执行后的name:"+p.getName());
}
传入的person的name:我是马化腾
方法内重新赋值后的name:我是张小龙
方法执行后的name:我是马化腾
因此实参在自己的副本中指向的是Person对象,进入另一个方法实参将地址传递给形参后,形参在自己的副本中指向的也是Person对象,当在方法中形参指向一个新建的对象的时候,此时形参在自己的副本中指向的就是另一个对象,而实参在自己的副本中指向还是person对象
- 图片右侧为xo3333,因此可以知道java中所有的参数传递不管是基本类型还是引用类型,都是值传递,在传递过程中,如果是对基本类型进行操作,原始的值和副本存放的是相同的值,但是不同的栈帧中,不会影响原始的值,如果是对引用类型进行操作,形参和实参都会存放相同的地址值,但是在不同的栈帧中,如果在方法中形参在副本中没有重新赋值指向新对象,则形参和实参在各自栈帧中指向的是堆中同一个对象,如果形参在副本中重新赋值指向新对象,则形参对应的是自己副本中新创建的对象,实参对应的是原始的内容,对形参进行更改不会影响实参的内容