Java是值传递,Java是值传递,Java是值传递。重要的事情说三遍。
介绍值传递、引用传递的概念之前先来回顾下形参和实参的概念。
形参:方法被调用时需要传递进来的参数,如func(int a)中的a,它只有在func被调用期间才有意义,也就是会被分配内存空间,在方法func执行完成后,a就会被销毁释放空间,也就是不存在了,结合Java运行时数据区来说就是局部变量a在方法func对应的栈帧中,随着方法被调用入栈,执行完成出栈。
实参:方法被调用时传入的实际值,它在方法被调用前就已经被初始化并且在方法被调用时传入。
再来说下值传递、引用传递的概念。
值传递:也叫按值调用(call by value),在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容。值传递传递的是真实内容的一个副本,对副本的操作不影响原内容,也就是形参怎么变化,不会影响实参对应的内容。重点就是这个副本副本副本。
引用传递:“引用”也就是指向真实内容的地址值,在方法调用时,实参的地址通过方法调用被传递给相应的形参,在方法体内,形参和实参指向同一块内存地址,对形参的操作会影响真实内容。
一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用时所对应的变量值。
Java中只有值传递。也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给他的任何参数变量的内容。
理解值传递时方法内部接收到的内容是实参值的一个副本是理解Java值传递的根本。以下面三种类型来举例:
1,参数类型为基本数据类型时:
代码如下:
public class PrimitiveTest {
public static void main(String[] args) {
int num = 10;
System.out.println(“main方法定义变量后num的值:” + num);
multiplyNum(num);
System.out.println(“main方法调用multiplyNum的值:” + num);
}
private static void multiplyNum(int arg) {
System.out.println(“multiplyNum方法开始获取到的参数值:” + arg);
arg = arg * 2;
System.out.println(“multiplyNum方法乘以2之后的值:” + arg);
}
}
打印结果为:
main方法定义变量后num的值:10
multiplyNum方法开始获取到的参数值:10
multiplyNum方法乘以2之后的值:20
main方法调用multiplyNum的值:10
画个简单的图:
方法multiplyNum中的变量arg是main方法num变量的一个副本,在multiplyNum中操作arg不会影响main方法中的num变量。
2,参数为引用类型时:
引用个别人的图
代码为:
public static void main(String[] args) {
Employee harry = new Employee(2000, “harry”);
System.out.println(“调用涨工资之前harry的工资:” + harry.getSalary());
raiseSalary(harry);
System.out.println(“调用涨工资之后harry的工资:” + harry.getSalary());
}
public static void raiseSalary(Employee x) {
x.setSalary(x.getSalary() * 3);
}
调用raiseSalary方法时,raiseSalary接收到的x是main方法中harry的副本,两者同时指向堆空间中的Employee对象,raiseSalary方法执行后,薪资salary变为了原来的3倍,所以harry指向的对象的薪资就是6000了。
3,参数类型为String、Integer等不可变的引用类型时:
public static void main(String[] args) {
String name = “paul”;
System.out.println(name);
upperString(name);
System.out.println(name);
}
private static void upperString(String str) {
str = str.toUpperCase();
}
输出结果为:
paul
paul
调用方法upperString时,方法upperString接收到的是main方法中name变量的副本,二者同时指向常量池中的paul对象,不过Java中String是不可变的,str.toUpperCase()实际是新开辟了一块空间存放PAUL对象,然后upperString中的str指向了新的对象,str在方法upperString执行结束就消失了,main中的name仍然指向paul对象。