1+ ---
2+ category : Java
3+ tag :
4+ - Java基础
5+ ---
6+
7+
8+
19# 为什么 Java 中只有值传递?
210
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 中只有值传递。
441
5- ** 按值调用(call by value) ** 表示方法接收的是调用者提供的值, ** 按引用调用(call by reference) ** 表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。它用来描述各种程序设计语言(不只是 Java)中方法参数传递方式。
42+ ## 为什么 Java 只有值传递?
643
7- ** Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。 **
44+ ** 为什么说 Java 只有值传递呢? ** 不需要太多废话,我通过 3 个例子来给大家证明。
845
9- ** 下面通过 3 个例子来给大家说明 **
46+ ### 案例1:传递基本类型参数
1047
11- > ** example 1 **
48+ 代码:
1249
1350``` java
1451public static void main(String [] args) {
1552 int num1 = 10 ;
1653 int num2 = 20 ;
17-
1854 swap(num1, num2);
19-
2055 System . out. println(" num1 = " + num1);
2156 System . out. println(" num2 = " + num2);
2257}
@@ -25,13 +60,12 @@ public static void swap(int a, int b) {
2560 int temp = a;
2661 a = b;
2762 b = temp;
28-
2963 System . out. println(" a = " + a);
3064 System . out. println(" b = " + b);
3165}
3266```
3367
34- ** 结果: **
68+ 输出:
3569
3670```
3771a = 20
@@ -40,103 +74,101 @@ num1 = 10
4074num2 = 20
4175```
4276
43- ** 解析:**
77+ 解析:
78+
79+ 在 ` swap() ` 方法中,` a ` 、` b ` 的值进行交换,并不会影响到 ` num1 ` 、` num2 ` 。因为,` a ` 、` b ` 的值,只是从 ` num1 ` 、` num2 ` 的复制过来的。也就是说,a、b 相当于 ` num1 ` 、` num2 ` 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
4480
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 )
4682
47- 在 swap 方法中,a、b 的值进行交换,并不会影响到 num1、num2。因为,a、b 中的值,只是从 num1、num2 的复制过来的。也就是说,a、b 相当于 num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身 。
83+ 通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看案例2 。
4884
49- ** 通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看 example2. **
85+ ### 案例2:传递引用类型参数1
5086
51- > ** example 2 **
87+ 代码:
5288
5389``` java
5490 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 ]);
5995 }
6096
6197 public static void change(int [] array) {
62- // 将数组的第一个元素变为0
63- array[0 ] = 0 ;
98+ // 将数组的第一个元素变为0
99+ array[0 ] = 0 ;
64100 }
65101```
66102
67- ** 结果: **
103+ 输出:
68104
69105```
701061
711070
72108```
73109
74- ** 解析:**
110+ 解析:
75111
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 )
77113
78- array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的是同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上 。
114+ 看了这个案例很多人肯定觉得 Java 对引用类型的参数采用的是引用传递 。
79115
80- ** 通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。 **
116+ 实际上,并不是的,这里传递的还是值,不过,这个值是实参的地址罢了!
81117
82- ** 很多程序设计语言(特别是,C++和 Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的作者)认为 Java 程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。 **
118+ 也就是说 ` change ` 方法的参数拷贝的是 ` arr ` (实参)的地址,因此,它和 ` arr ` 指向的是同一个数组对象。这也就说明了为什么方法内部对形参的修改会影响到实参。
83119
84- > ** example 3**
120+ 为了更强有力地反驳 Java 对引用类型的参数采用的不是引用传递,我们再来看下面这个案例!
121+
122+ ### 案例3 :传递引用类型参数2
85123
86124``` java
87- public class Test {
125+ public class Person {
126+ private String name;
127+ // 省略构造函数、Getter&Setter方法
128+ }
88129
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+ }
97137
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());
105144}
106145```
107146
108- ** 结果: **
147+ 输出:
109148
110149```
111- x :小李
112- y :小张
113- s1 :小张
114- s2 :小李
150+ person1 :小李
151+ person2 :小张
152+ xiaoZhang :小张
153+ xiaoLi :小李
115154```
116155
117- ** 解析:**
118-
119- 交换之前:
120-
121- ![ ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/88729818.jpg )
122-
123- 交换之后:
156+ 解析:
124157
125- ![ ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/34384414.jpg )
158+ 怎么回事???两个引用类型的形参互换并没有影响实参啊!
126159
127- 通过上面两张图可以很清晰的看出: ** 方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap 方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝 **
160+ ` swap ` 方法的参数 ` person1 ` 和 ` person2 ` 只是拷贝的实参 ` xiaoZhang ` 和 ` xiaoLi ` 的地址。因此, ` person1 ` 和 ` person2 ` 的互换只是拷贝的两个地址的互换罢了,并不会影响到实参 ` xiaoZhang ` 和 ` xiaoLi ` 。
128161
129- > ** 总结 **
162+ ![ 引用数据类型参数2 ] ( ./images/java-value-passing-03.png )
130163
131- Java 程序设计语言对对象采用的不是引用调用,实际上,对象引用是按
132- 值传递的。
164+ ## 总结
133165
134- 下面再总结一下 Java 中方法参数的使用情况 :
166+ Java 中将实参传递给方法(或函数)的方式是 ** 值传递 ** :
135167
136- - 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
137- - 一个方法可以改变一个对象参数的状态。
138- - 一个方法不能让对象参数引用一个新的对象。
168+ - 如果参数是基本类型的话,很简单,传递的就是基本类型的字面量值的拷贝,会创建副本。
169+ - 如果参数是引用类型,传递的就是实参所引用的对象在堆中地址值的拷贝,同样也会创建副本。
139170
140- ** 参考: **
171+ ## 参考
141172
142- 《Java 核心技术卷 Ⅰ》基础知识第十版第四章 4.5 小节
173+ - 《Java 核心技术卷 Ⅰ》基础知识第十版第四章 4.5 小节
174+ - [ Java 到底是值传递还是引用传递? - Hollis的回答 - 知乎] ( https://www.zhihu.com/question/31203609/answer/576030121 )
0 commit comments