介绍
总是搞不清楚参数传来传去到底是否会改变原有参数的值?本文将通过一个例子彻底讲清java的值传递到底是怎么玩的。
在程序设计语言中参数传递给方法有两种方式:
- 按值调用:表示方法接收的是调用者提供的值
- 按引用调用:表示方法接收的是调用者提供的变量地址,(这个变量地址可以抽象为数据实际存放的位置)
而在Java中总是采用按值调用的方式进行参数传递。也就是说方法接收到的参数是调用者提供参数值的一个拷贝,现在不清楚没关系,我们接着往下看。
分析
要弄清楚参数传来传去到底是否会改变原有参数的值,在这里我主要分为两种种情况来分别探讨
- 基本数据类型参数传递
- 对象引用作为参数传递
基本数据类型的参数传递
Java语言使用的是按值调用方式进行参数传递,因此在方法中是不能修改传递给它的任何参数的值。例如:将percent增加3倍的一段程序
public static void main(String[] args){
/*
* Test 1: Methods can't modify numeric parameters
*/
System.out.println("Testing tripleValue:");
double percent = 10;
System.out.println("Before: percent=" + percent);
tripleValue(percent);
System.out.println("After: percent=" + percent);
}
public static void tripleValue(double x) // doesn't work
{
x = 3 * x;
System.out.println("End of method: x=" + x);
}
这里percent在调用tripleValue方法前后的变化如下:
可以看到,调用了tripleValue方法后。percent的值并没有发生改变,只有tripleValue方法中的x增加了3倍。
这是因为这一段代码的执行流程是这样的:
- 调用
tripleValue(percent)方法时,x会被初始化为percent值的一个拷贝值,这个拷贝值也等于10 - 在
tripleValue(double x)方法中,x被乘以3后等于30了,但是percent的值并没有发生改变
- 方法结束后,参数变量x就不再被使用了
对象引用作为参数传递
但是对象引用作为参数就不同了,可以看下面一段程序,将一个雇员的薪资提高200%操作:
class Employee // simplified Employee class
{
private String name;
private double salary;
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
public static void main(String[] args)
{
/*
* Test 2: Methods can change the state of object parameters
*/
System.out.println("\nTesting tripleSalary:");
Employee harry = new Employee("Harry", 50000);
System.out.println("Before: salary=" + harry.getSalary());
tripleSalary(harry);
System.out.println("After: salary=" + harry.getSalary());
}
public static void tripleSalary(Employee x) // works
{
x.raiseSalary(200);
System.out.println("End of method: salary=" + x.getSalary());
}
这段代码输出结果为:
此时具体执行过程为:
- 调用
tripleSalary(harry)方法时,x被初始化为harry值的拷贝,注意这里harry值是Employee对象的引用,也就是说harry值的拷贝是当前对象的地址值,此时x和harry同时引用了同一个Employee对象。 - tripleSalary方法中
x.raiseSalary(200)的这个操作实际上是调用的同一个Employee对象方法。 - 方法结束后x不再使用,但由于x和harry引用的同一个对象,因此x修改被保留了下来。
看到这里,如果你了解C++的话,你可能困惑这不就是C++中的按引用传递的方式吗?难道Java针对对象使用的也是按引用传递的方式?其实不然,最后在通过一个程序片段彻底理解Java的按值传递方式。下面这段代码是用于交换两个雇员对象
public class Test{
public static void swap(Employee x, Employee y)
{
Employee temp = x;
x = y;
y = temp;
System.out.println("End of method: x=" + x.getName());
System.out.println("End of method: y=" + y.getName());
}
public static void main(String[] args)
{
/*
* Test 3: Methods can't attach new objects to object parameters
*/
System.out.println("\nTesting swap:");
Employee a = new Employee("Alice", 70000);
Employee b = new Employee("Bob", 60000);
System.out.println("Before: a=" + a.getName());
System.out.println("Before: b=" + b.getName());
swap(a, b);
System.out.println("After: a=" + a.getName());
System.out.println("After: b=" + b.getName());
}
}
class Employee // simplified Employee class
{
private String name;
private double salary;
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
如果Java对象传递的方式是按引用传递那么swap方法出来后的结果会是a和b交换引用,但实际结果却是这样的:
从结果可以看到,只有方法内的x和y交换了,实际的a和b并没有交换。也就是说swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝
总结
- Java是按值传递的
- 基本数据类型在方法中的计算结果不会被保留,需要设计额外的结果保留方式。(如使用成员变量保存结果等)
- 对象引用不能在方法中进行交换,方法中交换的是对象引用的拷贝。