Skip to content

Commit 0b0ea19

Browse files
复习笔记
1 parent 54a4687 commit 0b0ea19

6 files changed

Lines changed: 105 additions & 59 deletions

File tree

Android专项/四大组件/Activity/Actiivty 的启动过程.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,6 @@ startActivity(this);
1717

1818
因为要进程间交互。
1919

20-
## instrumentation 到底是个什么东西。
21-
22-
23-
24-
25-
26-
27-
2820
# 总结1:
2921

3022
通过对在应用里面启动新的Activity的过程进行源码跟踪,我们发现这里面主要涉及到几个类:Activity、ActivityThread、ApplicationThread、ActivityManagerService。

JAVA/0.1 java 问题自问自答.md

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,19 @@
44

55
# 请描述 new 一个对象的流程。
66

7+
1,首先到常量池中找类的带路径全名,然后检查对应的字节码是否已被加载,解析,验证,初始化,如果没有先执行类加载过程(class.forname())。
78

9+
2,类加载过程完成后,虚拟机会为对象分配内存。分配内存有两种方式,根据使用的垃圾收集器的不同使用不同的分配机制。
10+
11+
(1)指针碰撞,当虚拟机使用复制算法或标记整理算法实现的垃圾收集器时,内存区域都是规整的,这时候使用指针碰撞分配内存,用过的内存放在一边,空闲的内存在另一边,中间用一个指针作为分界点,当需要为新对象分配内存时只需把指针向空闲的一边移动一段与对象大小相等的距离。
12+
13+
(2)空闲列表,当虚拟机使用标记清除算法实现的垃圾收集器时,内存都是碎片化的,那虚拟机就要记录哪块内存是可用的,当需要分配内存时,找一块足够大的内存空间给对象实例,并更新记录。
14+
15+
3,设置对象头信息,如所属类,元数据信息,哈希码,gc分代年龄,等等。
16+
17+
4,调用对象的init()方法,根据传入的属性值给对象属性赋值。
18+
19+
5,==在线程栈中新建对象引用,并指向堆中刚刚新建的对象实例。==(所以普通的 DCL 会在超超高并发下出现并发问题。解决办法:为对象引用添加 volatite)
820

921
# Java 对象会不会分配到栈中?
1022

@@ -29,7 +41,13 @@
2941

3042

3143

32-
# java 里 equals和== 区别。
44+
# java 里 equals和 == 区别。
45+
46+
equals 比较值
47+
48+
== 比较内存地址
49+
50+
3351

3452
# try-catch-finally,try 里有 return,finally 还执行么?
3553

@@ -41,7 +59,7 @@
4159
4260
我们看到描述词用的是always,即在**try执行完成之后,finally是一定会执行的**。这种特性可以让程序员避免在`try`语句中使用了`return`, `continue`或者 `break`关键字而忽略了关闭相关资源的操作。把清理相关资源放到`finally`语句块中一直是最佳实践。
4361

44-
# try { return } finally{}?
62+
例如:
4563

4664
我们知道了finally语句会执行,当我们在IDE上运行该程序的时候,会发现运行结果是2。那么为什么不是3呢?
4765

@@ -84,7 +102,7 @@ Exception 又分为可检查(checked)异常和不检查(unchecked)异常
84102

85103

86104

87-
## 拓展: NoClassDefFoundError 和 ClassNOtFoundException
105+
## 拓展: NoClassDefFoundError 和 ClassNotFoundException
88106

89107
NoClassDefFoundError是一个错误(Error),而ClassNOtFoundException是一个异常,在Java中对于错误和异常的处理是不同的,我们可以从异常中恢复程序但却不应该尝试从错误中恢复程序。
90108

@@ -99,15 +117,59 @@ Java支持使用反射方式在运行时动态加载类,例如使用Class.forN
99117

100118

101119

