传值还是传引用
Java语言设计总是按值调用
按值调用:方法接收的是调用者提供的值
按引用调用:方法接收的是调用者提供的变量地址
一个方法可以修改传递引用所对应的变量值,但是不能修改传递值所对应的变量值。
基本数据类型参数 #
一个方法不能修改一个基本数据类型的参数(数值类型或布尔值)。
参考如下代码:
1@SuppressWarnings("all")
2class ByValue {
3 static void tripleValue(Integer x) {
4 x = 3 * x;
5 System.out.println("x = " + x);
6 }
7
8 public static void main(String[] args) {
9 int y = 10;
10 // Integer y = Integer.valueOf(10);
11 y = 3 * y;
12 tripleValue(y);
13 System.out.println("y = " + y);
14 }
15}
16
17/* output:
18 * x = 30
19 * y = 10
20 */// :~
方法执行时,x
被初始化为y
值的一个拷贝,方法执行后 ,x
值变为30,y
仍为10,之后x
不再使用。
对象引用 #
(引用的)对象可变 #
一个方法可以改变一个对象参数的状态
参考如下代码:
假如有一个这样的雇员类:
1public class Employee {
2 private String name;
3 private int salary;
4
5 public Employee(String name, int salary) {
6 this.name = name;
7 this.salary = salary;
8 }
9
10 public String getName() {
11 return name;
12 }
13
14 public int getSalary() {
15 return salary;
16 }
17
18 public void raiseSalary(int multiple) {
19 this.salary = salary * multiple;
20 }
21
22 @Override
23 public boolean equals(Object o) {
24 if (this == o)
25 return true;
26 if (o == null || getClass() != o.getClass())
27 return false;
28
29 Employee employee2 = (Employee) o;
30
31 return Objects.equals(name, employee2.name)
32 && salary == employee2.salary;
33 }
34
35 @Override
36 public int hashCode() {
37 int result = name != null ? name.hashCode() : 0;
38 result = 31 * result + salary;
39 return result;
40 }
41}
我们要给雇员涨薪水了:
1public class ByRef {
2
3 static void raiseSalary(Employee o) {
4 o.raiseSalary(3);
5 System.out.println("after salary of o = " + o.getSalary());
6 }
7
8 static void swap(Employee j, Employee k) {
9 System.out.println("before swap j.name = " + j.getName());
10 System.out.println("before swap k.name = " + k.getName());
11 Employee temp = j;
12 j = k;
13 k = temp;
14 System.out.println("after swap j.name = " + j.getName());
15 System.out.println("after swap k.name = " + k.getName());
16 }
17
18 public static void main(String[] args) {
19 Employee a = new Employee("ali", 1200);
20 System.out.println("before salary = " + a.getSalary());
21 raiseSalary(a);
22 System.out.println("after salary of a = " + a.getSalary());
23 Employee b = new Employee("bad", 1300);
24 swap(a, b);
25 System.out.println("after swap a.name = " + a.getName());
26 System.out.println("after swap b.name = " + b.getName());
27 }
28}
29
30
31/*
32 * before salary = 1200
33 * after salary of o = 3600
34 * after salary of a = 3600
35 */// :~
方法执行时,o
被初始化为a
值的拷贝,其中a
的值为对象的引用;raiseSalary
方法作用与o
和a
同时引用的那个对象;方法结束后,o
对象不再使用,结果是a
对象的薪水涨至原来3倍。
也就是说,当对象引用作为参数时,可以对原对象的属性(域)进行修改。
既然如此,那是否可以将 基本类型参数转化为其包装类型呢,这样就可以实现小节1中的期望了:
1// doesn't work
2class ByValue {
3 static void tripleValue(Integer x) {
4 x = 3 * x;
5 System.out.println("x = " + x);
6 }
7
8 public static void main(String[] args) {
9 Integer y = Integer.valueOf(10);
10 tripleValue(y);
11 System.out.println("y = " + y);
12 }
13}
14/* output:
15x = 30
16y = 10
17*///:~
非常遗憾,失败了。这是因为基础数据类型的包装器类型是final
的,其value
域(包装类型的值)也是final
的。
当使用
Integer y = 10;
初始化y
时,Java会将y
自动拆箱为int;同样地,
x = 3 * x;
对Integer类型作操作符运算也会拆箱。
引用不可变 #
一个方法不能让对象参数引用一个新的对象
不妨再看看
上述示例代码中的swap()
方法:
swap()
方法用于交换两个对象的引用。我们得到如下输出:
1/* output:
2before swap j.name = ali
3before swap k.name = bad
4after swap j.name = bad
5after swap k.name = ali
6after swap a.name = ali
7after swap b.name = bad
8*///:~
方法执行时,j
和k
分别是a
和b
值的拷贝,也就是说j
和k
分别是a
和b
的对象引用,swap
方法交换了这两个拷贝,但是方法结束后,j
和k
不再使用,而a
和b
还是指向方法调用前所指向的对象。
也就是说,对于对象参数,对象参数的引用也是按值传递的。
如何理解这句话?
引用数据类型的参数传递实际上是对象的一个引用(按引用传递)
被传递的这个引用本身是值传递性质(引用无法被方法更改)