Skip to content

Commit f27b4e8

Browse files
author
haoyang.shi
committed
fix
1 parent 3e08163 commit f27b4e8

4 files changed

Lines changed: 28 additions & 22 deletions

File tree

SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@
6565
- [Java](java/README.md)
6666
- [OOP](java/1-oop.md)
6767
- [序列化](java/10-serilaser.md)
68-
- [面试题](java/17-questions.md)
6968
- [运算符](java/2-operator.md)
7069
- [异常](java/3-exception.md)
7170
- [范型](java/4-generics.md)
7271
- [Object](java/5-object.md)
7372
- [StringBuilder](java/6-StringBuilder.md)
7473
- [代理](java/7-proxy.md)
74+
- [面试题](java/17-questions.md)
7575
- [集合](java/collection/README.md)
7676
- [集合](java/collection/1-collection.md)
7777
- [HashMap](java/collection/2-HashMap.md)

java/5-object.md

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
返回该对象的 `hashcode`,该方法对hash表提供支持,例如 `HashMap`
1010
对于该方法有几点需要注意:
1111
- 在运行中的Java应用,如果用在 `equals` 中进行比较的信息没有改变,那么不论何时调用都需要返回一致的int值。这个hash值在应用的两次执行中不需要保持一致。
12-
- 如果两个对象根据 `equals` 方法认为是相等的,那么这两个对象也应该返回相等的hashcode
13-
- 不要求两个不相等的对象,在调用 `hashCode` 方法返回的结果是必须是不同的。然而,程序员应该了解不同的对象产生不同的hashcode能够提升哈希表的效率
14-
Object的`hashcode`对不同的对象,尽可能返回不同的hashcode。这通常通过将对象的内部地址转换为整数来实现,但Java编程语言不需要此实现技术。
12+
- 如果两个对象根据 `equals` 方法认为是相等的,那么这两个对象也应该返回相等的 `hashcode`
13+
- 不要求两个不相等的对象,在调用 `hashCode` 方法返回的结果是必须是不同的。然而,程序员应该了解不同的对象产生不同的 `hashcode` 能够提升哈希表的效率
14+
Object的`hashcode`对不同的对象,尽可能返回不同的 `hashcode` 。这通常通过将对象的内部地址转换为整数来实现,但Java编程语言不需要此实现技术。
1515

1616
### Arrays.hashCode
1717

@@ -23,12 +23,13 @@ Arrays.hashCode 是一个数组的浅哈希码实现,深哈希可以使用 `de
2323
- 31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一。
2424
> 另外一些相近的质数,比如37、41、43等等,也都是不错的选择。那么为啥偏偏选中了31呢?请看第二个原因。
2525
26-
- 31可以被 JVM 优化,31 * i = (i << 5) - i。
26+
- 31可以被 JVM 优化,$$31 * i = (i << 5) - i$$
27+
2728
上面两个原因中,第一个需要解释一下,第二个比较简单,就不说了。一般在设计哈希算法时,会选择一个特殊的质数。至于为啥选择质数,我想应该是可以降低哈希算法的冲突率。
2829

2930
在 Effective Java 中有一段相关的解释:
3031

31-
>选择数字31是因为它是一个奇质数,如果选择一个偶数会在乘法运算中产生溢出,导致数值信息丢失,因为乘二相当于移位运算。选择质数的优势并不是特别的明显,但这是一个传统。同时,数字31有一个很好的特性,即乘法运算可以被移位和减法运算取代,来获取更好的性能:31 * i == (i << 5) - i,现代的 Java 虚拟机可以自动的完成这个优化。
32+
>选择数字31是因为它是一个奇质数,如果选择一个偶数会在乘法运算中产生溢出,导致数值信息丢失,因为乘二相当于移位运算。选择质数的优势并不是特别的明显,但这是一个传统。同时,数字31有一个很好的特性,即乘法运算可以被移位和减法运算取代,来获取更好的性能:$$31 * i == (i << 5) - i$$,现代的 Java 虚拟机可以自动的完成这个优化。
3233
3334
## equals
3435

@@ -37,9 +38,12 @@ Arrays.hashCode 是一个数组的浅哈希码实现,深哈希可以使用 `de
3738
## clone
3839