102-
(25)Static class 与 non static class 的区别。
120+
# Static class 与 non static class 的区别。
121+
122+
Static class 不会持有外部类的引用
123+
124+
non static class 会持有外部引用
125+
126+
而且的区别常常使用在方式内存泄漏
127+
128+
129+
103130
(26)PathClassLoader 与 DexClassLoader 的区别是什么?
104131
(27)什么是双亲委托机制,为什么需要双亲委托机制?
105132
(28)描述 JVM 类加载过程。
106-
(29)动态代理是什么?如何实现?
107-
(30)动态代理的方法怎么初始化的?(字节跳动)
133+
134+
# 动态代理是什么?如何实现?
135+
136+
原理是语法糖和反射
137+
138+
# 动态代理的方法怎么初始化的?(字节跳动)
139+
140+
这个问题没太懂?意思是说被代理的接口方法如何初始化的吗?
141+
142+
通过反射获取接口内的所有方法,然后通过语法糖创建一个实现了这个接口的类(代理类),此后外界在调用此接口的方法是都由这个代理类来代理,然后分别转交给我们实现的 invoke 方法。参考博客最后的例子
143+
144+
https://blog.csdn.net/lmj623565791/article/details/79278864
145+
108146
(31)CGLIB 动态代理(字节跳动)
147+
109148
(32)说说反射的应用场景,哪些框架,原理是什么?
110149
(33)Java 泛型的特点与优缺点,泛型擦除是怎么回事?
111-
(34)List 能否转为 List。
112-
(35)泛型 super 和 extends 的区别。a.说法 2:Java 的泛型,<? super T> 和 <? extends T> 的区别。
113-
(36)为什么 IO 是耗时操作?
150+
151+
# 泛型 super 和 extends 的区别。
152+
153+
154+
155+
```java
156+
// list 中装某一 View 的子类, 此种方式可以取值,但是不能赋值。
157+
List<? extends View> list = new ArrayList<>();
158+
```
159+
160+
```java
161+
// list 中装任何可以承载 View 的对象,此种方式可以赋值,但是不能取值。
162+
List<? super View> list1 = new ArrayList<>();
163+
```
164+
165+
166+
167+
# Java 的泛型,<? super T> 和 <? extends T> 的区别。
168+
169+
只写一个 ?是双向限定的,写也不能写,读也不能读。
170+
171+
172+
173+
# 为什么 IO 是耗时操作?
174+
175+
物理原因磁盘读写速度就是比内存慢,速度 磁盘IO< 内存 高速缓存 < CPU

JAVA/Map/1.0 HashMap(JDK8) 0 - 源码解读.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44

55
20年10月18日写
66

7-
本文基于 JDK 8 的 HashMap 缩写
7+
本文基于 JDK 8 的 HashMap 所写
88

99
# 概述
1010

11-
HashMap 是基于 Map 接口实现的哈希表,==它允许拥有为 null 的 key 和 value==。<font color = red>相比 HashTable 二者最大的不同在于 HashTable 不接受 null 值(HashMap 只允许一个 key 为 null, 但 value 可以多个为 null)</font>,而且 HashTable 是线程安全的但 HashMap 并不是,其他方面二者大致相同。要注意 HashMap 并不能保证映射的顺序性,而且随着事件的推移映射的顺序也可能发生改变(这是因为 hash 算法的随机性且在扩容时重新hash)。但是使用链表实现的 LinkedHashMap 可以保证顺序性。
11+
HashMap 是基于 Map 接口实现的哈希表,==它允许拥有为 null 的 key 和 value==。<font color = red>相比 HashTable 二者的明显不同在于 HashTable 不接受 null 值(HashMap 只允许一个 key 为 null, 但 value 可以多个为 null)</font>,而且 HashTable 是线程安全的但 HashMap 并不是,其他方面二者大致相同。要注意 HashMap 并不能保证映射的顺序性,而且随着时间的推移映射的顺序也可能发生改变(这是因为 hash 算法的随机性且在扩容时重新hash)。但是使用链表实现的 LinkedHashMap 可以保证顺序性。
1212

