|
1 | 1 |
|
2 | 2 |
|
3 |
| -## 一 Java中的值传递和引用传递(非常重要) |
| 3 | +## 一 为什么 Java 中只有值传递? |
4 | 4 |
|
5 |
| -**首先要明确的是:“对象传递(数组、类、接口)是引用传递,原始类型数据(整型、浮点型、字符型、布尔型)传递是值传递。”** |
6 | 5 |
|
7 |
| -### 那么什么是值传递和应用传递呢? |
| 6 | +首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。**按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。** 它用来描述各种程序设计语言(不只是Java)中方法参数传递方式。 |
8 | 7 |
|
9 |
| -**值传递**是指对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。(因为值传递的时候,实际上是将实参的值复制一份给形参。) |
| 8 | +**Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。** |
10 | 9 |
|
11 |
| -**引用传递**是指对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到所有的对象上。(因为引用传递的时候,实际上是将实参的地址值复制一份给形参。) |
| 10 | +**下面通过 3 个例子来给大家说明** |
12 | 11 |
|
13 |
| -有时候面试官不是单纯问你“Java中是值传递还是引用传递”是什么啊,骚年?而是给出一个例子,然后让你写出答案,这种也常见在笔试题目中!所以,非常重要了,请看下面的例子: |
| 12 | +### example 1 |
14 | 13 |
|
15 |
| -### 值传递和应用传递实例 |
16 |
| - |
17 |
| -#### 1. 值传递 |
18 | 14 |
|
19 | 15 | ```java
|
20 | 16 | public static void main(String[] args) {
|
@@ -48,151 +44,109 @@ num2 = 20
|
48 | 44 |
|
49 | 45 | **解析:**
|
50 | 46 |
|
51 |
| -在swap方法中,a、b的值进行交换,并不会影响到num1、num2。因为,a、b中的值,只是从num1、num2的复制过来的。 |
52 |
| -也就是说,a、b相当于num1、num2的副本,副本的内容无论怎么修改,都不会影响到原件本身。 |
53 |
| - |
54 |
| -#### 2. 引用传递 |
55 |
| - |
56 |
| -```java |
57 |
| -public static void main(String[] args) { |
58 |
| - int[] arr = {1,2,3,4,5}; |
| 47 | + |
59 | 48 |
|
60 |
| - change(arr); |
| 49 | +在swap方法中,a、b的值进行交换,并不会影响到 num1、num2。因为,a、b中的值,只是从 num1、num2 的复制过来的。也就是说,a、b相当于num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。 |
61 | 50 |
|
62 |
| - System.out.println(arr[0]); |
63 |
| -} |
| 51 | +**通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,请看 example2.** |
64 | 52 |
|
65 |
| -public static void change(int[] array) { |
66 |
| -//将数组的第一个元素变为0 |
67 |
| - array[0] = 0; |
68 |
| -} |
69 |
| -``` |
70 | 53 |
|
71 |
| -**结果:** |
72 |
| - |
73 |
| -``` |
74 |
| -1 |
75 |
| -0 |
76 |
| -``` |
77 |
| - |
78 |
| -**解析:** |
79 |
| - |
80 |
| -无论是主函数,还是change方法,操作的都是同一个地址值对应的数组。 。因此,外部对引用对象的改变会反映到所有的对象上。 |
81 |
| - |
82 |
| -### 一些特殊的例子 |
83 |
| - |
84 |
| -#### 1. StringBuffer类型传递 |
| 54 | +### example 2 |
85 | 55 |
|
86 | 56 | ```java
|
87 |
| - // 测试引用传递:StringBuffer |
88 |
| - @org.junit.Test |
89 |
| - public void method1() { |
90 |
| - StringBuffer str = new StringBuffer("公众号:Java面试通关手册"); |
91 |
| - System.out.println(str); |
92 |
| - change1(str); |
93 |
| - System.out.println(str); |
| 57 | + public static void main(String[] args) { |
| 58 | + int[] arr = { 1, 2, 3, 4, 5 }; |
| 59 | + System.out.println(arr[0]); |
| 60 | + change(arr); |
| 61 | + System.out.println(arr[0]); |
94 | 62 | }
|
95 | 63 |
|
96 |
| - public static void change1(StringBuffer str) { |
97 |
| - str = new StringBuffer("abc");//输出:“公众号:Java面试通关手册” |
98 |
| - //str.append("欢迎大家关注");//输出:公众号:Java面试通关手册欢迎大家关注 |
99 |
| - //str.insert(3, "(编程)");//输出:公众号(编程):Java面试通关手册 |
100 |
| - |
| 64 | + public static void change(int[] array) { |
| 65 | + // 将数组的第一个元素变为0 |
| 66 | + array[0] = 0; |
101 | 67 | }
|
102 | 68 | ```
|
103 | 69 |
|
104 | 70 | **结果:**
|
105 | 71 |
|
106 | 72 | ```
|
107 |
| -公众号:Java面试通关手册 |
108 |
| -公众号:Java面试通关手册 |
| 73 | +1 |
| 74 | +0 |
109 | 75 | ```
|
110 | 76 |
|
111 | 77 | **解析:**
|
112 | 78 |
|
| 79 | + |
113 | 80 |
|
114 |
| -很多要这个时候要问了:StringBuffer创建的明明也是对象,那为什么输出结果依然是原来的值呢? |
115 |
| - |
116 |
| -因为在`change1`方法内部我们是新建了一个StringBuffer对象,所以`str`指向了另外一个地址,相应的操作也同样是指向另外的地址的。 |
| 81 | +array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。 |
117 | 82 |
|
118 |
| -那么,如果将`change1`方法改成如下图所示,想必大家应该知道输出什么了,如果你还不知道,那可能就是我讲的有问题了,我反思(开个玩笑,上面程序中已经给出答案): |
119 | 83 |
|
120 |
| -``` |
121 |
| - public static void change1(StringBuffer str) { |
| 84 | +**通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。** |
122 | 85 |
|
123 |
| - str.append("欢迎大家关注"); |
124 |
| - str.insert(3, "(编程)"); |
125 |
| - |
126 |
| - } |
127 |
| -``` |
| 86 | +**很多程序设计语言(特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的作者)认为Java程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。** |
128 | 87 |
|
129 | 88 |
|
130 |
| -#### 2. String类型传递 |
| 89 | +### example 3 |
131 | 90 |
|
132 | 91 | ```java
|
133 |
| - // 测试引用传递:Sring |
134 |
| - @org.junit.Test |
135 |
| - public void method2() { |
136 |
| - String str = new String("公众号:Java面试通关手册"); |
137 |
| - System.out.println(str); |
138 |
| - change2(str); |
139 |
| - System.out.println(str); |
140 |
| - } |
| 92 | +public class Test { |
141 | 93 |
|
142 |
| - public static void change2(String str) { |
143 |
| - // str="abc"; //输出:公众号:Java面试通关手册 |
144 |
| - str = new String("abc"); //输出:公众号:Java面试通关手册 |
| 94 | + public static void main(String[] args) { |
| 95 | + // TODO Auto-generated method stub |
| 96 | + Student s1 = new Student("小张"); |
| 97 | + Student s2 = new Student("小李"); |
| 98 | + Test.swap(s1, s2); |
| 99 | + System.out.println("s1:" + s1.getName()); |
| 100 | + System.out.println("s2:" + s2.getName()); |
145 | 101 | }
|
146 | 102 |
|
| 103 | + public static void swap(Student x, Student y) { |
| 104 | + Student temp = x; |
| 105 | + x = y; |
| 106 | + y = temp; |
| 107 | + System.out.println("x:" + x.getName()); |
| 108 | + System.out.println("y:" + y.getName()); |
| 109 | + } |
| 110 | +} |
147 | 111 | ```
|
148 | 112 |
|
149 | 113 | **结果:**
|
150 | 114 |
|
151 | 115 | ```
|
152 |
| -公众号:Java面试通关手册 |
153 |
| -公众号:Java面试通关手册 |
| 116 | +x:小李 |
| 117 | +y:小张 |
| 118 | +s1:小张 |
| 119 | +s2:小李 |
154 | 120 | ```
|
155 | 121 |
|
156 |
| -可以看到不论是执行`str="abc;"`还是`str = new String("abc");`str的输出的值都不变。 |
157 |
| -按照我们上面讲“StringBuffer类型传递”的时候说的,`str="abc;"`应该会让str的输出的值都不变。为什么呢?因为String在创建之后是不可变的。 |
| 122 | +**解析:** |
158 | 123 |
|
159 |
| -#### 3. 一道类似的题目 |
| 124 | +交换之前: |
160 | 125 |
|
161 |
| -下面的程序输出是什么? |
162 |
| -```java |
163 |
| -public class Demo { |
164 |
| - public static void main(String[] args) { |
165 |
| - Person p = new Person("张三"); |
| 126 | + |
166 | 127 |
|
167 |
| - change(p); |
| 128 | +交换之后: |
168 | 129 |
|
169 |
| - System.out.println(p.name); |
170 |
| - } |
| 130 | + |
171 | 131 |
|
172 |
| - public static void change(Person p) { |
173 |
| - Person person = new Person("李四"); |
174 |
| - p = person; |
175 |
| - } |
176 |
| -} |
177 | 132 |
|
178 |
| -class Person { |
179 |
| - String name; |
| 133 | +通过上面两张图可以很清晰的看出: **方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝** |
180 | 134 |
|
181 |
| - public Person(String name) { |
182 |
| - this.name = name; |
183 |
| - } |
184 |
| -} |
185 |
| -``` |
186 |
| -很明显仍然会输出`张三`。因为`change`方法中重新创建了一个`Person`对象。 |
| 135 | +### 总结 |
187 | 136 |
|
188 |
| -那么,如果把` change`方法改为下图所示,输出结果又是什么呢? |
| 137 | +Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按 |
| 138 | +值传递的。 |
189 | 139 |
|
190 |
| -```java |
191 |
| - public static void change(Person p) { |
192 |
| - p.name="李四"; |
193 |
| - } |
194 |
| -``` |
195 |
| -答案我就不说了,我觉得大家如果认真看完上面的内容之后应该很很清楚了。 |
| 140 | +下面再总结一下Java中方法参数的使用情况: |
| 141 | + |
| 142 | +- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型》 |
| 143 | +- 一个方法可以改变一个对象参数的状态。 |
| 144 | +- 一个方法不能让对象参数引用一个新的对象。 |
| 145 | + |
| 146 | + |
| 147 | +### 参考: |
| 148 | + |
| 149 | +《Java核心技术卷Ⅰ》基础知识第十版第四章4.5小节 |
196 | 150 |
|
197 | 151 | ## 二 ==与equals(重要)
|
198 | 152 |
|
|
0 commit comments