diff --git a/README.md b/README.md index 19a43cc9..6664252e 100755 --- a/README.md +++ b/README.md @@ -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基础知识总结) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 0dcb30ec..7992331e 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -148,7 +148,7 @@ module.exports = config({ "java基础知识总结", { title: "重要知识点", - children: ["反射机制详解", "代理模式详解", "io模型详解"], + children: ["why-there-only-value-passing-in-java", "反射机制详解", "代理模式详解", "io模型详解"], },], }, { diff --git a/docs/java/basis/images/java-value-passing-01.drawio b/docs/java/basis/images/java-value-passing-01.drawio new file mode 100644 index 00000000..75e85435 --- /dev/null +++ b/docs/java/basis/images/java-value-passing-01.drawio @@ -0,0 +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== \ No newline at end of file diff --git a/docs/java/basis/images/java-value-passing-01.png b/docs/java/basis/images/java-value-passing-01.png new file mode 100644 index 00000000..2bfd5902 Binary files /dev/null and b/docs/java/basis/images/java-value-passing-01.png differ diff --git a/docs/java/basis/images/java-value-passing-02.drawio b/docs/java/basis/images/java-value-passing-02.drawio new file mode 100644 index 00000000..2ebb0674 --- /dev/null +++ b/docs/java/basis/images/java-value-passing-02.drawio @@ -0,0 +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 \ No newline at end of file diff --git a/docs/java/basis/images/java-value-passing-02.png b/docs/java/basis/images/java-value-passing-02.png new file mode 100644 index 00000000..22a59134 Binary files /dev/null and b/docs/java/basis/images/java-value-passing-02.png differ diff --git a/docs/java/basis/images/java-value-passing-03.drawio b/docs/java/basis/images/java-value-passing-03.drawio new file mode 100644 index 00000000..263a457c --- /dev/null +++ b/docs/java/basis/images/java-value-passing-03.drawio @@ -0,0 +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== \ No newline at end of file diff --git a/docs/java/basis/images/java-value-passing-03.png b/docs/java/basis/images/java-value-passing-03.png new file mode 100644 index 00000000..1aa74577 Binary files /dev/null and b/docs/java/basis/images/java-value-passing-03.png differ diff --git a/docs/java/basis/images/shallow&deep-copy.png b/docs/java/basis/images/shallow&deep-copy.png index 7b99ffbe..ef957390 100644 Binary files a/docs/java/basis/images/shallow&deep-copy.png and b/docs/java/basis/images/shallow&deep-copy.png differ diff --git a/docs/java/basis/why-there-only-value-passing-in-java.md b/docs/java/basis/why-there-only-value-passing-in-java.md index 7d415468..fabe3abb 100644 --- a/docs/java/basis/why-there-only-value-passing-in-java.md +++ b/docs/java/basis/why-there-only-value-passing-in-java.md @@ -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 小节 \ No newline at end of file +- 《Java 核心技术卷 Ⅰ》基础知识第十版第四章 4.5 小节 +- [Java 到底是值传递还是引用传递? - Hollis的回答 - 知乎](https://www.zhihu.com/question/31203609/answer/576030121) \ No newline at end of file