1313

1414

15-
在正常情况下 HashMap 提供的 get 和 put 方法的时间复杂度恒定为 O(1), 当如果存在大量 hash 碰撞的情况 get 方法会退化为 O(logn) ~ O(n) 之间。在遍历的时候 HashMap 的性能和容量、负载因子相关性非常大:容量越大、负载因子越小 HashMap 的性能就越差。如果迭代的性能很重要,则不要设置过高的容量和过低的负载因子。这一点非常重要。
15+
在正常情况下 HashMap 提供的 get 和 put 方法的时间复杂度恒定为 O(1), 当如果存在大量 hash 碰撞的情况 get 方法会退化为 O(logn) ~ O(n) 之间。在遍历的时候 HashMap 的性能和容量、负载因子相关性非常大:容量越大、负载因子越小 HashMap 的性能就越差(原因是 hash 冲突会变多)。如果迭代的性能很重要,则不要设置过高的容量和过低的负载因子。这一点非常重要。
1616

17-
初始容量(initialCapacity)和负载因子(loadFactor)是影响 HashMap 的重要指标,initialCapacity 指的是 HashMap 底层数组初始化时的容量,而 loadFactor 是指自动扩容的阈值。此阈值 = initialCapacity * loadFactor,当哈希表的使用条目大于了阈值。则将容量扩充为 initialCapacity 的 2 倍。
17+
初始容量(initialCapacity)和负载因子(loadFactor)是影响 HashMap 的重要指标,initialCapacity 指的是 HashMap 底层数组初始化时的容量,而 loadFactor 是指自动扩容的阈值。此阈值 = initialCapacity * loadFactor,当哈希表的使用条目大于了阈值。则将容量扩充为 initialCapacity 的 ==2 倍。==
1818

1919
默认情况下 HashMap 的负载因子为 0.75, 这是一个在空间和时间成本上做了很好这中的经验值。如果负载因子较大表面上是可以较少扩容次数节约空间,但是 get 和 push 的执行时间成本将会增加。如果设置的过低,则会多次触发扩容和重新 hash 时间性能会大幅下降。所以权衡 initialCapacity 和 loadFactor 是很关键的。在设置他们的时候应该结合使用场景考虑映射中的预期条目数及其负载因子,要最大程度上减少重新哈希操作的次数。
2020

