Skip to content

Commit c73d1ed

Browse files
committed
增加“最最最常见的几道Java基础知识面试题总结”
1 parent a1111b5 commit c73d1ed

File tree

1 file changed

+236
-0
lines changed

1 file changed

+236
-0
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
2+
## 一 Java中的值传递和引用传递(非常重要)
3+
4+
**首先要明确的是:“对象传递(数组、类、接口)是引用传递,原始类型数据(整型、浮点型、字符型、布尔型)传递是值传递。”**
5+
6+
### 那么什么是值传递和应用传递呢?
7+
8+
**值传递**是指对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。(因为值传递的时候,实际上是将实参的值复制一份给形参。)
9+
10+
**引用传递**是指对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到所有的对象上。(因为引用传递的时候,实际上是将实参的地址值复制一份给形参。)
11+
12+
有时候面试官不是单纯问你“Java中是值传递还是引用传递”是什么啊,骚年?而是给出一个例子,然后让你写出答案,这种也常见在笔试题目中!所以,非常重要了,请看下面的例子:
13+
14+
### 值传递和应用传递实例
15+
16+
#### 1. 值传递
17+
18+
```java
19+
public static void main(String[] args) {
20+
int num1 = 10;
21+
int num2 = 20;
22+
23+
swap(num1, num2);
24+
25+
System.out.println("num1 = " + num1);
26+
System.out.println("num2 = " + num2);
27+
}
28+
29+
public static void swap(int a, int b) {
30+
int temp = a;
31+
a = b;
32+
b = temp;
33+
34+
System.out.println("a = " + a);
35+
System.out.println("b = " + b);
36+
}
37+
```
38+
39+
**结果:**
40+
41+
```
42+
a = 20
43+
b = 10
44+
num1 = 10
45+
num2 = 20
46+
```
47+
48+
**解析:**
49+
50+
在swap方法中,a、b的值进行交换,并不会影响到num1、num2。因为,a、b中的值,只是从num1、num2的复制过来的。
51+
也就是说,a、b相当于num1、num2的副本,副本的内容无论怎么修改,都不会影响到原件本身。
52+
53+
#### 2. 引用传递
54+
55+
```java
56+
public static void main(String[] args) {
57+
int[] arr = {1,2,3,4,5};
58+
59+
change(arr);
60+
61+
System.out.println(arr[0]);
62+
}
63+
64+
public static void change(int[] array) {
65+
//将数组的第一个元素变为0
66+
array[0] = 0;
67+
}
68+
```
69+
70+
**结果:**
71+
72+
```
73+
1
74+
0
75+
```
76+
77+
**解析:**
78+
79+
无论是主函数,还是change方法,操作的都是同一个地址值对应的数组。 。因此,外部对引用对象的改变会反映到所有的对象上。
80+
81+
### 一些特殊的例子
82+
83+
#### 1. StringBuffer类型传递
84+
85+
```java
86+
// 测试引用传递:StringBuffer
87+
@org.junit.Test
88+
public void method1() {
89+
StringBuffer str = new StringBuffer("公众号:Java面试通关手册");
90+
System.out.println(str);
91+
change1(str);
92+
System.out.println(str);
93+
}
94+
95+
public static void change1(StringBuffer str) {
96+
str = new StringBuffer("abc");//输出:“公众号:Java面试通关手册”
97+
//str.append("欢迎大家关注");//输出:公众号:Java面试通关手册欢迎大家关注
98+
//str.insert(3, "(编程)");//输出:公众号(编程):Java面试通关手册
99+
100+
}
101+
```
102+
103+
**结果:**
104+
105+
```
106+
公众号:Java面试通关手册
107+
公众号:Java面试通关手册
108+
```
109+
110+
**解析:**
111+
112+
113+
很多要这个时候要问了:StringBuffer创建的明明也是对象,那为什么输出结果依然是原来的值呢?
114+
115+
因为在`change1`方法内部我们是新建了一个StringBuffer对象,所以`str`指向了另外一个地址,相应的操作也同样是指向另外的地址的。
116+
117+
那么,如果将`change1`方法改成如下图所示,想必大家应该知道输出什么了,如果你还不知道,那可能就是我讲的有问题了,我反思(开个玩笑,上面程序中已经给出答案):
118+
119+
```
120+
public static void change1(StringBuffer str) {
121+
122+
str.append("欢迎大家关注");
123+
str.insert(3, "(编程)");
124+
125+
}
126+
```
127+
128+
129+
#### 2. String类型传递
130+
131+
```java
132+
// 测试引用传递:Sring
133+
@org.junit.Test
134+
public void method2() {
135+
String str = new String("公众号:Java面试通关手册");
136+
System.out.println(str);
137+
change2(str);
138+
System.out.println(str);
139+
}
140+
141+
public static void change2(String str) {
142+
// str="abc"; //输出:公众号:Java面试通关手册
143+
str = new String("abc"); //输出:公众号:Java面试通关手册
144+
}
145+
146+
```
147+
148+
**结果:**
149+
150+
```
151+
公众号:Java面试通关手册
152+
公众号:Java面试通关手册
153+
```
154+
155+
可以看到不论是执行`str="abc;"`还是`str = new String("abc");`str的输出的值都不变。
156+
按照我们上面讲“StringBuffer类型传递”的时候说的,`str="abc;"`应该会让str的输出的值都不变。为什么呢?因为String在创建之后是不可变的。
157+
158+
#### 3. 一道类似的题目
159+
160+
下面的程序输出是什么?
161+
```java
162+
public class Demo {
163+
public static void main(String[] args) {
164+
Person p = new Person("张三");
165+
166+
change(p);
167+
168+
System.out.println(p.name);
169+
}
170+
171+
public static void change(Person p) {
172+
Person person = new Person("李四");
173+
p = person;
174+
}
175+
}
176+
177+
class Person {
178+
String name;
179+
180+
public Person(String name) {
181+
this.name = name;
182+
}
183+
}
184+
```
185+
很明显仍然会输出`张三`。因为`change`方法中重新创建了一个`Person`对象。
186+
187+
那么,如果把` change`方法改为下图所示,输出结果又是什么呢?
188+
189+
```java
190+
public static void change(Person p) {
191+
p.name="李四";
192+
}
193+
```
194+
答案我就不说了,我觉得大家如果认真看完上面的内容之后应该很很清楚了。
195+
196+
## 二 ==与equals(重要)
197+
198+
**==** : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。
199+
200+
**equals()** : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况(前面第1部分已详细介绍过):
201+
- 情况1:类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
202+
- 情况2:类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
203+
204+
205+
## 三 hashCode与equals(重要)
206+
207+
面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”
208+
209+
### hashCode()介绍
210+
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。
211+
212+
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
213+
214+
### 为什么要有hashCode
215+
216+
217+
**我们以“HashSet如何检查重复”为例子来说明为什么要有hashCode:**
218+
219+
当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他已经加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head fist java》第二版)。这样我们就大大减少了equals的次数,相应就大大提高了执行速度。
220+
221+
222+
223+
### hashCode()与equals()的相关规定
224+
225+
1. 如果两个对象相等,则hashcode一定也是相同的
226+
2. 两个对象相等,对两个对象分别调用equals方法都返回true
227+
3. 两个对象有相同的hashcode值,它们也不一定是相等的
228+
4. **因此,equals方法被覆盖过,则hashCode方法也必须被覆盖**
229+
5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
230+
231+
232+
233+
234+
235+
236+

0 commit comments

Comments
 (0)