1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

[feat]add 为什么 Java 中只有值传递?

This commit is contained in:
guide 2021-11-24 15:36:41 +08:00
parent 878067bfa6
commit b3cf01f104
10 changed files with 103 additions and 67 deletions

View File

@ -44,6 +44,7 @@
**重要知识点详解:**
- [为什么 Java 中只有值传递?](docs/java/basis/why-there-only-value-passing-in-java.md)
- [什么是反射机制?反射机制的应用场景有哪些?](docs/java/basis/反射机制详解.md)
- [代理模式详解:静态代理+JDK/CGLIB 动态代理实战](docs/java/basis/代理模式详解.md)
- [常见的 IO 模型有哪些Java 中的 BIO、NIO、AIO 有啥区别?](docs/java/basis/java基础知识总结)

View File

@ -148,7 +148,7 @@ module.exports = config({
"java基础知识总结",
{
title: "重要知识点",
children: ["反射机制详解", "代理模式详解", "io模型详解"],
children: ["why-there-only-value-passing-in-java", "反射机制详解", "代理模式详解", "io模型详解"],
},],
},
{

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2021-11-24T03:52:30.002Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="bvsmkjngkIq_COWrT3rC" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7VfbjpswEP0aS+1DKi6BhEcgJG2lqqtut7vtS+WAAygOzhrn1q/vGEy4ZndVKbsvVR7iOR57xnPOgEGmvzkuON4mX1hEKDK06IjMGTIMXdds+JPIqUQM3VZIzNNIedXAbfqHKFBT6C6NSN5yFIxRkW7bYMiyjISihWHO2aHttmK0HXWLY9IDbkNM++h9GomkRKfGpMY/kjROqsi67ZQzG1w5q5PkCY7YoQGZATJ9zpgoR5ujT6isXlWXct38wuw5MU4y8ZIFEfua5d/uKLF/Z/78093NXbgYqV32mO7UgVFgoamFXB8FY+T5yJkUyBx5MJgXiPsZ7/FCEoMMm0Jwb8nlAcVJVc1+3MlTeSuWiVFecOqCg25vQRhePQ+juPiXoWbIgZhTNNVk/CpUGQYycEzkWqqS50CGIEe5QSI2FAAdhrngbE18RhkHJGMZkYmklHYgTNM4AzOE8hHAvT3hIgXiXTWxSaNIhvEOSSrI7RaHMuYBdA4YZ7ssIrKyGlgULwn1cLiOC7wbHKqghK3byq5ckGF6xa/YU2CRMhl7ZMpt8zURYaLO1ae74g7yJscGpOhfELYhgp/ARc0aEyXFQ61kfaqwpKlihWHVPPF5q1pfMFASG5bbd5NnHv/z6zG4If6PKIzu3fVIt/t6UxKSVXmJiAwTRNRTULbbGO+kTNwAOYFS7NR4X+0O2ZYBlOC6Oqr51C8w3pBYTHGeV3KrulrvEtbUHNC8WhE7DHsChZlo4iw17ZxVj88B1i9S7KgWUY9bc4Bwe4Bw62qET69HuP6fcCBcaxEOHWb1KZ+8JuVy/QXK8y3O/p3yZcE3vCdcY5Dvcvc345vokUUmQ3w79sTE9lUaXH/zDjfGV+pw/Azdb9zer0R3p72H3uCv291Db/BO8UkWufLqLW9XsrRp+RTEXPThBg1QEn56UFeqwvgpjQ9WZc6OzcnZqbKOqXhojBurwKoXSeO8JoJbvsqYcZGwmGWYBjWqyJN+T1MHJ2c7HpIXPBGhAjERz/ZSXwwNtq0nyOaEwgVy3054SAEqwg1LiwZVWhu3tda9BJbHVGua3xmdbbqSHXf2KavQ2wekgU8Nt610yC9na3aytawnszK7WbXcYVDGrxvjXP6hXgGz/nAr3evvXzP4Cw==</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2021-11-24T03:33:09.587Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="pc9xWnTv6V551KKFcp_6" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7Vlbc9o4FP41mtl9oGNZvj5iAnSzvWQ2k2mzLzvCFrY3xmJlEaC/fiVbvjuEUmiTTuEB69P9fN85OjIATVa7OcPr6D0NSAJ0LdgBdAV0HULNEj8S2ReIDi2FhCwOVKsauI2/EAVqCt3EAclaDTmlCY/XbdCnaUp83sIwY3TbbrakSXvWNQ5JD7j1cdJHP8UBjwrU0e0af0viMCpnhpZb1Kxw2VjtJItwQLcNCE0BmjBKefG02k1IIq1X2qXoN3uitloYIyk/psPi72gzG7OHT/q7PY4/hNtr+HGkF6M84mSjNqwWy/elBRjdpAGRg0CAvG0Uc3K7xr6s3QrSBRbxVaKqwwRnmXquNpwXHgj3I1VYxkkyoQll+QwowMRZ+rIRZ/SBNGos3yGLpajpb1bt/5EwTnYNSG1+TuiKcLYXTVQtKiWlpDjSNQVsa2J1S2FRg1RkKxArMYXV4LW9xYMy+deYv29uEgj9qSJlPKIhTXEyrVGvJkQTpbrNO0rXysD/Es73ypnwhtM2ScKGbP9Z9c8L97LwxiyLV7tm5dVelZ4kIaMb5pMD+zSU52IWEn6gHVRuJY1wkFNGEszjx7aTDtGTdx0zhveNBmsapzxrjHwjgVoqut2WimW2PeuZ5iNodpRRLKDWSbWT06VjDHiulXDpXTRfXK0p678NLStGWa6KsWigo/WurhRPofzFcqm/gakJvCsw1uWDMwOO/ns5vFhtMUPR/gfECwIDk9hD8cK1bISt88QLRXpJqtEPFnAoWJgXixVaz9Q/ZaywXnesGEHYOYdfQrSwLhct8lgxngJ3+hJjxXJJLH8wtwhsd6EdlOrJsWIEkdmPFvb3jBb2NxOOtCHCc7I14FjHUiwsyNs8tplIaUo6tCkIJ3GYiqIveCEC9yQfsUjNx6piFQdBHuWGhNOJfKIy5pIeQ1NbVSEPuueRgKF33drunxjGgAT0S0kADmng+yf3JnECY8gBHX2BrDMd1si129Z39Dd9F3RQ3/yVq36L/QP6Mc3+ukuI9U86mf1xd3Pnz0ewb34ZIIX/TMDUAN4EiEUXIdMTD7McGV/jRzyXl97SwRbsGG+FlvDW3E17DmvITM4VczrA0eT85VTFNGIFLgJj84V6boIXJPGw/xDmeHfyhiNbqtyQmJd/8zG5yAGonHuEtK5kzyBA6LbdX0cD+aIz4P7wDO6/0e4+/MnuJ/NJdm3S+/V79nZ/zNWepMFYviOp7RngLKrCQYN5id9gLthMc0QcD5UeyvciejNdrHLHIl3Un8sXyS7mnxvPzW6iWPeShbJT96w35XfwPUL+qZjupIwD1Pd5bvBoHjjIvy7Z7CWHvTjWGaHInlWnA0mm4XRPo/Y4RXbdG+eEdHNQe+jn195rUxLSzqOk7jgXVpL5S0kvTUmjc0mpN9CFtfTsJdivTow6i0LFZyCxUn2zNU5Pv0CDqQVcE3gamNrA84BjNC5WxdDt6V7HfeuJ9OkoVZdqgUZbLQP3KdPqq/6E+5Qo1v/DFGqr/85C0/8B</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2021-11-24T07:29:28.493Z" agent="5.0 (Macintosh; Intel Mac OS X 10_16_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="YfKlEbrvOOY6jty1IFoX" version="13.4.5" type="device"><diagram id="KJUpccsywgqX94dHTPwA" name="Page-1">7Vlbd+I2EP41Pqd9IMeWfOPREMh2l223zTbZ5k3YwlYxFivErb++ki3jKw7pQki7PTygGY1u8818GtsaHC52dwwto480wLEG9GCnwVsNAMPQbfEnNftMAwxbaUJGAmVVKO7JX1gpdaVdkwCvKoac0piTZVXp0yTBPq/oEGN0WzWb0bi66hKFuKG491Hc1D6SgEeZ1gVOoX+HSRjlKxt2P+tZoNxYnWQVoYBuSyo40uCQUcqz1mI3xLH0Xu6XbNz4SO9hYwwn/JQB06doPfbY/BFM9oj8HG7fG7/0QDbLBsVrdWC1Wb7PPcDoOgmwnMTQ4GAbEY7vl8iXvVsButBFfBGr7jBGq5VqHw6cCnPM/UgJMxLHQxpTlq4AA4TdmS+NOKNzXOqxfRdPZ6KneVh1/g1mHO9KKnX4O0wXmLO9MFG9wDWzISoUe0BXyGwLYIGtdFEJVOgoJVLBFB4mL/wtGsrl7e7/6et+bD6YCXE/333++OCaH/xNz254Gwci/JRIGY9oSBMUjwrtoMBDF1JhM6F0qfz7J+Z8r3IJrTmtYiRcyPZf1PhU+EMKN1Yu3u7Knbd7JR3FYEXXzMcdUaYczxELMe9wh7KTPuhElOEYcbKppmgbOOlQjzG0LxksKUn4qjTzJ6koAgXm3KMCxbSqefWMuWvXwiJbvwiSw0FOipsuh1bS1o65TC2a7q2IKPvrmuYdvVUaE54wAHC5KzpFK5T/O4LohPygjSzNG2n9kWy4Y80FP+bzi+1mS2QDrsAWsxm2/Va2CJz+VO+M1NPZwrEqoIImVRhtVGFdiims74Mp7CZTtNoZzrmp4lR0urZ9kXx8ilAS/p+SxdWc399GnqPlpOxfKClbYXe+GXaot8Gegq1r8iY5DWLhQV7FsYpEQhNcg02pUEzCRIi+wAUL/UDiQUT966mOBQmClE3aAqfGMKKTcAmPqaujKmox+ucJARM0YqBJzGZLCIBLhYBxPAZWS5T889Rf8XWAU8jTyaaswQml4MiWulr+BxZ2A7Mt/10wheKJ7yzgQ9u9sWrwA3DTQgImbCGBg+XZ72b3P3c3tx4T/KureOO6ZXyXR8/OHUvMVjQBadEwuNU80Fo0XJk0sCFow2kjjb7tQHQm0nBqUXD1Qt75PsgCvvlCvmvbZy/ks4w0nsnIK5fxr5ORbq2Ec1sy0nnNjDz+MuWCFdyENPVvh5tfqaCrhYLdEgmvXMrlRUDXC3GcBJ78siAkXzqXZE++iPGm+gwcjHeEfym1S6OEVAySwmHMs1fK5ctAeCKxl9C2OtL+ZdVis7yrsUd2TGXUURbW2arOQpkbGvO8tFoFLytW64/BVsX8m4vVx99B4gS/7Z5+ZZ8eJpt36MN80cvTu5QZ8g6zNG+ojUxtMNT6jrrVBqIxTjXee7RBd/IbXpnunmdUwxaMmjJi49WIKS/QvljT1QQ4Yv18qWwZsYM+1Lzm+9K38Y4kRlMcD5A/D1N9ffHSKxNbySXyHaS/dE4ukoHKtXtQr5P5ER5uSffj1AyNxrN2Czu7LQlrvPyeFmLxHTSL1+JzMhz9DQ==</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -1,22 +1,57 @@
---
category: Java
tag:
- Java基础
---
# 为什么 Java 中只有值传递?
首先,我们回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。
开始之前,我们先来搞懂下面这两个概念:
**按值调用(call by value)** 表示方法接收的是调用者提供的值,**按引用调用call by reference)** 表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。它用来描述各种程序设计语言(不只是 Java中方法参数传递方式。
- 形参&实参
- 值传递&引用传递
**Java 程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。**
## 形参&实参
**下面通过 3 个例子来给大家说明**
方法的定义可能会用到 **参数**(有参的方法),参数在程序语言中分为:
> **example 1**
- **实参(实际参数)** :用于传递给函数/方法的参数,必须有确定的值。
- **形参(形式参数)** :用于定义函数/方法,接收实参,不需要有确定的值。
```java
String hello = "Hello!";
// hello 为实参
sayHello(hello);
// str 为形参
void sayHello(String str) {
System.out.println(str);
}
```
## 值传递&引用传递
程序设计语言将实参传递给方法(或函数)的方式分为两种:
- **值传递** :方法接收的是实参值的拷贝,会创建副本。
- **引用传递** :方法接收的直接是实参所引用的对象在堆中的地址,不会创建副本,对形参的修改将影响到实参。
很多程序设计语言(比如 C++、 Pascal )提供了两种参数传递的方式,不过,在 Java 中只有值传递。
## 为什么 Java 只有值传递?
**为什么说 Java 只有值传递呢?** 不需要太多废话,我通过 3 个例子来给大家证明。
### 案例1传递基本类型参数
代码:
```java
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
@ -25,13 +60,12 @@ public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
```
**结果:**
输出:
```
a = 20
@ -40,103 +74,101 @@ num1 = 10
num2 = 20
```
**解析:**
解析:
![example 1 ](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/22191348.jpg)
`swap()` 方法中,`a``b` 的值进行交换,并不会影响到 `num1``num2`。因为,`a``b` 的值,只是从 `num1``num2` 的复制过来的。也就是说a、b 相当于 `num1``num2` 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
在 swap 方法中a、b 的值进行交换,并不会影响到 num1、num2。因为a、b 中的值,只是从 num1、num2 的复制过来的。也就是说a、b 相当于 num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
![基本数据类型参数](./images/java-value-passing-01.png)
**通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看 example2.**
通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看案例2。
> **example 2**
### 案例2传递引用类型参数1
代码:
```java
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
System.out.println(arr[0]);
change(arr);
System.out.println(arr[0]);
int[] arr = { 1, 2, 3, 4, 5 };
System.out.println(arr[0]);
change(arr);
System.out.println(arr[0]);
}
public static void change(int[] array) {
// 将数组的第一个元素变为0
array[0] = 0;
// 将数组的第一个元素变为0
array[0] = 0;
}
```
**结果:**
输出:
```
1
0
```
**解析:**
解析:
![example 2](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/3825204.jpg)
![引用数据类型参数1](./images/java-value-passing-02.png)
array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的是同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上
看了这个案例很多人肯定觉得 Java 对引用类型的参数采用的是引用传递
**通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。**
实际上,并不是的,这里传递的还是值,不过,这个值是实参的地址罢了!
**很多程序设计语言特别是C++和 Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的作者)认为 Java 程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。**
也就是说 `change` 方法的参数拷贝的是 `arr` (实参)的地址,因此,它和 `arr` 指向的是同一个数组对象。这也就说明了为什么方法内部对形参的修改会影响到实参。
> **example 3**
为了更强有力地反驳 Java 对引用类型的参数采用的不是引用传递,我们再来看下面这个案例!
### 案例3 传递引用类型参数2
```java
public class Test {
public class Person {
private String name;
// 省略构造函数、Getter&Setter方法
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s1 = new Student("小张");
Student s2 = new Student("小李");
Test.swap(s1, s2);
System.out.println("s1:" + s1.getName());
System.out.println("s2:" + s2.getName());
}
public static void main(String[] args) {
Person xiaoZhang = new Person("小张");
Person xiaoLi = new Person("小李");
swap(xiaoZhang, xiaoLi);
System.out.println("xiaoZhang:" + xiaoZhang.getName());
System.out.println("xiaoLi:" + xiaoLi.getName());
}
public static void swap(Student x, Student y) {
Student temp = x;
x = y;
y = temp;
System.out.println("x:" + x.getName());
System.out.println("y:" + y.getName());
}
public static void swap(Person person1, Person person2) {
Person temp = person1;
person1 = person2;
person2 = temp;
System.out.println("person1:" + person1.getName());
System.out.println("person2:" + person2.getName());
}
```
**结果:**
输出:
```
x:小李
y:小张
s1:小张
s2:小李
person1:小李
person2:小张
xiaoZhang:小张
xiaoLi:小李
```
**解析:**
解析:
交换之前:
怎么回事???两个引用类型的形参互换并没有影响实参啊!
![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/88729818.jpg)
`swap` 方法的参数 `person1``person2` 只是拷贝的实参 `xiaoZhang``xiaoLi` 的地址。因此, `person1``person2` 的互换只是拷贝的两个地址的互换罢了,并不会影响到实参 `xiaoZhang``xiaoLi`
交换之后:
![引用数据类型参数2](./images/java-value-passing-03.png)
![](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-27/34384414.jpg)
## 总结
通过上面两张图可以很清晰的看出: **方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap 方法的参数 x 和 y 被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝**
Java 中将实参传递给方法(或函数)的方式是 **值传递**
> **总结**
- 如果参数是基本类型的话,很简单,传递的就是基本类型的字面量值的拷贝,会创建副本。
- 如果参数是引用类型,传递的就是实参所引用的对象在堆中地址值的拷贝,同样也会创建副本。
Java 程序设计语言对对象采用的不是引用调用,实际上,对象引用是按
值传递的。
## 参考
下面再总结一下 Java 中方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
- 一个方法可以改变一个对象参数的状态。
- 一个方法不能让对象参数引用一个新的对象。
**参考:**
《Java 核心技术卷 Ⅰ》基础知识第十版第四章 4.5 小节
- 《Java 核心技术卷 Ⅰ》基础知识第十版第四章 4.5 小节
- [Java 到底是值传递还是引用传递? - Hollis的回答 - 知乎](https://www.zhihu.com/question/31203609/answer/576030121)