@@ -179,9 +179,10 @@ HashMap 的数据存储过程主要体现在 putVal 函数当中,但此部分
179179
int hash = hash(key);
180180
// 检测 table 有没有初始化
181181
if (table == null || length == 0) {
182+
// ! 这种一行干了几件事的代码不可学习,增加阅读障碍!
182183
length = (table = resize()).length;
183184
}
184-
// 计算索引 - 计算索引是个重点!!!!
185+
// 计算索引 计算索引是个重点!!!!
185186
int index = indexFor(hash, length);
186187
// 如果当前位置没有使用过,直接创建 Node 存储
187188
if (table[index] == null) {
@@ -210,17 +211,18 @@ HashMap 的数据存储过程主要体现在 putVal 函数当中,但此部分
210211
private V putOnHashCollision(Node<K, V>[] table, Node<K, V> curNode,
211212
int hashOfKey, K key, V value) {
212213
Node<K, V> linkedNode;
214+
// 比较内存地址
213215
if (isEqualsWith(curNode, key, hashOfKey)) {
214216
// key 相同则直接覆盖
215217
linkedNode = curNode;
216218
} else if (curNode instanceof TreeNode) {
217-
// todo 红黑数的操作,暂时忽略
219+
// todo 红黑数的操作,暂时忽略……
218220
linkedNode = new TreeNode<>(hashOfKey, key, value, null);
219221
} else {
220222
// key 不同 在链表中寻找目标
221223
for (int binCount = 0; ; binCount++) {
222224
linkedNode = curNode.next;
223-
// 遍历到了尾节点,直接插入 [尾插法]
225+
// 遍历到了尾节点,直接插入 [尾插法](JDK8在这里做了改动,JDK7还是头插法:造成并发死循环的根本原因)
224226
if (linkedNode == null) {
225227
curNode.next = newNode(hashOfKey, key, value, null);
226228
break;
@@ -246,7 +248,7 @@ HashMap 的数据存储过程主要体现在 putVal 函数当中,但此部分
246248
## 小结:
247249

248250
1. JDK 8 中的 HashMap 是在第一次 put 元素的时候才初始化的,初始化的具体逻辑在 resize 函数中
249-
2. JDK 8 发生冲突的时候使用尾插法插入新数据,而 JDK 7 使用的是头插入法。使用头插法的原因是不想遍历链表,但是头插法会改变节点原始顺序,在多线程中会造成链表有环的问题。
251+
2. ==JDK 8 发生冲突的时候使用尾插法插入新数据,而 JDK 7 使用的是头插入法。使用头插法的原因是不想遍历链表,但是头插法会改变节点原始顺序,在多线程中会造成链表有环的问题。==
250252
3. 更新数据的时候 modeCount 并不会累加
251253
4. 在 hash 冲突的时候会使用 key 的 equls 和 hashCode 方法,==hasCode 方法用于确定数组中的索引位置,而 equls 用于比较 key 是否是同一个==。可见这两个方法很重要,所以如果我们复写了 equls 和 hashCode 其中的任何一个,都要复写另外一个。这样才能确定对象的唯一性,保证使用 Hash 算法集合的正确性。
252254
5. 计算数组索引的函数极其重要,只有好的函数才能将元素均匀的分配在数组中,充分利用存储空间,较少 hash 碰撞。
@@ -271,9 +273,9 @@ static int indexFor(int h, int length) { //jdk1.7的源码,jdk1.8没有这个
271273

272274

273275

274-
方法一的目的是保证 HashMap 容量很小的时候 hashCode 的高位也能参与运算,较少 hash 冲突。
276+
==方法一的目的是保证 HashMap 容量很小的时候 hashCode 的高位也能参与运算,较少 hash 冲突。==
275277

276-
方法二的目的是加快取模运算,但是 % 运算的效率很低,所以可以使用等价的位运算:
278+
==方法二的目的是加快取模运算,但是 % 运算的效率很低,所以可以使用等价的位运算:==
277279

278280
> 当且仅当 $$length = 2^n $$ 此公式成立:$$hashCode \% length = h \& (length - 1)$$ ==这也就是为啥 HashMap 的容量必须为$ 2^n $==
279281

JAVA/Map/2.3 ConcurrentHashMap .md

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,6 @@
11

22
研究了基础原理,以及 ConcurrentHashMap 数据 put 的流程等线程安全的,来回顾一下面试的问题点:
33

4-
* **ConcurrentHashMap 的实现原理**
5-
6-
* **ConcurrentHashMap1.7 和 1.8 的区别?**
7-
* **ConcurrentHashMap 使用什么技术来保证线程安全**
8-
* **ConcurrentHashMap 的 put() 方法**
9-
10-
* **ConcurrentHashmap 不支持 key 或者 value 为 null 的原因?**
11-
* **put() 方法如何实现线程安全呢?**
12-
* **ConcurrentHashMap 扩容机制**
13-
* **ConcurrentHashMap 的 get 方法是否要加锁,为什么?**
14-
* **其他问题**
15-
16-
* **为什么使用 ConcurrentHashMap**
17-
* **ConcurrentHashMap 迭代器是强一致性还是弱一致性?HashMap 呢?**
18-
* **JDK1.7 与 JDK1.8 中 ConcurrentHashMap 的区别**
19-
20-
那我们接下继续看看 CurrentHashMap 核心内容,扩容机制。
21-
224
### ConcurrentHashMap 的扩容机制
235

246
1. 扩容变量

0 commit comments

Comments
 (0)