1
+ ---
2
+ category : Java
3
+ tag :
4
+ - Java基础
5
+ ---
6
+
7
+
8
+
1
9
# 为什么 Java 中只有值传递?
2
10
3
- 首先,我们回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。
11
+ 开始之前,我们先来搞懂下面这两个概念:
12
+
13
+ - 形参&实参
14
+ - 值传递&引用传递
15
+
16
+ ## 形参&实参
17
+
18
+ 方法的定义可能会用到 ** 参数** (有参的方法),参数在程序语言中分为:
19
+
20
+ - ** 实参(实际参数)** :用于传递给函数/方法的参数,必须有确定的值。
21
+ - ** 形参(形式参数)** :用于定义函数/方法,接收实参,不需要有确定的值。
22
+
23
+ ``` java
24
+ String hello = " Hello!" ;
25
+ // hello 为实参
26
+ sayHello(hello);
27
+ // str 为形参
28
+ void sayHello(String str) {
29
+ System . out. println(str);
30
+ }
31
+ ```
32
+
33
+ ## 值传递&引用传递
34
+
35
+ 程序设计语言将实参传递给方法(或函数)的方式分为两种:
36
+
37
+ - ** 值传递** :方法接收的是实参值的拷贝,会创建副本。
38
+ - ** 引用传递** :方法接收的直接是实参所引用的对象在堆中的地址,不会创建副本,对形参的修改将影响到实参。
39
+
40
+ 很多程序设计语言(比如 C++、 Pascal )提供了两种参数传递的方式,不过,在 Java 中只有值传递。
4
41
5
- ** 按值调用(call by value) ** 表示方法接收的是调用者提供的值, ** 按引用调用(call by reference) ** 表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式。
42
+ ## 为什么 Java 只有值传递?
6
43
7
- ** Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。 **
44
+ ** 为什么说 Java 只有值传递呢? ** 不需要太多废话,我通过 3 个例子来给大家证明。
8
45
9
- ** 下面通过 3 个例子来给大家说明 **
46
+ ### 案例1:传递基本类型参数
10
47
11
- > ** example 1 **
48
+ 代码:
12
49
13
50
``` java
14
51
public static void main(String [] args) {
15
52
int num1 = 10 ;
16
53
int num2 = 20 ;
17
-
18
54
swap(num1, num2);
19
-
20
55
System . out. println(" num1 = " + num1);
21
56
System . out. println(" num2 = " + num2);
22
57
}
@@ -25,13 +60,12 @@ public static void swap(int a, int b) {
25
60
int temp = a;
26
61
a = b;
27
62
b = temp;
28
-
29
63
System . out. println(" a = " + a);
30
64
System . out. println(" b = " + b);
31
65
}
32
66
```
33
67
34
- ** 结果: **
68
+ 输出:
35
69
36
70
```
37
71
a = 20
@@ -40,103 +74,101 @@ num1 = 10
40
74
num2 = 20
41
75
```
42
76
43
- ** 解析:**
77
+ 解析:
78
+
79
+ 在 ` swap() ` 方法中,` a ` 、` b ` 的值进行交换,并不会影响到 ` num1 ` 、` num2 ` 。因为,` a ` 、` b ` 的值,只是从 ` num1 ` 、` num2 ` 的复制过来的。也就是说,a、b 相当于 ` num1 ` 、` num2 ` 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
44
80
45
- ![ example 1 ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/22191348.jpg )
81
+ ![ 基本数据类型参数 ] ( ./images/java-value-passing-01.png )
46
82
47
- 在 swap 方法中,a、b 的值进行交换,并不会影响到 num1、num2。因为,a、b 中的值,只是从 num1、num2 的复制过来的。也就是说,a、b 相当于 num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身 。
83
+ 通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看案例2 。
48
84
49
- ** 通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看 example2. **
85
+ ### 案例2:传递引用类型参数1
50
86
51
- > ** example 2 **
87
+ 代码:
52
88
53
89
``` java
54
90
public static void main(String [] args) {
55
- int [] arr = { 1 , 2 , 3 , 4 , 5 };
56
- System . out. println(arr[0 ]);
57
- change(arr);
58
- System . out. println(arr[0 ]);
91
+ int [] arr = { 1 , 2 , 3 , 4 , 5 };
92
+ System . out. println(arr[0 ]);
93
+ change(arr);
94
+ System . out. println(arr[0 ]);
59
95
}
60
96
61
97
public static void change(int [] array) {
62
- // 将数组的第一个元素变为0
63
- array[0 ] = 0 ;
98
+ // 将数组的第一个元素变为0
99
+ array[0 ] = 0 ;
64
100
}
65
101
```
66
102
67
- ** 结果: **
103
+ 输出:
68
104
69
105
```
70
106
1
71
107
0
72
108
```
73
109
74
- ** 解析:**
110
+ 解析:
75
111
76
- ![ example 2 ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/3825204.jpg )
112
+ ![ 引用数据类型参数1 ] ( ./images/java-value-passing-02.png )
77
113
78
- array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的是同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上 。
114
+ 看了这个案例很多人肯定觉得 Java 对引用类型的参数采用的是引用传递 。
79
115
80
- ** 通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。 **
116
+ 实际上,并不是的,这里传递的还是值,不过,这个值是实参的地址罢了!
81
117
82
- ** 很多程序设计语言(特别是,C++和 Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的作者)认为 Java 程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。 **
118
+ 也就是说 ` change ` 方法的参数拷贝的是 ` arr ` (实参)的地址,因此,它和 ` arr ` 指向的是同一个数组对象。这也就说明了为什么方法内部对形参的修改会影响到实参。
83
119
84
- > ** example 3**
120
+ 为了更强有力地反驳 Java 对引用类型的参数采用的不是引用传递,我们再来看下面这个案例!
121
+
122
+ ### 案例3 :传递引用类型参数2
85
123
86
124
``` java
87
- public class Test {
125
+ public class Person {
126
+ private String name;
127
+ // 省略构造函数、Getter&Setter方法
128
+ }
88
129
89
- public static void main (String [] args ) {
90
- // TODO Auto-generated method stub
91
- Student s1 = new Student (" 小张" );
92
- Student s2 = new Student (" 小李" );
93
- Test . swap(s1, s2);
94
- System . out. println(" s1:" + s1. getName());
95
- System . out. println(" s2:" + s2. getName());
96
- }
130
+ public static void main(String [] args) {
131
+ Person xiaoZhang = new Person (" 小张" );
132
+ Person xiaoLi = new Person (" 小李" );
133
+ swap(xiaoZhang, xiaoLi);
134
+ System . out. println(" xiaoZhang:" + xiaoZhang. getName());
135
+ System . out. println(" xiaoLi:" + xiaoLi. getName());
136
+ }
97
137
98
- public static void swap (Student x , Student y ) {
99
- Student temp = x;
100
- x = y;
101
- y = temp;
102
- System . out. println(" x:" + x. getName());
103
- System . out. println(" y:" + y. getName());
104
- }
138
+ public static void swap(Person person1, Person person2) {
139
+ Person temp = person1;
140
+ person1 = person2;
141
+ person2 = temp;
142
+ System . out. println(" person1:" + person1. getName());
143
+ System . out. println(" person2:" + person2. getName());
105
144
}
106
145
```
107
146
108
- ** 结果: **
147
+ 输出:
109
148
110
149
```
111
- x :小李
112
- y :小张
113
- s1 :小张
114
- s2 :小李
150
+ person1 :小李
151
+ person2 :小张
152
+ xiaoZhang :小张
153
+ xiaoLi :小李
115
154
```
116
155
117
- ** 解析:**
118
-
119
- 交换之前:
120
-
121
- ![ ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/88729818.jpg )
122
-
123
- 交换之后:
156
+ 解析:
124
157
125
- ![ ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/34384414.jpg )
158
+ 怎么回事???两个引用类型的形参互换并没有影响实参啊!
126
159
127
- 通过上面两张图可以很清晰的看出: ** 方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap 方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝 **
160
+ ` swap ` 方法的参数 ` person1 ` 和 ` person2 ` 只是拷贝的实参 ` xiaoZhang ` 和 ` xiaoLi ` 的地址。因此, ` person1 ` 和 ` person2 ` 的互换只是拷贝的两个地址的互换罢了,并不会影响到实参 ` xiaoZhang ` 和 ` xiaoLi ` 。
128
161
129
- > ** 总结 **
162
+ ![ 引用数据类型参数2 ] ( ./images/java-value-passing-03.png )
130
163
131
- Java 程序设计语言对对象采用的不是引用调用,实际上,对象引用是按
132
- 值传递的。
164
+ ## 总结
133
165
134
- 下面再总结一下 Java 中方法参数的使用情况 :
166
+ Java 中将实参传递给方法(或函数)的方式是 ** 值传递 ** :
135
167
136
- - 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
137
- - 一个方法可以改变一个对象参数的状态。
138
- - 一个方法不能让对象参数引用一个新的对象。
168
+ - 如果参数是基本类型的话,很简单,传递的就是基本类型的字面量值的拷贝,会创建副本。
169
+ - 如果参数是引用类型,传递的就是实参所引用的对象在堆中地址值的拷贝,同样也会创建副本。
139
170
140
- ** 参考: **
171
+ ## 参考
141
172
142
- 《Java 核心技术卷 Ⅰ》基础知识第十版第四章 4.5 小节
173
+ - 《Java 核心技术卷 Ⅰ》基础知识第十版第四章 4.5 小节
174
+ - [ Java 到底是值传递还是引用传递? - Hollis的回答 - 知乎] ( https://www.zhihu.com/question/31203609/answer/576030121 )
0 commit comments