Skip to content

Commit 1616589

Browse files
committed
add && fix
1 parent 0fb7e13 commit 1616589

6 files changed

Lines changed: 425 additions & 45 deletions

File tree

docs/01.LearnJavaToFindAJob/02.【初级】6~12k档/01.Java基础/Java基础面试题.md

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ tags:
4545
4646
5. 接口隔离原则——ISP
4747

48-
> 把接口分成满足依赖关系的最小接口,实现类中不能有不需要的方法。
48+
> 把接口分成满足依赖关系的最小接口,实现类中不能有不需要的方法。简单来说就是大接口拆开变成小接口
4949
5050
6. 迪米特原则——LOD
5151

@@ -59,24 +59,24 @@ tags:
5959

6060
常见的有 抽象工厂模式、工厂方法模式、建造者模式、单例模式。
6161

62-
![ ](https://cdn.jsdelivr.net/gh/DogerRain/image@main/img-20210401/401339-20170928225241215-295252070.png)
62+
![ ](http://rainyudianxx.baimuxym.cn/HelloCoder/blog/401339-20170928225241215-295252070.png)
6363

6464

6565

6666
### 2、8种基本数据类型
6767

6868
> String不是基本类型。
6969
70-
双等号(==),比较的是他们的值。基本数据类型没有equals方法。
70+
双等号(==),比较的是他们的值。**基本数据类型没有equals方法。**
7171

72-
![ ](https://images-1253198264.cos.ap-guangzhou.myqcloud.com/clipboard-1601965896460.png)
72+
![ ](http://rainyudianxx.baimuxym.cn/HelloCoder/blog/clipboard-1601965896460.png)
7373

7474
拆箱和装箱:
7575

7676
```java
7777
//自动装箱
7878
Integer total = 99;
79-
//自定拆箱
79+
//自动拆箱
8080
int totalprim = total;
8181
```
8282

@@ -117,12 +117,16 @@ Integer的默认值是null;int的默认值是0。
117117

118118
### 3、访问修饰符
119119

120-
![ ](https://images-1253198264.cos.ap-guangzhou.myqcloud.com/7010483_1495588925759_ACED241801E307EE7A39612F85A94EBF.png)
120+
| 访问修饰符 | 当前类 | 同一个包 | 不同包的子类 | 不同包的非子类 | 通俗理解 |
121+
| :--------------- | :----- | :------- | :----------- | :------------- | :-------------------------------- |
122+
| `public` | ✔️ | ✔️ | ✔️ | ✔️ | **完全开放** - 对所有类可见 |
123+
| `protected` | ✔️ | ✔️ | ✔️ || **家族共享** - 对子类和同包类开放 |
124+
| `default` (不写) | ✔️ | ✔️ ||| **内部资料** - 仅同包内可见 |
125+
| `private` | ✔️ |||| **个人隐私** - 仅本类内可见 |
126+
127+
128+
121129

122-
public:所有地方的类都可以访问。
123-
private:只能在当前类中进行访问。
124-
protected:可以在当前类、当前包、子类中进行访问
125-
不写默认为default:可以在当前类,当前包中进行访问。
126130

127131

128132

@@ -159,6 +163,7 @@ protected:可以在当前类、当前包、子类中进行访问
159163
方法的重载和重写都是实现多态的方式,区别在于:
160164

161165
重载实现的是编译时的多态性。
166+
162167
重写实现的是运行时的多态性。
163168

164169
- 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;**重载对返回类型没有特殊的要求,但不能根据返回类型进行区分。**
@@ -237,7 +242,7 @@ public interface People {
237242
| 实例化 | 不是具体类,不能实例化 | 不是类,不能实例化 |
238243
| 方法体 | 抽象方法访问类型**不能是private**,不能是 staticfinal修饰(需要实现的~),而且默认是 abstract 修饰 | 接口中方法默认是`public abstract`(只能是这两个关键字,或其中一个或都省略)。因为要被继承,所以是public的。 |
239244

240-
> 接口可以看作一个更特殊的抽象类,修饰符和限制都更多了,但是自从1.8中及以后,接口的**普通方法**要比抽象类要灵活了,跟普通类一样使用;而抽象类不能使用static其他基本无异
245+
> 接口可以看作一个更特殊的抽象类,修饰符和限制都更多了,但是自从1.8中及以后,接口的**普通方法**要比抽象类要灵活了,跟普通类一样使用;抽象类除了不能使用static,其他基本无异
241246

242247

243248

@@ -251,7 +256,9 @@ public interface People {
251256

252257
### 8switch中能否使用string做参数?
253258

254-
JDK 1.7之前,switch只能支持byte,short,char,int或者其对应的包装类以及Enum类型.JDK 1.7之后switch开始支持String类型.但到目前为止,switch都不支持long类型。
259+
JDK 1.7之前,switch只能支持byte,short,char,int或者其对应的包装类以及Enum类型。
260+
261+
JDK 1.7之后switch开始支持String类型、枚举类型,但到目前为止,switch都不支持long类型。
255262

256263
**还有break的坑:**
257264

@@ -282,10 +289,8 @@ public class SwitchCaseTest {
282289
### 9StringStringBufferStringBuilder区别
283290

284291
- 可变性
285-
简单的来说:String 类中使用 final 关键字字符数组保存字符串, `private final char value[]` ,所以 String
286-
对象是不可变的。而StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder
287-
中也是使用字符数组保存字符串`char[] value` 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
288-
292+
简单的来说:String 类中使用 final 关键字字符数组保存字符串, `private final char value[]` ,所以 String对象是不可变的。而StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串`char[] value` 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
293+
289294
- 安全性
290295

291296
String 是一个字符串常量,final修饰,当创建之后即不能更改,不可被继承,线程安全
@@ -315,6 +320,17 @@ public class SwitchCaseTest {
315320

316321

317322

323+
| 特性 | String | StringBuffer | StringBuilder |
324+
| :----------- | :--------------------- | :------------------------- | :----------------------- |
325+
| **可变性** | ❌ 不可变 | ✅ 可变 | ✅ 可变 |
326+
| **线程安全** | ✅ 线程安全 | ✅ 线程安全(synchronized| ❌ 非线程安全 |
327+
| **性能** | 最低(频繁创建新对象) | 中等(有同步开销) | **最高**(无同步开销) |
328+
| **使用场景** | 字符串常量、不需要修改 | 多线程环境下的字符串操作 | 单线程环境下的字符串操作 |
329+
330+
331+
332+
333+
318334
留个题目:
319335

320336
```Java
@@ -344,6 +360,18 @@ Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。
344360

345361

346362

363+
或者你可以这样理解:
364+
365+
> 简单来说:离目标小数近的整数;中间的(即 .5)往右靠
366+
367+
```shell
368+
往右 ——————————>
369+
370+
-11.7 -- -11.5 —— -11 —— 0 —— 11.5 —— 12
371+
```
372+
373+
374+
347375
### 11、String s = "xyz"和String s = new String("xyz");区别
348376

349377
`String s = new String("hello");`**可能创建两个对象也可能创建一个对象**。如果常量池中有`hello`字符串常量的话,则仅仅在堆中创建一个对象。如果常量池中没有`hello`对象,则堆上和常量池都需要创建。
@@ -381,7 +409,7 @@ System. out. println(str==str2); //false
381409

382410
### 12、 异常
383411

384-
![](https://cdn.jsdelivr.net/gh/DogerRain/image@main/Home/image-20210115172917862.png)
412+
![](http://rainyudianxx.baimuxym.cn/HelloCoder/blog/image-20210115172917862.png)
385413

386414
Throwable 分为Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。
387415

@@ -419,7 +447,7 @@ Java 的所有异常可以分为受检异常(checked exception)和非受检
419447

420448
### 13Throw 和 throws 的区别:
421449

422-
**位置不同:**
450+
**位置不同:**
423451

424452
1. throws 用在函数上,后面跟的是异常类,可以跟多个;而 throw 用在函数内,后面跟的是异常对象。
425453

@@ -433,6 +461,13 @@ Java 的所有异常可以分为受检异常(checked exception)和非受检
433461

434462

435463

464+
```java
465+
public static void main(String[] args) throws Exception,InterruptedException {}
466+
467+
468+
throw new ZkSerializerException(e);
469+
```
470+
436471

437472

438473
### 14、java中4种类型的流
@@ -453,13 +488,15 @@ Java 的所有异常可以分为受检异常(checked exception)和非受检
453488

454489

455490

456-
字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法。
491+
字节流没有缓冲区,是直接输出的,而字符流是输出到缓冲区的。因此在输出时,字节流不调用colse()方法时,信息已经输出了,而字符流只有在调用close()方法关闭缓冲区时,信息才输出。要想字符流在未关闭时输出信息,则需要手动调用flush()方法。
492+
493+
- 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
494+
495+
- 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
457496

458-
· 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
459497

460-
· 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
461498

462-
**结论:只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节流。**
499+
**结论:只要是处理纯文本数据,就优先考虑使用字符流。除此之外都使用字节流。** 但是字节流可以处理字符流,反之不能。
463500

464501

465502

docs/01.LearnJavaToFindAJob/02.【初级】6~12k档/01.Java基础/Java多线程面试题.md

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,21 @@ synchronized关键字在JavaSE1.6之后进行了主要包括为了减少获得
4444

4545
多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞。
4646

47-
volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。
47+
**volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。**
4848

49-
volatile关键字主要用于解决变量在多个线程之间的**可见性**,而 synchronized关键字解决的是多个线程之间访问资源的同步性。
49+
**volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访问资源的同步性。**
5050

5151
synchronized 保证三大性:**原子性,有序性,可见性**
5252

53-
volatile 保证**有序性,可见性,不能保证原子性**
53+
volatile 保证**有序性,可见性,不能保证原子性**
5454

5555

5656

5757
### 4、sleep方法和wait方法有什么区别
5858

5959
sleep方法和wait方法都可以用来放弃CPU一定的时间暂停当前运行的线程,不同点在于如果线程持有某个对象的监锁,**sleep方法不会释放这个对象的锁,wait方法会释放这个对象的锁**,sleep必须要设定时间,而wait可以设定也可以不设定。
6060

61-
Sleep属于Thread类,wait属于Object类。
61+
sleep属于Thread类,wait属于Object类。
6262

6363

6464

@@ -104,7 +104,7 @@ https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&album_id=151964076808745
104104

105105
1. start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码。
106106

107-
2. 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。
107+
2. 通过调用 Thread 类的 start()方法来启动一个线程, **此时线程是处于就绪状态, 并没有运行。**
108108

109109
3. 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运行 run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
110110

@@ -117,6 +117,7 @@ https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&album_id=151964076808745
117117
1. 都是用来协调多线程对共享对象、变量的访问
118118
2. 都是可重入锁,同一线程可以多次获得同一个锁
119119
3. 都保证了可见性和互斥性
120+
4. synchronized 和 ReentrantLock 都是悲观锁(假设一定会发生冲突,先加锁再操作)
120121

121122
异:
122123

@@ -126,8 +127,6 @@ https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&album_id=151964076808745
126127
3. ReentrantLock 是 API 级别的,synchronized 是 JVM 级别的
127128
4. ReentrantLock 可以实现公平锁
128129
5. ReentrantLock 通过 Condition 可以绑定多个条件
129-
6. 底层实现不一样, synchronized 是同步阻塞,使用的是悲观并发策略,lock 是同步非阻
130-
塞,采用的是乐观并发策略
131130
7. Lock 是一个接口,而 synchronized 是 Java 中的关键字,synchronized 是内置的语言
132131
实现。
133132
8. synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;
@@ -140,6 +139,10 @@ https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&album_id=151964076808745
140139

141140

142141

142+
中断interrupt 只影响线程的状态,不影响 synchronized\ReentrantLockDeadLock 的锁持有
143+
144+
145+
143146
### 11、线程的调度策略
144147

145148
线程调度器选择优先级最高的线程运行,但是,如果发生以下情况,就会终止线程的运行:
@@ -153,14 +156,3 @@ https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&album_id=151964076808745
153156
(4)另外一个更高优先级线程出现
154157

155158
(5)在支持时间片的系统中,该线程的时间片用完
156-
157-
158-
159-
### 12、如何停止一个正在运行的线程?
160-
161-
1. 使用退出标志(比如return),使线程正常退出,也就是当run方法完成后线程终止。
162-
2. 使用stop方法强行终止,但是不推荐这个方法,因为stop和suspend及resume一样都是过期作废的方法。
163-
3. 使用interrupt方法中断线程。
164-
165-
166-
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
锁升级过程:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁。
2+
3+
4+
5+
### 各jdk版本锁
6+
7+
8+
9+
> 在jdk1.5版本(包含)之前,锁的状态只有两种状态:“无锁状态”和“重量级锁状态”,只要有线程访问共享资源对象,则锁直接成为重量级锁。因为加锁和释放锁的过程JVM的底层都是由操作系统mutex lock来实现的,其中会涉及上下文的切换(即用户态和内核态的转换),性能消耗极其高,所以在当时synchronized锁是公认的重量级锁。性能消耗非常大。后来JVM开发团队为解决性能问题,在jdk1.5版本中加入了JUC并发包,包下开发了很多Lock相关的锁。
10+
>
11+
>
12+
>
13+
> jdk1.6版本后,对synchronized锁进行了优化,新加了“偏向锁”和“轻量级锁”,偏向锁是默认开启的,用来减少上下文的切换以提高性能,通过锁的升级来解决不同并发场景下的性能问题。所以锁就有了4种状态。
14+
>
15+
>
16+
>
17+
> 在 JDK 15 中,但由于偏向锁其撤销成本较高,偏向锁被默认禁用了。因为很多开发者发现,对于竞争激烈的场景,偏向锁带来的收益小于其撤销成本。
18+
19+
对象头(Mark Word)存储的内容:
20+
21+
![](http://rainyudianxx.baimuxym.cn/HelloCoder/blog/image-20251126111334471.png)
22+
23+
> 解释:
24+
>
25+
> 无锁:对于共享资源,不涉及多线程的竞争访问,就不会加锁
26+
27+
28+
29+
30+
31+
### 锁升级流程
32+
33+
**第一阶段:无锁 -> 偏向锁**
34+
35+
- 触发条件:一个线程第一次进入同步块。
36+
37+
- 目标:在没有竞争的情况下,消除同步原语的开销。这个锁会“偏向”于第一个获取它的线程。
38+
39+
- 过程:
40+
41+
- 线程 A 访问同步块,检查锁标志位为 01,且偏向锁位为 0(表示可偏向)。
42+
- 使用 CAS 操作,尝试将 Mark Word 中的 ThreadID 替换为自己的线程 ID。
43+
- 如果 CAS 成功,线程 A 就持有了偏向锁。之后,只要线程 A 再次进入这个同步块,它只需要检查 Mark Word 中的 ThreadID 是否是自己,如果是,则无需任何同步操作,直接执行。
44+
45+
- 优点:对于同一个线程重复获取锁的场景,性能极高,几乎没有额外开销。
46+
47+
- 撤销:当有另一个线程 B 来尝试竞争这个锁时,偏向模式就宣告结束。JVM 需要撤销偏向锁。这个过程需要等待全局安全点,然后暂停持有偏向锁的线程 A,检查 A 是否存活或已退出同步块。然后根据情况,将锁升级为轻量级锁或恢复到无锁状态。
48+
49+
> 注意:在 JDK 6 之后,偏向锁是默认开启的,但由于其撤销成本较高,在 JDK 15 中,偏向锁被默认禁用了。因为很多开发者发现,对于竞争激烈的场景,偏向锁带来的收益小于其撤销成本。
50+
51+
52+
53+
54+
55+
**第二阶段:偏向锁 -> 轻量级锁**
56+
57+
- 触发条件:当偏向锁被撤销后,如果有线程竞争,但竞争是轻度的(即线程交替执行同步块,没有同时竞争)。
58+
- 目标:在线程交替执行的场景下,避免直接使用重量级锁带来的用户态/内核态切换开销。
59+
- 过程:
60+
- 线程 A 持有偏向锁,线程 B 来竞争。
61+
- 偏向锁被撤销。
62+
- 线程 A 和 B 都会在自己的栈帧中创建一个名为 锁记录(Lock Record) 的空间。
63+
- 线程 A 首先将当前 Mark Word 的内容复制到自己的锁记录中(称为 Displaced Mark Word)。
64+
- 然后,线程 A 尝试用 CAS 操作将对象头的 Mark Word 替换为指向自己锁记录的指针。
65+
- 如果成功,线程 A 就获取了轻量级锁。
66+
- 此时,线程 B 也会进行同样的 CAS 操作来尝试获取锁,但会失败(因为 Mark Word 已被线程 A 修改)。失败后,线程 B 会自旋(循环重试 CAS)一小段时间来尝试获取锁。
67+
- 如果在线程 B 自旋期间,线程 A 释放了锁(通过 CAS 将 Displaced Mark Word 写回对象头),那么线程 B 的 CAS 就可能成功,从而获取到锁。这样,线程间就没有发生阻塞。
68+
- 优点:竞争的线程不会阻塞,通过自旋来提高响应速度。
69+
70+
升级:如果线程 B 自旋了一定次数后(JDK 6 引入了自旋适应的策略,一般默认锁10次 ?),还没能获取到锁,或者此时有第三个线程来竞争,说明竞争加剧了。此时,轻量级锁就会升级为重量级锁。
71+
72+
73+
74+
**第三阶段:轻量级锁 -> 重量级锁**
75+
76+
- 触发条件:轻量级锁竞争失败(自旋失败或有多线程竞争)。
77+
- 目标:处理重度竞争的场景。
78+
- 过程:
79+
- JVM 会申请一个操作系统层面的 互斥量(Mutex Lock),也称为监视器锁(Monitor)。
80+
- 将 Mark Word 的内容替换为指向这个互斥量的指针。
81+
- 此时,所有未竞争到锁的线程(如线程 B),都会被挂起,进入操作系统的等待队列中。
82+
- 等待锁被释放后,操作系统会负责唤醒这些被挂起的线程,让它们重新竞争。
83+
- 缺点:开销巨大。涉及到操作系统内核态的切换,线程的挂起和唤醒都是很耗时的操作。
84+
- 优点:在激烈竞争下,避免了 CPU 空转(无休止的自旋)。
85+
86+
87+
88+
89+
90+
91+
92+
93+
94+
![](http://rainyudianxx.baimuxym.cn/HelloCoder/blog/image-20251126112248887.png)

0 commit comments

Comments
 (0)