3940
创建一个该对象的副本,并且对于对象 x 应当满足以下表达式:
40-
- x.clone() != x
41-
- x.clone().getClass() == x.getClass()
42-
- x.clone().equals(x)
41+
42+
```
43+
x.clone() != x
44+
x.clone().getClass() == x.getClass()
45+
x.clone().equals(x)
46+
```
4347

4448
## toString
4549

@@ -69,7 +73,9 @@ synchronized (obj) {
6973
7074
#### 解释
7175

72-
虚假唤醒(spurious wakeup)是一个表象,即在多处理器的系统下发出wait的程序有可能在没有notify唤醒的情形下苏醒继续执行。以运行在linux的hotspot虚拟机上的java程序为例,wait方法在jvm执行时实质是调用了底层 `pthread_cond_wait/pthread_cond_timedwait` 函数,挂起等待条件变量来达到线程间同步通信的效果,而底层wait函数在设计之初为了不减慢条件变量操作的效率并没有去保证每次唤醒都是由notify触发,而是把这个任务交由上层应用去实现,即使用者需要定义一个循环去判断是否条件真能满足程序继续运行的需求,当然这样的实现也可以避免因为设计缺陷导致程序异常唤醒的问题。
76+
虚假唤醒(`spurious wakeup`)是一个表象,即在多处理器的系统下发出 wait 的程序有可能在没有 notify 唤醒的情形下苏醒继续执行。
77+
78+
以运行在 Linux 的 hotspot 虚拟机上的 java 程序为例, `wait` 方法在 jvm 执行时实质是调用了底层 `pthread_cond_wait/pthread_cond_timedwait` 函数,挂起等待条件变量来达到线程间同步通信的效果,而底层 `wait` 函数在设计之初为了不减慢条件变量操作的效率并没有去保证每次唤醒都是由 `notify` 触发,而是把这个任务交由上层应用去实现,即使用者需要定义一个循环去判断是否条件真能满足程序继续运行的需求,当然这样的实现也可以避免因为设计缺陷导致程序异常唤醒的问题。
7379

7480
## notify
7581

@@ -86,21 +92,21 @@ synchronized (obj) {
8692

8793
## finalize
8894

89-
当GC认为该对象已经没有任何引用的时候,该方法被GC收集器调用。子类可以 overwrite 该方法来关闭系统资源或者其他清理任务。
95+
当 GC 认为该对象已经没有任何引用的时候,该方法被GC收集器调用。子类可以 `overwrite` 该方法来关闭系统资源或者其他清理任务。
9096

91-
finalize的一般契约是,如果Java™虚拟机确定不再有任何方法可以通过任何尚未死亡的线程访问此对象,除非由于某个操作,它将被调用通过最终确定准备完成的其他一些对象或类来完成。 finalize方法可以采取任何操作,包括使该对象再次可用于其他线程;但是,finalize的通常目的是在对象被不可撤销地丢弃之前执行清理操作。例如,表示输入/输出连接的对象的finalize方法可能会执行显式I/O事务,以在永久丢弃对象之前断开连接。
97+
`finalize` 的一般契约是,如果 Java 虚拟机确定不再有任何方法可以通过任何尚未死亡的线程访问此对象,除非由于某个操作,它将被调用通过最终确定准备完成的其他一些对象或类来完成。 `finalize` 方法可以采取任何操作,包括使该对象再次可用于其他线程但是,`finalize` 的通常目的是在对象被不可撤销地丢弃之前执行清理操作。例如,表示输入/输出连接的对象的 `finalize` 方法可能会执行显式 `I/O` 事务,以在永久丢弃对象之前断开连接。
9298

93-
类Object的finalize方法不执行任何特殊操作;它只是正常返回。 Object的子类可以覆盖此定义
99+
类 Object 的 finalize 方法不执行任何特殊操作;它只是正常返回。 Object 的子类可以覆盖此定义
94100

95-
Java编程语言不保证哪个线程将为任何给定对象调用finalize方法。但是,可以保证,调用finalize时,调用finalize的线程不会持有任何用户可见的同步锁。如果finalize方法抛出未捕获的异常,则忽略该异常并终止该对象的终止。在为对象调用finalize方法之后,在Java虚拟机再次确定不再有任何方法可以通过任何尚未死亡的线程访问此对象之前,不会采取进一步操作,包括可能的操作通过准备完成的其他对象或类,此时可以丢弃该对象。
101+
Java 编程语言不保证哪个线程将为任何给定对象调用 `finalize` 方法。但是,可以保证,调用 finalize 时,调用 finalize 的线程不会持有任何用户可见的同步锁。如果 `finalize` 方法抛出未捕获的异常,则忽略该异常并终止该对象的终止。在为对象调用 `finalize` 方法之后,在 Java 虚拟机再次确定不再有任何方法可以通过任何尚未死亡的线程访问此对象之前,不会采取进一步操作,包括可能的操作通过准备完成的其他对象或类,此时可以丢弃该对象。
96102

97-
对于任何给定对象,Java虚拟机永远不会多次调用finalize方法。finalize方法抛出的任何异常都会导致暂停此对象的终结,但会被忽略。
103+
对于任何给定对象,Java 虚拟机永远不会多次调用 `finalize` 方法。 `finalize` 方法抛出的任何异常都会导致暂停此对象的终结,但会被忽略。
98104

99105
### 缺陷
100106

101-
- 一些与finalize相关的方法,由于一些致命的缺陷,已经被废弃了,如System.runFinalizersOnExit()方法、Runtime.runFinalizersOnExit()方法。
102-
- System.gc()与System.runFinalization()方法增加了finalize方法执行的机会,但不可盲目依赖它们。
103-
- Java语言规范并不保证finalize方法会被及时地执行、而且根本不会保证它们会被执行。
104-
- finalize方法可能会带来性能问题。因为JVM通常在单独的低优先级线程中完成finalize的执行。
105-
- 对象再生问题:finalize方法中,可将待回收对象赋值给GC Roots可达的对象引用,从而达到对象再生的目的。
106-
- finalize方法至多由GC执行一次(用户当然可以手动调用对象的finalize方法,但并不影响GC对finalize的行为)。
107+
- 一些与 `finalize` 相关的方法,由于一些致命的缺陷,已经被废弃了,`System.runFinalizersOnExit()` 方法、`Runtime.runFinalizersOnExit()`方法。
108+
- `System.gc()``System.runFinalization()` 方法增加了finalize方法执行的机会,但不可盲目依赖它们。
109+
- Java 语言规范并不保证 `finalize` 方法会被及时地执行、而且根本不会保证它们会被执行。
110+
- `finalize` 方法可能会带来性能问题。因为JVM通常在单独的低优先级线程中完成finalize的执行。
111+
- 对象再生问题: `finalize` 方法中,可将待回收对象赋值给GC Roots可达的对象引用,从而达到对象再生的目的。
112+
- `finalize` 方法至多由GC执行一次(用户当然可以手动调用对象的 `finalize` 方法,但并不影响GC对 `finalize` 的行为)。

java/6-StringBuilder.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,3 @@
8181
return new String(value, 0, count);
8282
}
8383
```
84-
基于内部数组新建了一个String,注意,**这个String构造方法不会直接用value数组,而会新建一个,以保证String的不可变性**

java/collection/2-HashMap.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ static final int hash(Object key) {
4040
![](images/2-HashMap-7bdc9.png)
4141

4242
因此元素在重新计算hash之后,因为n变为2倍,那么n-1的mask范围在高位多1bit(红色),因此新的index就会发生这样的变化:
43+
4344
![](images/2-HashMap-03719.png)
4445

4546
因此,我们在扩充HashMap的时候,不需要重新计算hash,只需要看看原来的hash值新增的那个bit是1还是0就好了,是0的话索引没变,是1的话索引变成“`原索引+oldCap`”。可以看看下图为16扩充为32的resize示意图:

0 commit comments

Comments
 (0)