-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
364 lines (175 loc) · 653 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>工作前的记录</title>
<link href="/2023/06/25/%E5%B7%A5%E4%BD%9C%E5%89%8D%E7%9A%84%E8%AE%B0%E5%BD%95/"/>
<url>/2023/06/25/%E5%B7%A5%E4%BD%9C%E5%89%8D%E7%9A%84%E8%AE%B0%E5%BD%95/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><p>浅浅记录一下正式工作之前这段时间的日子。</p><p>从学校回家后待了两天,我照常去亲戚家拜访和闲聊,可能是马上要参加工作的原因,大家都和我说了很多,也学到了很多。</p><p>先是我的婶婶,她是内蒙人,填志愿的时候因为分数的原因到了我家这个小地方,毕业后就当了妇幼保健院的护士,在16年的时候和我叔叔结了婚,她头婚,我叔叔二婚,两人相差十多岁,她带我叔叔去见她爸妈时也是主动隐瞒了年龄和二婚的事情。现在两人育有两个女儿,一个六岁一个三岁,都非常可爱,但是给这个家庭带来快乐的同时也带来了很多痛苦吧。她说自己觉得婚前婚后的生活和自己想象中的差别很大,现在积累了很多怨气。婚前她有爱好,会弹古筝,健身,遛狗,也有事业的上进心,会为了考儿童医生的职称每天学习。怀孕之后几乎全部打乱了,孩子出生后她想过请保姆住家,虽然有所缓解但是难免总要分心到孩子身上。就在我回家前几天保姆就辞职了,她不得不做出选择,还是看淡了事业,变回了以前的值班护士,因为这样才有时间去接两个孩子放学,健身也减少了次数,遛狗全权交给了叔叔,古筝倒是再也没弹过了。我去她家时正好赶上晚饭时间,她邀请我尝一尝她新学做的小龙虾,她告诉我说:“虽然生活和我想象中的差别很大,但我还是选择了适应和接纳这一切。家庭的责任让我放弃了一些爱好和事业,但看到孩子们的笑容,我觉得这一切都是值得的。她们是我生活中的快乐源泉,我愿意为她们付出一切。虽然有时候会感到疲惫和无奈,但我知道,我是她们的母亲,我有责任给予他们爱和关怀。虽然现在放下了之前的爱好和事业,但我知道,这只是人生中的一个阶段,我依然保留着对未来的希望和憧憬。家庭是我最重要的支撑,我愿意为了家人的幸福而努力奋斗,我现在也在新的阶段里有了新的爱好,下班之后做一桌好菜也是美事。”</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230624230509288.png" alt="婶婶的朋友圈"></p><p>再是我的叔叔吧,叔叔他其实挺可惜的。小时候他是村子里的小流氓,长的不高但是极其嚣张,甚至有一次打架没打过回家提了把菜刀就冲出去了,最后被我姑姑拦了下来。他初中毕业后就主动辍学出去打工了,如果我没记错的话应该是搬砖,干了一个月后哭着回来和我奶奶说不打工了要好好读书,最后他也成了村子里的第一个大学生,而且是当时的计算机专业。但他在广州上完大学毕业后反而没有从事计算机方面的,而是选择了在淘宝开网店卖珠宝翡翠,当时也算是赶上网购的红利了,很快就靠这个网店在广州买了房买了车,又很快的和第一任婶婶结了婚,但是一年之后就离婚了,具体情况就连我奶奶都不清楚。他接着在广州坚持了几年后还是把房子租出去了,带上全部家当开车回家发展。回到老家后不久就和我姑父合伙在抚州新开的沃尔玛里开了一家连锁的海尔专卖店,我当时还在里面给进门的顾客指导安装APP抽奖。但是现在沃尔玛经营不善,店面开了几年也亏本转出去了。后来他又干过水果店加盟,在苏宁易购卖家电,在抖音上发有关家电的视频获取流量,晚上回家也会看一些书籍,看风向炒股等等,简单说就是自由职业。收入不是很稳定,但还是可以养的起一家四口。知道我要工作之后兴致冲冲的和我说他对现在互联网的看法,他说:”现在做服务业产品的行情都很不好,可以往人工智能方面去了解,或者游戏方面,尽量去大平台甚至大平台的外包起步都行,现在像chatGPT之类的产品出来会淘汰很大一部分低级程序员。“。其实我也是算早一批使用chaGPT的人群之一了,在去年11月份的时候就看到了相关介绍并使用,使用的最频繁的时候反而是做毕设时,它能给出我不了解的技术栈的解决方案,以很低的成本就可以简单使用新方法,甚至提出一些我想不到的实现方法,但是说目前开放的版本可以替代程序员应该是不现实的,但是可以提高效率是一定的。他在知道我就在使用chatGPT之后就很兴奋的说要拿它和自己手机里的讯飞星火对比一下,但是他提的一些问题有点奇怪,比如“给出抚州三日游的旅行方案”、“中医治病的原理”、“艾灸真的可以治病吗”,其实他更像是把它们这些产品当成新的搜索引擎来使用吧。就在我想和他探讨的时候,他却让我帮忙遛一下狗狗,他要给两个女儿艾灸治疗咳嗽了。噢,原来是想知道女儿能不能快点好起来。临走时他让我把简历发给他,他说帮我推给在深圳搞计算机的同学帮忙看看有没有更好的机会……其实我经常会想,如果叔叔他当年真的在广州从事计算机现在会是什么样子,又会不会后悔呢,这个问题就等到以后再问他吧。</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230624235626120.png" alt="名字叫嘟嘟"></p><p>然后是我的堂姐,嗯……她算是我这一辈最开明的人了吧,这肯定也和她的个人经历有关。她在我们老家也算一个富二代了,她的爸爸也就是我的姑父,在20年前就考上了高中老师的编制,然后又拿到了镇上第一个海尔家电的代理权,在镇中心开了个大卖场,在当时那个年代镇上的人想买家电基本上都是跑去他那买。我堂姐小时候也特别叛逆,经常和父母吵架,而且生活很邋遢,家里人对她的评价也不好。她在师范大学读的英专,毕业后去了澳大利亚留学,她说她读了差不多快10年的英语了,回国后一开始也是选择在广州,教人备考雅思、托福、专八这些项目,蛮辛苦的但是也攒不下钱。最后还是回到老家,以非常优秀的经历当上了临川一中的英语老师,在学校对面租了一个三室两厅的房子,一年租金1.1w,骑个小电瓶车就可以去到生活所需的所有地方。她说她现在很满足自己的生活,每天两节课上完就没事了,空下来可以去健身房减肥、去买菜学做饭、去学习考教师编。而且她今年30岁,没对象,也对结婚没有什么向往。和她一起在商场喝咖啡的时候她谈到这点,她觉得我们这小地方对这种思想观念还是存在很多偏见的地方,她说最不想看到结婚之后出现的情况就是:对方承认我的优秀和努力,但是认为我不需要这么优秀和努力,觉得我做这些都是多余的,觉得女方在家庭中就是要做到生孩子带孩子做家务的责任。听完之后我表示认同,但是有些时候我自己都会掉入这种偏见,所以也算是纠正了我这方面的观点。然后她又和我说了些不能沾的东西,无非就是黄赌毒,毒品不多说,碰不得。赌博的话刚好附近就有个活生生的例子,在镇公共汽车站旁边开了20多年的小超市老板,就今年染上了赌瘾,欠款金额巨大,承受不住跳楼了,小超市直接关门开启法拍了。她说黄反而是国内很难控制的,她偷偷告诉我市里某某家电的代理商在整个市里都是有名的嫖客。最后也是和她一起吐槽了一下现在国内大学的注水课程,与实际要求严重脱轨,还有如今惨淡的就业率,现在6月份了,我所在的职位就收到了6000多份的求职意向。再附上堂姐书房一张。</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625161550583.png" alt="堂姐书房"></p><p>在我出发前往深圳的前一个下午,我堂姐和我说有个师傅来她家吃饭休息了,可以来找她聊聊天。她和这位师傅早就认识,起初是这位师傅在化缘时来到了我姑姑的店里,刚好姑姑是信佛的,我堂姐也在这吃饭,饭后堂姐请师傅去她休息顺便请教一些问题,她说经过师傅的解答和开导之后对生活的态度确实发生了些改变。于是她也想让我和师傅聊一聊。我看到师傅的第一眼就和我认知里的尼姑形象很符合,清瘦,和蔼,脸上一直带着微笑,说话也很平和。简单寒暄之后我就开始问一些问题,总的围绕两点:放下和提升自我。师傅说其实这世间万物都是空的,所有的事物都是由我们无量的念头叠加在一起而产生的,包括这个床、这个手机、甚至我们自身的肉团身。你留恋的这些也是,要学会放下,就像你看过的风景、吃过的美食,有些可能让你印象深刻,有些可能就不记得了。但是要看透它们的本质,空想的念头叠加在一起,努力看破。师傅推荐我去读《大乘妙法莲花经》和《觉悟本心》两本经书,说看不懂也要坚持读,会提高你的修为,提高你看世界的视角。按我自己变相的理解就是提高自己的阅历吧,平台高了视角自然就不一样了。我还问师傅为什么出家,师傅说自己已经出家快十年了,当时就是一瞬的感觉自己觉得没有什么好留恋的了,看破了都觉得是空的。后面从堂姐那我才了解到十年前师傅出家的庙还是破破烂烂的,一个和尚也没有,靠师傅一个人打理,讲法,现在庙里修缮完好,也有了香火,师傅也有一群忠实的听众,油然的心生佩服。和师傅聊了大概两个小时,谈了很多问题(如果感兴趣可以联系我获取录音),总得感觉佛教讲究的是“缘”,有一种自“缘”其说的感觉,师傅说你想要完成一件事情,首先需要你有强烈的信念和念头,还需要那一份“缘”来促成,不成则是缘分未到,就像你我今天在这见面也是。所以我觉得念念不忘必有回响应该是错误的哈哈。</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625014927280.png" alt="和师傅的合照"></p><p>在家呆了两天后就出发去深圳了,早上8点的车,粉都没来得及吃就跑了。我有两个在深圳发展的亲戚,一个研究生表哥,一个在深圳干了20多年的舅舅,两个人都是从事IT的。在还没找到工作的时候就和表哥简单聊过,他说现在行情特别不好,可以理解,他所在的公司裁员了两波、自离了两波,现在整个公司没有测试部门了,工作量增加了很多。他的公司也算大厂了,东南亚某知名电商平台,去年过年的时候年终奖也被砍了,说要离职去广州,最后还是选择留下。他让我到时候来深圳的时候可以先在他那住一段时间找房子。一到深圳就开始下暴雨,伞还放到箱子里了,好在要坐很久的地铁才躲过一劫。他租的房子是一栋写字楼大厦因为租不出去改造成的公寓,因为在地铁终点前一个站,后瑞站,所以房租比较便宜,空间也比较大,房型也比较板正。晚上和他一起去吃的潮汕牛肉火锅,然后聊了点对这行业的看法,他说他是悲观派,舅舅属于乐观派,舅舅那时候的发展路线放到如今已经不适用了,他以前是类似风投的做法,他有技术的同时会去寻找他所看好的项目或者公司,然后进去当CTO,再一步步走上来。现在想这样已经很难了,现在说是AI很牛,但是不用魔法对国内都是不开放的。 我问表哥之后打算怎么发展,什么时候找对象,他说他也不清楚,现在行情和就业率都不好,先在这公司干着吧,现在也攒不下钱,每天抽烟都要花费不少,房租更是大头。找对象就更是要慎重,要和自己的理念符合的。吃完饭他又带我逛了一下周边,超市,小吃,奶茶店啥都有,要不是这离我上班的地方太远可能就也住这了。回到他房间后都到10点了,他洗了个澡就开始办公了,我问他这算加班吗?他说不算,上周部门有个女生因为身体原因回重庆老家了,马上又过端午,工作量大了要在过节前完成交接。其实我个人还是很抗拒在下班后忙工作上的事情的,觉得这是属于我自己的时间,但我也不知道自己以后会不会因为各种原因而被迫接受。</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625124421199.png" alt="写代码的表哥"></p><p>到深圳的第二天舅舅就来找我和表哥了,说要带我俩去他家吃饭。舅舅他算是属于跨越阶级了吧,他有个亲弟弟,也就是我表哥的父亲,在他们俩兄弟小时候成绩都很优秀,但是家里只能负担起一个人上大学,最后的结果是供舅舅一个人去了北京,他的弟弟则留在了镇里,后面也当了一个小学老师。舅舅他跟我说起自己当初也是一个人来的深圳,从北京到深圳舍不得买卧铺就硬座了过来,还快递了自己的台式机电脑,那个时候交通还很不发达,在路上还要拿着纸质地图看路线,不像现在用手机导航就可以。他又问我这一届毕业生的就业率怎么样,我回答说差不多30%吧,他也表示感慨。他推荐我去寻找一些开源的,有前景的项目去跟进,也算是为跳槽做准备,他说他很看好一个叫LangChain的框架,甚至说它以后的地位会和大数据领域中Hadoop的级别一样。简单说一下这个东西主要是做什么的,它可以在本地环境中部署一个内部的知识库自动问答应用,可以理解成定制的AI回答工具,其实现在国内很多开发环境都是内网的,不能放到公网上,表哥的公司就警告不要把代码贴到chatGPT上,如果有了这样一个本地的问答库,就可以在内网的开发环境中提高很大的效率,管理者可以更好的管理,甚至可以放到保密场所使用,就脱离了需要魔法和联网的限制。我个人阅历不足,只能对舅舅的长远眼光表示赞同,但也能感觉到确实是这个趋势,以后有时间可以去学习了解一下。其实最让我感到惊讶的是舅舅他们一家对生活和孩子的理念,刚到他家时舅妈一直在房间里,直到开饭她才出来,我一直以为她在睡觉,但是真实情况是在学习,还调侃说要卷死你们这些小年轻,我不知道舅妈的职业是什么,但她说现在在公司真不好呆,以为根本没项目做,但是又要装着很忙的样子,还要让领导知道你在忙,只能学些新技能。他和舅妈几乎不管儿子,就随便他怎么搞,吃饭的时候只喊了两遍,最后到吃完收筷子了他儿子还没出来吃,这种情况在我家几乎不可能发生,后面我偶然听到他儿子和表哥的谈话,说是要准备托福去海外了,目前的选择是德国,只能说深二代还是好啊。小时候我看他儿子不顺眼甚至还打了一架,现在看确实是我自己太狭隘了,同样的生活环境我可能比他还嚣张哈哈。</p><p>剩下几天就是找房子的事情了,加了五六个中介,朋友圈都被刷屏了。在朋友的建议下还是选择了通勤时间短优先于房租便宜,个人感觉也是,上下班时间太长真挺压抑的。我先是从通勤40分钟内从远处找起的,先是到了1号线的坪洲站,这里有个很有名的城中村叫麻布村,几乎都是精装修的公寓,很多打工人都在这里住,一楼的门店很齐全,也很挤,到了那种一开窗就能和对面房间里的人脸对脸的程度,而且表哥以前就住在麻布村,早高峰进地铁站是要站在外面排队的……所以就果断把这里pass掉了。第二天换了个中介带我到9号线的荔林站,这里也有一个城中村叫南山村,在这我是见识到了什么叫做隔断房,一间大房子被份隔断成了七个单间,一开门就感觉好压抑,甚至空调热水器都是五级能耗的,草草看了几间之后就走了。没办法下午又换了一个中介,带我到9号线的南油站附近,这个中介很聪明啊,先是带我到一个脏乱差的生活区隔断房看,然后再带我到房东阿姨别墅改的单间里看,这对比下来直接就提高了我对后者的接受能力。但好在也不是特别差,房型算是这几天看过最板正的了,也比干净,虽然离地铁站稍远,但是只需要坐四站,通勤费用减少了很多,因为在老生活区,快递驿站饭店超市的也不远,最后还是选择了这里。刚来的时候房间还没有床,打了一晚上的地铺,也是发现了一些缺点:第一隔音很差,我甚至能听到隔壁一家吃饭刷视频的声音,第二就是窗外有一盏路灯是不会熄灯的,睡觉时就算拉着窗帘也会有一丝亮光,第三晒衣服的空间真的很小,衣服多了得跑到天台上晒。这几天折腾下来也算有了个落脚的地方和租房经验吧。一定要打听清楚早高峰的地铁情况、隔音、采光、家电能耗、空间布局……</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625151755995.png" alt="床太大了!让房东换个小的"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625151913257.png" alt="打地铺蚊子好多"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625152105781.png" alt="我的房间"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625152225211.png" alt="我的衣柜"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625152831319.png" alt="5号线的9点早高峰"></p><p>最后再记录一下第一次没有吃到粽子的端午,白天收拾东西的时候一屁股把窗帘坐掉了,淦,一开始真的挺不知所措的,找房东肯定得扣押金。想了一下先在美团上找上门师傅,端午节师傅都放假,开价还贼贵,300块不包上门费。后面多亏了万能的评论区推荐了一个APP叫“万师傅”,有点类似滴滴,设置好要维修的东西就可以等维修师傅报价接单了,甚至还可以选择报价低的,最后只花了90块当天下午就搞定了,推荐给出门在外的朋友。晚上本想买点速冻的粽子,去晚了速冻的都被卖光了,买了袋糯米鸡代替一下。端午节晚上睡不着,想着出去逛一下,挑战一下骑单车乱逛最后不看导航回来。不得不说深圳人是真多,凌晨一两点外面的烧烤摊还是没有空位,也可能是我在生活区的原因,店子特别多,甚至还看到了江西菜馆。骑车乱逛看到了好多,有两个女主播在地铁口的路灯下直播卖货,有流浪汉在空地上睡觉(在这看到流浪汉还是挺惊讶的,在我印象里已经很久没有看到过了),有很多骑着小电瓶的路人带着老婆孩子去吃夜宵。骑了差不多40多分钟,最后还真被我骑回来了,但还是没有睡意,选择看部电影。</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625154625318.png" alt="一屁股的惨剧"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625154648069.png" alt="自己蒸的晚饭"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230625154703754.png" alt="晚上看楚门的世界"></p><p>明天正式上班了,定一些目标吧</p><ul><li>努力搞钱,不被淘汰</li><li>脱离低级趣味多看书</li><li>身体健康不出毛病</li><li>找到一个新爱好</li></ul>]]></content>
<categories>
<category>随笔</category>
</categories>
<tags>
<tag>随笔</tag>
<tag>工作</tag>
</tags>
</entry>
<entry>
<title>SQL经典50题</title>
<link href="/2022/11/05/SQL50%E9%A2%98/"/>
<url>/2022/11/05/SQL50%E9%A2%98/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="一、导入SQL数据表"><a href="#一、导入SQL数据表" class="headerlink" title="一、导入SQL数据表"></a>一、导入SQL数据表</h1><h2 id="1-学生表"><a href="#1-学生表" class="headerlink" title="1.学生表"></a>1.学生表</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> Student(SId <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>),Sname <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>),Sage datetime,Ssex <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>));<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'01'</span> , <span class="hljs-string">'赵雷'</span> , <span class="hljs-string">'1990-01-01'</span> , <span class="hljs-string">'男'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'02'</span> , <span class="hljs-string">'钱电'</span> , <span class="hljs-string">'1990-12-21'</span> , <span class="hljs-string">'男'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'03'</span> , <span class="hljs-string">'孙风'</span> , <span class="hljs-string">'1990-05-20'</span> , <span class="hljs-string">'男'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'04'</span> , <span class="hljs-string">'李云'</span> , <span class="hljs-string">'1990-08-06'</span> , <span class="hljs-string">'男'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'05'</span> , <span class="hljs-string">'周梅'</span> , <span class="hljs-string">'1991-12-01'</span> , <span class="hljs-string">'女'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'06'</span> , <span class="hljs-string">'吴兰'</span> , <span class="hljs-string">'1992-03-01'</span> , <span class="hljs-string">'女'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'07'</span> , <span class="hljs-string">'郑竹'</span> , <span class="hljs-string">'1989-07-01'</span> , <span class="hljs-string">'女'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'09'</span> , <span class="hljs-string">'张三'</span> , <span class="hljs-string">'2017-12-20'</span> , <span class="hljs-string">'女'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'10'</span> , <span class="hljs-string">'李四'</span> , <span class="hljs-string">'2017-12-25'</span> , <span class="hljs-string">'女'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'11'</span> , <span class="hljs-string">'李四'</span> , <span class="hljs-string">'2017-12-30'</span> , <span class="hljs-string">'女'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'12'</span> , <span class="hljs-string">'赵六'</span> , <span class="hljs-string">'2017-01-01'</span> , <span class="hljs-string">'女'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Student <span class="hljs-keyword">values</span>(<span class="hljs-string">'13'</span> , <span class="hljs-string">'孙七'</span> , <span class="hljs-string">'2018-01-01'</span> , <span class="hljs-string">'女'</span>);<br></code></pre></td></tr></table></figure><h2 id="2-课程表"><a href="#2-课程表" class="headerlink" title="2.课程表"></a>2.课程表</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> Course(CId <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>),Cname nvarchar(<span class="hljs-number">10</span>),TId <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>));<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Course <span class="hljs-keyword">values</span>(<span class="hljs-string">'01'</span> , <span class="hljs-string">'语文'</span> , <span class="hljs-string">'02'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Course <span class="hljs-keyword">values</span>(<span class="hljs-string">'02'</span> , <span class="hljs-string">'数学'</span> , <span class="hljs-string">'01'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Course <span class="hljs-keyword">values</span>(<span class="hljs-string">'03'</span> , <span class="hljs-string">'英语'</span> , <span class="hljs-string">'03'</span>);<br></code></pre></td></tr></table></figure><h2 id="3-教师表"><a href="#3-教师表" class="headerlink" title="3.教师表"></a>3.教师表</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> Teacher(TId <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>),Tname <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>));<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Teacher <span class="hljs-keyword">values</span>(<span class="hljs-string">'01'</span> , <span class="hljs-string">'张三'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Teacher <span class="hljs-keyword">values</span>(<span class="hljs-string">'02'</span> , <span class="hljs-string">'李四'</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> Teacher <span class="hljs-keyword">values</span>(<span class="hljs-string">'03'</span> , <span class="hljs-string">'王五'</span>);<br></code></pre></td></tr></table></figure><h2 id="4-成绩表"><a href="#4-成绩表" class="headerlink" title="4.成绩表"></a>4.成绩表</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> SC(SId <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>),CId <span class="hljs-type">varchar</span>(<span class="hljs-number">10</span>),score <span class="hljs-type">decimal</span>(<span class="hljs-number">18</span>,<span class="hljs-number">1</span>));<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'01'</span> , <span class="hljs-string">'01'</span> , <span class="hljs-number">80</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'01'</span> , <span class="hljs-string">'02'</span> , <span class="hljs-number">90</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'01'</span> , <span class="hljs-string">'03'</span> , <span class="hljs-number">99</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'02'</span> , <span class="hljs-string">'01'</span> , <span class="hljs-number">70</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'02'</span> , <span class="hljs-string">'02'</span> , <span class="hljs-number">60</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'02'</span> , <span class="hljs-string">'03'</span> , <span class="hljs-number">80</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'03'</span> , <span class="hljs-string">'01'</span> , <span class="hljs-number">80</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'03'</span> , <span class="hljs-string">'02'</span> , <span class="hljs-number">80</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'03'</span> , <span class="hljs-string">'03'</span> , <span class="hljs-number">80</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'04'</span> , <span class="hljs-string">'01'</span> , <span class="hljs-number">50</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'04'</span> , <span class="hljs-string">'02'</span> , <span class="hljs-number">30</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'04'</span> , <span class="hljs-string">'03'</span> , <span class="hljs-number">20</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'05'</span> , <span class="hljs-string">'01'</span> , <span class="hljs-number">76</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'05'</span> , <span class="hljs-string">'02'</span> , <span class="hljs-number">87</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'06'</span> , <span class="hljs-string">'01'</span> , <span class="hljs-number">31</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'06'</span> , <span class="hljs-string">'03'</span> , <span class="hljs-number">34</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'07'</span> , <span class="hljs-string">'02'</span> , <span class="hljs-number">89</span>);<br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> SC <span class="hljs-keyword">values</span>(<span class="hljs-string">'07'</span> , <span class="hljs-string">'03'</span> , <span class="hljs-number">98</span>);<br></code></pre></td></tr></table></figure><h1 id="二、SQL函数和优化"><a href="#二、SQL函数和优化" class="headerlink" title="二、SQL函数和优化"></a>二、SQL函数和优化</h1><p><a href="https://www.cnblogs.com/yuyueq/p/16039789.html">记录一次SQL函数和优化的问题 </a></p><h1 id="三、题目与题解"><a href="#三、题目与题解" class="headerlink" title="三、题目与题解"></a>三、题目与题解</h1><h2 id="1-查询”-01-“课程比”-02-“课程成绩高的学生的信息及课程分数"><a href="#1-查询”-01-“课程比”-02-“课程成绩高的学生的信息及课程分数" class="headerlink" title="1.查询” 01 “课程比” 02 “课程成绩高的学生的信息及课程分数"></a>1.查询” 01 “课程比” 02 “课程成绩高的学生的信息及课程分数</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>student.<span class="hljs-operator">*</span>,<br>sc1.CId <span class="hljs-keyword">AS</span> <span class="hljs-number">01</span>课程,<br>sc2.score <span class="hljs-keyword">AS</span> <span class="hljs-number">01</span>课程分数,<br>sc2.CId <span class="hljs-keyword">AS</span> <span class="hljs-number">02</span>课程,<br>sc2.score <span class="hljs-keyword">AS</span> <span class="hljs-number">02</span>课程分数 <br><span class="hljs-keyword">FROM</span><br>student,<br>sc sc1,<br>sc sc2 <br><span class="hljs-keyword">WHERE</span><br>student.SId <span class="hljs-operator">=</span> sc1.Sid <br><span class="hljs-keyword">AND</span> student.SId <span class="hljs-operator">=</span> sc2.Sid <br><span class="hljs-keyword">AND</span> sc1.CId <span class="hljs-operator">=</span> <span class="hljs-string">'01'</span> <br><span class="hljs-keyword">AND</span> sc2.CId <span class="hljs-operator">=</span> <span class="hljs-string">'02'</span> <br><span class="hljs-keyword">AND</span> sc1.score <span class="hljs-operator">></span> sc2.score<br></code></pre></td></tr></table></figure><h2 id="2-查询平均成绩大于等于-60-分的同学的学生编号和学生姓名和平均成绩"><a href="#2-查询平均成绩大于等于-60-分的同学的学生编号和学生姓名和平均成绩" class="headerlink" title="2.查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩"></a>2.查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>student.SId,<br>Sname,<br><span class="hljs-built_in">AVG</span>( score ) <br><span class="hljs-keyword">FROM</span><br>student,<br>sc <br><span class="hljs-keyword">WHERE</span><br>student.SId <span class="hljs-operator">=</span> sc.SId <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>sc.SId <br><span class="hljs-keyword">HAVING</span><br><span class="hljs-built_in">AVG</span>( score ) <span class="hljs-operator">></span> <span class="hljs-number">60</span><br></code></pre></td></tr></table></figure><h2 id="3-查询在-SC-表存在成绩的学生信息"><a href="#3-查询在-SC-表存在成绩的学生信息" class="headerlink" title="3.查询在 SC 表存在成绩的学生信息"></a>3.查询在 SC 表存在成绩的学生信息</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.<span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> student st, sc<br><span class="hljs-keyword">where</span> st.SId <span class="hljs-operator">=</span> sc.SId<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> sid<br></code></pre></td></tr></table></figure><h2 id="4-查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩-没成绩的显示为-null"><a href="#4-查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩-没成绩的显示为-null" class="headerlink" title="4.查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )"></a>4.查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>student.sid,<br>sname,<br><span class="hljs-built_in">count</span>( sc.cid ) <span class="hljs-keyword">AS</span> 选课数,<br><span class="hljs-built_in">SUM</span>( score ) <span class="hljs-keyword">AS</span> 总成绩 <br><span class="hljs-keyword">FROM</span><br>student<br><span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> sc <span class="hljs-keyword">ON</span> student.SId <span class="hljs-operator">=</span> sc.SId <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>student.SId<br></code></pre></td></tr></table></figure><h2 id="5-查询姓“张”的老师个数"><a href="#5-查询姓“张”的老师个数" class="headerlink" title="5.查询姓“张”的老师个数"></a>5.查询姓“张”的老师个数</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br><span class="hljs-built_in">COUNT</span>( tid ) <span class="hljs-keyword">as</span> 张姓老师<br><span class="hljs-keyword">FROM</span><br>teacher <br><span class="hljs-keyword">WHERE</span><br>Tname <span class="hljs-keyword">LIKE</span> <span class="hljs-string">'张%'</span><br></code></pre></td></tr></table></figure><h2 id="6-查询学过张三老师课的学生的学号和姓名"><a href="#6-查询学过张三老师课的学生的学号和姓名" class="headerlink" title="6.查询学过张三老师课的学生的学号和姓名"></a>6.查询学过张三老师课的学生的学号和姓名</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>student.Sid,<br>Sname <br><span class="hljs-keyword">FROM</span><br>student,<br>sc <br><span class="hljs-keyword">WHERE</span><br>student.SId <span class="hljs-operator">=</span> sc.SId <br><span class="hljs-keyword">AND</span> Cid <span class="hljs-keyword">IN</span> ( <span class="hljs-keyword">SELECT</span> Cid <span class="hljs-keyword">FROM</span> teacher, course <span class="hljs-keyword">WHERE</span> teacher.TId <span class="hljs-operator">=</span> course.TId <span class="hljs-keyword">AND</span> Tname <span class="hljs-operator">=</span> <span class="hljs-string">'张三'</span> ) <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>Sid<br></code></pre></td></tr></table></figure><h2 id="7-查询没有学全所有课程的同学的信息"><a href="#7-查询没有学全所有课程的同学的信息" class="headerlink" title="7.查询没有学全所有课程的同学的信息"></a>7.查询没有学全所有课程的同学的信息</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>st.<span class="hljs-operator">*</span> <br><span class="hljs-keyword">FROM</span><br>student st <br><span class="hljs-keyword">WHERE</span><br>st.SId <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">IN</span> (<br><span class="hljs-keyword">SELECT</span><br>sid <br><span class="hljs-keyword">FROM</span><br>sc <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>sid <br><span class="hljs-keyword">HAVING</span><br><span class="hljs-built_in">count</span>( cid ) <span class="hljs-operator">=</span> ( <span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">count</span>( cid ) <span class="hljs-keyword">FROM</span> course ) <br>)<br></code></pre></td></tr></table></figure><h2 id="8-查询至少有一门课与学号为”-01-“的同学所学相同的同学的信息"><a href="#8-查询至少有一门课与学号为”-01-“的同学所学相同的同学的信息" class="headerlink" title="8.查询至少有一门课与学号为” 01 “的同学所学相同的同学的信息"></a>8.查询至少有一门课与学号为” 01 “的同学所学相同的同学的信息</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.<span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> student st, sc<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId <span class="hljs-keyword">AND</span> cid <span class="hljs-keyword">in</span> (<span class="hljs-keyword">SELECT</span> cid <span class="hljs-keyword">FROM</span> sc <span class="hljs-keyword">where</span> sid <span class="hljs-operator">=</span> <span class="hljs-string">'01'</span>) <span class="hljs-keyword">AND</span> sc.SId <span class="hljs-operator">!=</span> <span class="hljs-string">'01'</span><br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> sid<br></code></pre></td></tr></table></figure><h2 id="9-查询和”-01-“号的同学学习的课程-完全相同的其他同学的信息"><a href="#9-查询和”-01-“号的同学学习的课程-完全相同的其他同学的信息" class="headerlink" title="9. 查询和” 01 “号的同学学习的课程 完全相同的其他同学的信息"></a>9. 查询和” 01 “号的同学学习的课程 完全相同的其他同学的信息</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.<span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> student st, sc<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId <span class="hljs-keyword">AND</span> cid <span class="hljs-keyword">in</span> (<span class="hljs-keyword">SELECT</span> cid <span class="hljs-keyword">FROM</span> sc <span class="hljs-keyword">WHERE</span> sid <span class="hljs-operator">=</span> <span class="hljs-string">'01'</span>) <span class="hljs-keyword">AND</span> sc.SId <span class="hljs-operator">!=</span> <span class="hljs-string">'01'</span><br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> sid<br><span class="hljs-keyword">HAVING</span> <span class="hljs-built_in">COUNT</span>(cid) <span class="hljs-operator">=</span> (<span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">COUNT</span>(cid) <span class="hljs-keyword">FROM</span> sc <span class="hljs-keyword">WHERE</span> SId <span class="hljs-operator">=</span> <span class="hljs-string">'01'</span>)<br></code></pre></td></tr></table></figure><h2 id="10-查询没学过”张三”老师讲授的任一门课程的学生姓名"><a href="#10-查询没学过”张三”老师讲授的任一门课程的学生姓名" class="headerlink" title="10. 查询没学过”张三”老师讲授的任一门课程的学生姓名"></a>10. 查询没学过”张三”老师讲授的任一门课程的学生姓名</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> sname<br><span class="hljs-keyword">FROM</span> student<br><span class="hljs-keyword">WHERE</span> Sname <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> (<br><span class="hljs-keyword">SELECT</span> Sname<br><span class="hljs-keyword">FROM</span> student st, sc, course c, teacher t<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId <span class="hljs-keyword">AND</span> sc.CId <span class="hljs-operator">=</span> c.CId <span class="hljs-keyword">AND</span> c.TId <span class="hljs-operator">=</span> t.TId <span class="hljs-keyword">AND</span> tname <span class="hljs-operator">=</span> <span class="hljs-string">'张三'</span><br>)<br></code></pre></td></tr></table></figure><h2 id="11-查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩"><a href="#11-查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩" class="headerlink" title="11. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩"></a>11. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.SId, Sname, <span class="hljs-built_in">AVG</span>(score)<br><span class="hljs-keyword">FROM</span> student st, sc<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> sc.SId<br><span class="hljs-keyword">HAVING</span> <span class="hljs-built_in">SUM</span>(score <span class="hljs-operator"><</span> <span class="hljs-number">60</span>) <span class="hljs-operator">>=</span> <span class="hljs-number">2</span><br></code></pre></td></tr></table></figure><h2 id="12-检索”-01-“课程分数小于-60,按分数降序排列的学生信息"><a href="#12-检索”-01-“课程分数小于-60,按分数降序排列的学生信息" class="headerlink" title="12. 检索” 01 “课程分数小于 60,按分数降序排列的学生信息"></a>12. 检索” 01 “课程分数小于 60,按分数降序排列的学生信息</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.<span class="hljs-operator">*</span>, score<br><span class="hljs-keyword">FROM</span> student st, sc<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId <span class="hljs-keyword">AND</span> CId <span class="hljs-operator">=</span> <span class="hljs-string">'01'</span> <span class="hljs-keyword">AND</span> score <span class="hljs-operator"><</span> <span class="hljs-number">60</span><br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> score <span class="hljs-keyword">DESC</span><br></code></pre></td></tr></table></figure><h2 id="13-按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩"><a href="#13-按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩" class="headerlink" title="13.按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩"></a>13.按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.SId, st.Sname, Cname, score, <span class="hljs-built_in">AVG</span>(score) <span class="hljs-keyword">OVER</span> (<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> SId) 平均成绩<br><span class="hljs-keyword">FROM</span> student st, sc, course<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId <span class="hljs-keyword">AND</span> course.CId <span class="hljs-operator">=</span> sc.CId<br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> 平均成绩 <span class="hljs-keyword">DESC</span><br></code></pre></td></tr></table></figure><h2 id="14-查询各科成绩最高分、最低分和平均分"><a href="#14-查询各科成绩最高分、最低分和平均分" class="headerlink" title="14. 查询各科成绩最高分、最低分和平均分"></a>14. 查询各科成绩最高分、最低分和平均分</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> Cname, <span class="hljs-built_in">MAX</span>(score), <span class="hljs-built_in">MIN</span>(score), <span class="hljs-built_in">AVG</span>(score)<br><span class="hljs-keyword">FROM</span> course, sc<br><span class="hljs-keyword">WHERE</span> course.CId <span class="hljs-operator">=</span> sc.CId<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> sc.CId<br></code></pre></td></tr></table></figure><h2 id="15-以如下形式显示:课程-ID,课程-name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率-及格为-gt-x3D-60,中等为:70-80,优良为:80-90,优秀为:-gt-x3D-90"><a href="#15-以如下形式显示:课程-ID,课程-name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率-及格为-gt-x3D-60,中等为:70-80,优良为:80-90,优秀为:-gt-x3D-90" class="headerlink" title="15.以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率;及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90"></a>15.以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率;及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>sc.CId 课程ID,<br>Cname 课程名,<br><span class="hljs-built_in">MAX</span>( score ) 最高分,<br><span class="hljs-built_in">MIN</span>( score ) 最低分,<br><span class="hljs-built_in">AVG</span>( score ) 平均分,<br><span class="hljs-built_in">SUM</span>( score <span class="hljs-operator">>=</span> <span class="hljs-number">60</span> )<span class="hljs-operator">/</span> <span class="hljs-built_in">COUNT</span>( score ) 及格率,<br><span class="hljs-built_in">SUM</span>(<span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> score <span class="hljs-keyword">BETWEEN</span> <span class="hljs-number">70</span> <span class="hljs-keyword">AND</span> <span class="hljs-number">80</span> <span class="hljs-keyword">THEN</span> <span class="hljs-number">1</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-number">0</span> <span class="hljs-keyword">END</span>)<span class="hljs-operator">/</span> <span class="hljs-built_in">COUNT</span>( score ) 中等率,<br><span class="hljs-built_in">SUM</span>(<span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> score <span class="hljs-keyword">BETWEEN</span> <span class="hljs-number">80</span> <span class="hljs-keyword">AND</span> <span class="hljs-number">90</span> <span class="hljs-keyword">THEN</span> <span class="hljs-number">1</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-number">0</span> <span class="hljs-keyword">END</span>)<span class="hljs-operator">/</span> <span class="hljs-built_in">COUNT</span>( score ) 优良率,<br><span class="hljs-built_in">SUM</span>( score <span class="hljs-operator">>=</span> <span class="hljs-number">90</span> )<span class="hljs-operator">/</span> <span class="hljs-built_in">COUNT</span>( score ) 优秀率<br><span class="hljs-keyword">FROM</span><br>course,<br>sc <br><span class="hljs-keyword">WHERE</span><br>course.CId <span class="hljs-operator">=</span> sc.CId <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>sc.CId<br></code></pre></td></tr></table></figure><h2 id="16-按各科成绩进行排序,并显示排名,-Score-重复时保留名次空缺"><a href="#16-按各科成绩进行排序,并显示排名,-Score-重复时保留名次空缺" class="headerlink" title="16.按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺"></a>16.按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>cid,<br><span class="hljs-built_in">rank</span>() <span class="hljs-keyword">over</span> ( <span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> cid <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> score <span class="hljs-keyword">DESC</span> ) 排名,<br>score <br><span class="hljs-keyword">FROM</span><br>sc<br></code></pre></td></tr></table></figure><h2 id="17-按各科成绩进行排序,并显示排名,Score-重复时合并名次"><a href="#17-按各科成绩进行排序,并显示排名,Score-重复时合并名次" class="headerlink" title="17.按各科成绩进行排序,并显示排名,Score 重复时合并名次"></a>17.按各科成绩进行排序,并显示排名,Score 重复时合并名次</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>cid,<br><span class="hljs-built_in">dense_rank</span>() <span class="hljs-keyword">over</span> ( <span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> cid <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> score <span class="hljs-keyword">DESC</span> ) 排名,<br>score <br><span class="hljs-keyword">FROM</span><br>sc<br></code></pre></td></tr></table></figure><ul><li>使用rank(),1,2,2,4,4,6</li><li>使用dense_rank(),1,2,2,3,3,4</li></ul><h2 id="18-要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列"><a href="#18-要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列" class="headerlink" title="18.要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列"></a>18.要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> CId, <span class="hljs-built_in">count</span>(SId)<br><span class="hljs-keyword">FROM</span> sc<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> CId<br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-number">2</span> <span class="hljs-keyword">DESC</span>, <span class="hljs-number">1</span> <span class="hljs-keyword">ASC</span><br></code></pre></td></tr></table></figure><h2 id="19-查询学生的总成绩,并进行排名,总分重复时保留名次空缺"><a href="#19-查询学生的总成绩,并进行排名,总分重复时保留名次空缺" class="headerlink" title="19.查询学生的总成绩,并进行排名,总分重复时保留名次空缺"></a>19.查询学生的总成绩,并进行排名,总分重复时保留名次空缺</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>SId, <br><span class="hljs-built_in">SUM</span>( score ) 总分,<br><span class="hljs-built_in">rank</span>() <span class="hljs-keyword">over</span> (<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">sum</span>( score ) <span class="hljs-keyword">DESC</span>) 排名 <br><span class="hljs-keyword">FROM</span><br>sc <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>sid<br></code></pre></td></tr></table></figure><h2 id="20-查询学生的总成绩,并进行排名,总分重复时不保留名次空缺"><a href="#20-查询学生的总成绩,并进行排名,总分重复时不保留名次空缺" class="headerlink" title="20.查询学生的总成绩,并进行排名,总分重复时不保留名次空缺"></a>20.查询学生的总成绩,并进行排名,总分重复时不保留名次空缺</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>SId, <br><span class="hljs-built_in">SUM</span>( score ) 总分,<br><span class="hljs-built_in">dense_rank</span>() <span class="hljs-keyword">over</span> (<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-built_in">sum</span>( score ) <span class="hljs-keyword">DESC</span>) 排名 <br><span class="hljs-keyword">FROM</span><br>sc <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>sid<br></code></pre></td></tr></table></figure><h2 id="21-统计各科成绩各分数段人数:课程编号,课程名称,-100-85-,-85-70-,-70-60-,-60-0-及所占百分比"><a href="#21-统计各科成绩各分数段人数:课程编号,课程名称,-100-85-,-85-70-,-70-60-,-60-0-及所占百分比" class="headerlink" title="21.统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[60-0] 及所占百分比"></a>21.统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[60-0] 及所占百分比</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>cid,<br><span class="hljs-built_in">sum</span>( <span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> score <span class="hljs-keyword">BETWEEN</span> <span class="hljs-number">85</span> <span class="hljs-keyword">AND</span> <span class="hljs-number">100</span> <span class="hljs-keyword">THEN</span> <span class="hljs-number">1</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-number">0</span> <span class="hljs-keyword">END</span> )<span class="hljs-operator">*</span> <span class="hljs-number">1.0</span> <span class="hljs-operator">/</span> <span class="hljs-built_in">count</span>( sid ) <span class="hljs-string">'85-100'</span>,<br><span class="hljs-built_in">sum</span>( <span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> score <span class="hljs-keyword">BETWEEN</span> <span class="hljs-number">70</span> <span class="hljs-keyword">AND</span> <span class="hljs-number">85</span> <span class="hljs-keyword">THEN</span> <span class="hljs-number">1</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-number">0</span> <span class="hljs-keyword">END</span> )<span class="hljs-operator">*</span> <span class="hljs-number">1.0</span> <span class="hljs-operator">/</span> <span class="hljs-built_in">count</span>( sid ) <span class="hljs-string">'70-85'</span>,<br><span class="hljs-built_in">sum</span>( <span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> score <span class="hljs-keyword">BETWEEN</span> <span class="hljs-number">60</span> <span class="hljs-keyword">AND</span> <span class="hljs-number">70</span> <span class="hljs-keyword">THEN</span> <span class="hljs-number">1</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-number">0</span> <span class="hljs-keyword">END</span> )<span class="hljs-operator">*</span> <span class="hljs-number">1.0</span> <span class="hljs-operator">/</span> <span class="hljs-built_in">count</span>( sid ) <span class="hljs-string">'60-70'</span>,<br><span class="hljs-built_in">sum</span>( <span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> score <span class="hljs-keyword">BETWEEN</span> <span class="hljs-number">85</span> <span class="hljs-keyword">AND</span> <span class="hljs-number">100</span> <span class="hljs-keyword">THEN</span> <span class="hljs-number">1</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-number">0</span> <span class="hljs-keyword">END</span> )<span class="hljs-operator">*</span> <span class="hljs-number">1.0</span> <span class="hljs-operator">/</span> <span class="hljs-built_in">count</span>( sid ) <span class="hljs-string">'0-60'</span> <br><span class="hljs-keyword">FROM</span><br>sc <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>cid<br></code></pre></td></tr></table></figure><h2 id="22-查询各科成绩前三名的记录"><a href="#22-查询各科成绩前三名的记录" class="headerlink" title="22.查询各科成绩前三名的记录"></a>22.查询各科成绩前三名的记录</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br><span class="hljs-operator">*</span> <br><span class="hljs-keyword">FROM</span><br>( <span class="hljs-keyword">SELECT</span> cid, sid, score, <span class="hljs-built_in">ROW_NUMBER</span>() <span class="hljs-keyword">over</span> ( <span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> Cid <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> score ) <span class="hljs-keyword">AS</span> pm <span class="hljs-keyword">FROM</span> sc ) t <br><span class="hljs-keyword">WHERE</span><br>pm <span class="hljs-operator"><=</span> <span class="hljs-number">3</span><br></code></pre></td></tr></table></figure><h2 id="23-查询每门课程被选修的学生数"><a href="#23-查询每门课程被选修的学生数" class="headerlink" title="23. 查询每门课程被选修的学生数"></a>23. 查询每门课程被选修的学生数</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>cid,<br><span class="hljs-built_in">count</span>( sid ) <br><span class="hljs-keyword">FROM</span><br>sc <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>cid<br></code></pre></td></tr></table></figure><h2 id="24-查询出只选修两门课程的学生学号和姓名"><a href="#24-查询出只选修两门课程的学生学号和姓名" class="headerlink" title="24.查询出只选修两门课程的学生学号和姓名"></a>24.查询出只选修两门课程的学生学号和姓名</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.SId, Sname<br><span class="hljs-keyword">FROM</span> student st, sc<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> SId<br><span class="hljs-keyword">HAVING</span> <span class="hljs-built_in">COUNT</span>(CId) <span class="hljs-operator">=</span> <span class="hljs-number">2</span><br></code></pre></td></tr></table></figure><h2 id="25-查询男生,女生人数"><a href="#25-查询男生,女生人数" class="headerlink" title="25. 查询男生,女生人数"></a>25. 查询男生,女生人数</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> Ssex, <span class="hljs-built_in">COUNT</span>(SId)<br><span class="hljs-keyword">FROM</span> student st<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> Ssex<br></code></pre></td></tr></table></figure><h2 id="26-查询名字中含有「风」字的学生信息"><a href="#26-查询名字中含有「风」字的学生信息" class="headerlink" title="26.查询名字中含有「风」字的学生信息"></a>26.查询名字中含有「风」字的学生信息</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br><span class="hljs-operator">*</span> <br><span class="hljs-keyword">FROM</span><br>student <br><span class="hljs-keyword">WHERE</span><br>sname <span class="hljs-keyword">LIKE</span> <span class="hljs-string">'%风%'</span><br></code></pre></td></tr></table></figure><h2 id="27-查询同名同性学生名单,并统计同名人数"><a href="#27-查询同名同性学生名单,并统计同名人数" class="headerlink" title="27.查询同名同性学生名单,并统计同名人数"></a>27.查询同名同性学生名单,并统计同名人数</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>Sname,<br><span class="hljs-built_in">COUNT</span>( SId ) <br><span class="hljs-keyword">FROM</span><br>student <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>Sname<br></code></pre></td></tr></table></figure><h2 id="28-查询-1990-年出生的学生名单"><a href="#28-查询-1990-年出生的学生名单" class="headerlink" title="28.查询 1990 年出生的学生名单"></a>28.查询 1990 年出生的学生名单</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>Sname,<br>Sage <br><span class="hljs-keyword">FROM</span><br>student <br><span class="hljs-keyword">WHERE</span><br>Sage <span class="hljs-keyword">BETWEEN</span> <span class="hljs-string">'1990-01-01'</span> <span class="hljs-keyword">AND</span> <span class="hljs-string">'1991-01-01'</span><br></code></pre></td></tr></table></figure><h2 id="29-查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列"><a href="#29-查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列" class="headerlink" title="29.查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列"></a>29.查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> cid, <span class="hljs-built_in">AVG</span>(score)<br><span class="hljs-keyword">FROM</span> sc<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> CId<br><span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> <span class="hljs-number">2</span> <span class="hljs-keyword">DESC</span>, <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><h2 id="30-查询平均成绩大于等于-85-的所有学生的学号、姓名和平均成绩"><a href="#30-查询平均成绩大于等于-85-的所有学生的学号、姓名和平均成绩" class="headerlink" title="30.查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩"></a>30.查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.SId, Sname, <span class="hljs-built_in">AVG</span>(score) avg_score<br><span class="hljs-keyword">FROM</span> student st, sc<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> SId<br><span class="hljs-keyword">HAVING</span> avg_score <span class="hljs-operator">>=</span> <span class="hljs-number">85</span><br></code></pre></td></tr></table></figure><h2 id="31-查询平均成绩大于等于-85-的所有学生的学号、姓名和平均成绩"><a href="#31-查询平均成绩大于等于-85-的所有学生的学号、姓名和平均成绩" class="headerlink" title="31.查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩"></a>31.查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> Sname, Cname, score<br><span class="hljs-keyword">FROM</span> student st, sc, course c<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId <span class="hljs-keyword">AND</span> Cname <span class="hljs-operator">=</span> <span class="hljs-string">'数学'</span> <span class="hljs-keyword">AND</span> c.CId <span class="hljs-operator">=</span> sc.CId <span class="hljs-keyword">AND</span> score <span class="hljs-operator"><</span> <span class="hljs-number">60</span><br></code></pre></td></tr></table></figure><h2 id="32-查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)"><a href="#32-查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)" class="headerlink" title="32.查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)"></a>32.查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>st.sid,<br>st.sname,<br>sc.<span class="hljs-operator">*</span> <br><span class="hljs-keyword">FROM</span><br>student st<br><span class="hljs-keyword">LEFT</span> <span class="hljs-keyword">JOIN</span> sc <span class="hljs-keyword">ON</span> st.sid <span class="hljs-operator">=</span> sc.sid<br></code></pre></td></tr></table></figure><h2 id="33-查询任何一门课程成绩在-70-分以上的姓名、课程名称和分数"><a href="#33-查询任何一门课程成绩在-70-分以上的姓名、课程名称和分数" class="headerlink" title="33.查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数"></a>33.查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> Sname, Cname, score<br><span class="hljs-keyword">FROM</span> student st, sc, course c<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId <span class="hljs-keyword">AND</span> c.CId <span class="hljs-operator">=</span> sc.CId <span class="hljs-keyword">AND</span> score <span class="hljs-operator">></span> <span class="hljs-number">70</span><br></code></pre></td></tr></table></figure><h2 id="34-查询不及格的成绩"><a href="#34-查询不及格的成绩" class="headerlink" title="34.查询不及格的成绩"></a>34.查询不及格的成绩</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.SId, Sname, Cname, score<br><span class="hljs-keyword">FROM</span> student st, sc, course c<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId <span class="hljs-keyword">AND</span> c.CId <span class="hljs-operator">=</span> sc.CId <span class="hljs-keyword">AND</span> score <span class="hljs-operator"><</span> <span class="hljs-number">60</span><br></code></pre></td></tr></table></figure><h2 id="35-查询程编号为-01-且课程成绩在-80-分以上的学生的学号和姓名"><a href="#35-查询程编号为-01-且课程成绩在-80-分以上的学生的学号和姓名" class="headerlink" title="35.查询程编号为 01 且课程成绩在 80 分以上的学生的学号和姓名"></a>35.查询程编号为 01 且课程成绩在 80 分以上的学生的学号和姓名</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>st.SId,<br>Sname,<br>Cname,<br>score <br><span class="hljs-keyword">FROM</span><br>student st,<br>sc,<br>course c <br><span class="hljs-keyword">WHERE</span><br>st.SId <span class="hljs-operator">=</span> sc.SId <br><span class="hljs-keyword">AND</span> c.CId <span class="hljs-operator">=</span> sc.CId <br><span class="hljs-keyword">AND</span> c.CId <span class="hljs-operator">=</span> <span class="hljs-string">'01'</span> <br><span class="hljs-keyword">AND</span> score <span class="hljs-operator">>=</span> <span class="hljs-number">80</span><br></code></pre></td></tr></table></figure><h2 id="36-求每门课程的学生人数"><a href="#36-求每门课程的学生人数" class="headerlink" title="36.求每门课程的学生人数"></a>36.求每门课程的学生人数</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br>sc.cid,<br><span class="hljs-built_in">count</span>( sc.sid ) <br><span class="hljs-keyword">FROM</span><br>sc <br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span><br>sc.cid<br></code></pre></td></tr></table></figure><h2 id="37-成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩"><a href="#37-成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩" class="headerlink" title="37.成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩"></a>37.成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.<span class="hljs-operator">*</span>, c.CId, <span class="hljs-built_in">MAX</span>(score)<br><span class="hljs-keyword">FROM</span> student st, sc, teacher t, course c<br><span class="hljs-keyword">WHERE</span> t.Tname <span class="hljs-operator">=</span> <span class="hljs-string">'张三'</span> <span class="hljs-keyword">AND</span> c.TId <span class="hljs-operator">=</span> t.TId <span class="hljs-keyword">AND</span> sc.CId <span class="hljs-operator">=</span> c.CId <span class="hljs-keyword">AND</span> st.SId <span class="hljs-operator">=</span> sc.SId<br></code></pre></td></tr></table></figure><h2 id="38-成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩"><a href="#38-成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩" class="headerlink" title="38.成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩"></a>38.成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.<span class="hljs-operator">*</span>, c.CId, score<br><span class="hljs-keyword">FROM</span> student st, sc, teacher t, course c<br><span class="hljs-keyword">WHERE</span> t.Tname <span class="hljs-operator">=</span> <span class="hljs-string">'张三'</span> <span class="hljs-keyword">AND</span> c.TId <span class="hljs-operator">=</span> t.TId <span class="hljs-keyword">AND</span> sc.CId <span class="hljs-operator">=</span> c.CId <span class="hljs-keyword">AND</span> st.SId <span class="hljs-operator">=</span> sc.SId <span class="hljs-keyword">AND</span> score <span class="hljs-operator">=</span> (<span class="hljs-keyword">SELECT</span> <span class="hljs-built_in">MAX</span>(score)<br><span class="hljs-keyword">FROM</span> student st, sc, teacher t, course c<br><span class="hljs-keyword">WHERE</span> t.Tname <span class="hljs-operator">=</span> <span class="hljs-string">'张三'</span> <span class="hljs-keyword">AND</span> c.TId <span class="hljs-operator">=</span> t.TId <span class="hljs-keyword">AND</span> sc.CId <span class="hljs-operator">=</span> c.CId <span class="hljs-keyword">AND</span> st.SId <span class="hljs-operator">=</span> sc.SId)<br></code></pre></td></tr></table></figure><h2 id="39-查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩"><a href="#39-查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩" class="headerlink" title="39.查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩"></a>39.查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.SId, Sname, CId, score<br><span class="hljs-keyword">FROM</span> student st, sc<br><span class="hljs-keyword">WHERE</span> st.SId <span class="hljs-operator">=</span> sc.SId<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> SId<br><span class="hljs-keyword">HAVING</span> <span class="hljs-built_in">MAX</span>(score) <span class="hljs-operator">=</span> <span class="hljs-built_in">MIN</span>(score)<br><br><span class="hljs-keyword">select</span> sc.<span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> sc,<br>(<span class="hljs-keyword">select</span> sid,cid,score <span class="hljs-keyword">from</span> sc<br><span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> sid<br><span class="hljs-keyword">having</span> <span class="hljs-built_in">count</span>(<span class="hljs-keyword">distinct</span> cid) <span class="hljs-operator">></span> <span class="hljs-built_in">count</span>(<span class="hljs-keyword">distinct</span> score)) t1<br><span class="hljs-keyword">where</span> sc.sid <span class="hljs-operator">=</span> t1.sid<br></code></pre></td></tr></table></figure><h2 id="40-查询每门功成绩最好的前两名"><a href="#40-查询每门功成绩最好的前两名" class="headerlink" title="40.查询每门功成绩最好的前两名"></a>40.查询每门功成绩最好的前两名</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br><span class="hljs-operator">*</span> <br><span class="hljs-keyword">FROM</span><br>( <span class="hljs-keyword">SELECT</span> Cid, SId, <span class="hljs-built_in">ROW_NUMBER</span>() <span class="hljs-keyword">OVER</span> ( <span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> Cid <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> score <span class="hljs-keyword">DESC</span> ) <span class="hljs-keyword">AS</span> pm, score <span class="hljs-keyword">FROM</span> sc ) t <br><span class="hljs-keyword">WHERE</span><br>pm <span class="hljs-operator"><=</span><span class="hljs-number">2</span><br></code></pre></td></tr></table></figure><h2 id="41-统计每门课程的学生选修人数(超过-5-人的课程才统计)"><a href="#41-统计每门课程的学生选修人数(超过-5-人的课程才统计)" class="headerlink" title="41.统计每门课程的学生选修人数(超过 5 人的课程才统计)"></a>41.统计每门课程的学生选修人数(超过 5 人的课程才统计)</h2><figure class="highlight n1ql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs n1ql"><span class="hljs-keyword">SELECT</span> Cid, <span class="hljs-built_in">COUNT</span>(SId) c<br><span class="hljs-keyword">FROM</span> sc<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> CId<br><span class="hljs-keyword">HAVING</span> c > <span class="hljs-number">5</span><br></code></pre></td></tr></table></figure><h2 id="42-检索至少选修两门课程的学生学号"><a href="#42-检索至少选修两门课程的学生学号" class="headerlink" title="42.检索至少选修两门课程的学生学号"></a>42.检索至少选修两门课程的学生学号</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> Sid, <span class="hljs-built_in">COUNT</span>(CId) c<br><span class="hljs-keyword">FROM</span> sc<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> SId<br><span class="hljs-keyword">HAVING</span> c <span class="hljs-operator">></span> <span class="hljs-number">2</span><br></code></pre></td></tr></table></figure><h2 id="43-查询选修了全部课程的学生信息"><a href="#43-查询选修了全部课程的学生信息" class="headerlink" title="43.查询选修了全部课程的学生信息"></a>43.查询选修了全部课程的学生信息</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> st.<span class="hljs-operator">*</span><br><span class="hljs-keyword">FROM</span> sc, student st, course c<br><span class="hljs-keyword">WHERE</span> sc.SId <span class="hljs-operator">=</span> st.SId<br><span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> SId<br><span class="hljs-keyword">HAVING</span> <span class="hljs-built_in">COUNT</span>(<span class="hljs-keyword">DISTINCT</span> sc.CId) <span class="hljs-operator">=</span> <span class="hljs-built_in">COUNT</span>(<span class="hljs-keyword">DISTINCT</span> c.CId)<br></code></pre></td></tr></table></figure><h2 id="44-查询各学生的年龄"><a href="#44-查询各学生的年龄" class="headerlink" title="44.查询各学生的年龄"></a>44.查询各学生的年龄</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs SQL"><span class="hljs-keyword">SELECT</span><br>sname,<br>TIMESTAMPDIFF(<span class="hljs-keyword">YEAR</span>,sage,CURDATE()) <br><span class="hljs-keyword">FROM</span><br>student<br></code></pre></td></tr></table></figure><h2 id="45-查询本周过生日的同学"><a href="#45-查询本周过生日的同学" class="headerlink" title="45.查询本周过生日的同学"></a>45.查询本周过生日的同学</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br><span class="hljs-operator">*</span> <br><span class="hljs-keyword">FROM</span><br>student <br><span class="hljs-keyword">WHERE</span><br>WEEKOFYEAR( student.Sage )<span class="hljs-operator">=</span> WEEKOFYEAR(<br><span class="hljs-built_in">CURRENT_DATE</span> ())<br></code></pre></td></tr></table></figure><h2 id="46-查询下周过生日的同学(没考虑跨年)"><a href="#46-查询下周过生日的同学(没考虑跨年)" class="headerlink" title="46.查询下周过生日的同学(没考虑跨年)"></a>46.查询下周过生日的同学(没考虑跨年)</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br><span class="hljs-operator">*</span> <br><span class="hljs-keyword">FROM</span><br>student <br><span class="hljs-keyword">WHERE</span><br>WEEKOFYEAR( student.Sage )<span class="hljs-operator">=</span> WEEKOFYEAR(<br><span class="hljs-built_in">CURRENT_DATE</span> ())<span class="hljs-operator">+</span> <span class="hljs-number">1</span><br></code></pre></td></tr></table></figure><h2 id="47-查询本月过生日的同学"><a href="#47-查询本月过生日的同学" class="headerlink" title="47.查询本月过生日的同学"></a>47.查询本月过生日的同学</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br><span class="hljs-operator">*</span> <br><span class="hljs-keyword">FROM</span><br>student <br><span class="hljs-keyword">WHERE</span><br><span class="hljs-keyword">MONTH</span> ( student.Sage )<span class="hljs-operator">=</span> <span class="hljs-keyword">MONTH</span> (<br><span class="hljs-built_in">CURRENT_DATE</span> ())<br></code></pre></td></tr></table></figure><h2 id="48-查询下月过生日的同学"><a href="#48-查询下月过生日的同学" class="headerlink" title="48.查询下月过生日的同学"></a>48.查询下月过生日的同学</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span><br><span class="hljs-operator">*</span> <br><span class="hljs-keyword">FROM</span><br>student <br><span class="hljs-keyword">WHERE</span><br><span class="hljs-keyword">CASE</span> <span class="hljs-keyword">WHEN</span> <span class="hljs-keyword">MONTH</span> (sage)<span class="hljs-operator">=</span> <span class="hljs-number">12</span> <span class="hljs-keyword">THEN</span> <span class="hljs-keyword">MONTH</span> (sage)<span class="hljs-operator">=</span> <span class="hljs-number">1</span> <span class="hljs-keyword">ELSE</span> <span class="hljs-keyword">MONTH</span> (sage)<span class="hljs-operator">=</span>(CURDATE())<span class="hljs-operator">+</span> <span class="hljs-number">1</span> <span class="hljs-keyword">END</span>;<br><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>数据库</category>
<category>SQL</category>
</categories>
<tags>
<tag>SQL</tag>
</tags>
</entry>
<entry>
<title>Java面试题自我总结</title>
<link href="/2022/10/03/%E9%9D%A2%E8%AF%95%E9%A2%98/"/>
<url>/2022/10/03/%E9%9D%A2%E8%AF%95%E9%A2%98/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>以下所有回答都是博主<strong>个人认为</strong>在面试时可以快速的口头表达且清晰的话语。</p><h1 id="Java基础"><a href="#Java基础" class="headerlink" title="Java基础"></a>Java基础</h1><h2 id="Java的基本数据类型了解吗?"><a href="#Java的基本数据类型了解吗?" class="headerlink" title="Java的基本数据类型了解吗?"></a>Java的基本数据类型了解吗?</h2><p>Java一共有8种数据类型;</p><p>整型有<strong>byte</strong>、<strong>short</strong>、<strong>int</strong>、<strong>long</strong>,其中byte占8位1字节,short占16位2字节,int占32位4字节,long占64位8字节;</p><p>字符型有<strong>char</strong>,占16位2字节;</p><p>浮点型有<strong>float</strong>和<strong>double</strong>,其中float占32位4字节,double占64位8字节;</p><p>最后是布尔型<strong>boolean</strong>,占1位,默认值为false</p><hr><h2 id="基本类型和包装类型的区别"><a href="#基本类型和包装类型的区别" class="headerlink" title="基本类型和包装类型的区别"></a>基本类型和包装类型的区别</h2><ul><li>对于成员变量,包装类型不赋值就是<code>null</code>,基本类型不赋值有默认值且不是null;</li><li>包装类型可以用于泛型,而基本类型不行;</li><li>对于基本类型,它的局部变量放置在Java虚拟机栈的局部变量表中,不被<code>static</code>修饰的成员变量放置在堆中;对于包装类型,包装类型属于对象类型,而几乎所有的对象都存放在堆中;</li><li>基本数据类型的内存占用比包装类型小</li></ul><hr><h2 id="面向对象的三大特性"><a href="#面向对象的三大特性" class="headerlink" title="面向对象的三大特性"></a>面向对象的三大特性</h2><p>面向对象的三大特性分别是封装、继承、多态;</p><p>封装:封装指的是把一个对象的状态信息(属性)都隐藏在内部,不允许外部直接访问对象的信息,而是向外部提供一些操作这些属性的方法。最典型的例子就是实体类,实体类的属性用private修饰表示私有,然后提供getter和setter的方法供外部访问。</p><p>继承:继承是指在已有类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以重写父类的功能;通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。</p><p>多态:多态,顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。多态存在的三个必要条件是继承、重写、父类引用指向子类对象。多态不能调用<code>只有子类存在而父类不存在</code>的方法。</p><hr><h2 id="x3D-x3D-和equals的区别"><a href="#x3D-x3D-和equals的区别" class="headerlink" title="==和equals的区别"></a>==和equals的区别</h2><p><code>==</code>常用于基本类型之间的比较,也可用于两个相同类型对象之间的比较</p><ul><li>==在基本类型之间比较的是两者的值是否相等</li><li>如果比较的是两个对象,实际上是比较两个对象的引用,看两者的内存地址是否相等</li></ul><p><code>equals</code>不能用于基本类型之间的比较,只能用来判断两个对象是否相等</p><hr><h2 id="了解Java的异常吗?"><a href="#了解Java的异常吗?" class="headerlink" title="了解Java的异常吗?"></a>了解Java的异常吗?</h2><p>在Java的所有异常中都有一个共同的祖先,<code>Java.lang</code>中的<code>Throwable</code>,</p><p><code>Throwable</code>有两个重要的子类,分别是<code>Exceptions</code>和<code>Error</code>;</p><p><code>Error</code>指的是程序无法处理的错误,无法用catch捕获,例如系统崩溃、虚拟机内存不足、类定义错误等等;</p><p><code>Exceptions</code>指的是可以被try-catch捕获并处理的异常, <code>Exceptions</code>又分为受检查异常<code>CheckExceptions</code>和非受检查异常<code>UnCheckExceptions</code>(或者说是RuntimeExceptions运行时异常);</p><p>这个受检查异常<code>CheckExceptions</code>是指在编译过程中没有被<code>catch</code>或者<code>throws</code>处理的话就无法通过编译,比如在编写IO操作或者SQL操作时编译器会提示用try-catch包裹或者throws出异常;</p><p>而非受检查异常<code>UnCheckExceptions</code>是Java 代码在编译过程中 ,我们即使不处理也可以正常通过编译,<code>RuntimeException</code> 及其子类都统称为非受检查异常,常见的有</p><ul><li><code>NullPointerException</code>(空指针错误)</li><li><code>IllegalArgumentException</code>(参数错误比如方法入参类型错误)</li><li><code>NumberFormatException</code>(字符串转换为数字格式错误,<code>IllegalArgumentException</code>的子类)</li><li><code>ArrayIndexOutOfBoundsException</code>(数组越界错误)</li><li><code>ClassCastException</code>(类转换异常)</li></ul><hr><h2 id="项目中哪里用到了泛型"><a href="#项目中哪里用到了泛型" class="headerlink" title="项目中哪里用到了泛型"></a>项目中哪里用到了泛型</h2><ul><li>自定义接口通用返回结果 <code>CommonResult<T></code> 通过参数 <code>T</code> 可根据具体的返回类型动态指定结果的数据类型</li></ul><hr><h1 id="集合"><a href="#集合" class="headerlink" title="集合"></a>集合</h1><h2 id="说说-List-Set-Queue-Map-四者的区别?"><a href="#说说-List-Set-Queue-Map-四者的区别?" class="headerlink" title="说说 List, Set, Queue, Map 四者的区别?"></a>说说 List, Set, Queue, Map 四者的区别?</h2><ul><li>List存储的元素是有序的、可重复的</li><li>Set存储的元素是无序的、不可重复的</li><li>Queue存储的元素是按照特定的排队规则确定顺序的,可重复</li><li>Map是按照key-value键值对存储的,key是无序、不可重复,value是无序、可重复,一个key只能对应一个value</li></ul><hr><h2 id="说说ArrayList和LinkedList的区别?"><a href="#说说ArrayList和LinkedList的区别?" class="headerlink" title="说说ArrayList和LinkedList的区别?"></a>说说ArrayList和LinkedList的区别?</h2><ul><li><p>ArrayList和LinkedList都是线程不同步的,也就是线程不安全的;</p></li><li><p>ArrayList采用<strong>数组</strong>存储,对于尾删,尾插的时间复杂度都是O(1),对于从中间插入/删除(包括头插头删)的时间复杂度都是O(n-i);</p><p>LinkedList采用<strong>双向链表</strong>存储,对于头尾插/删的时间复杂度都是O(1),从中间插入/删除的时间复杂度为 O(n) ,因为需要先移动到指定位置再插入。</p></li><li><p>LinkedList不支持快速的随机访问,而ArrayList支持,因为可以通过元素序号快速获取元素的值;</p></li><li><p>ArrayList对内存空间的浪费在于会在末尾预留一些空间,而LinkedList对内存空间的花费在于每个元素都要比ArrayList花费多一点,因为要存放前驱和后继</p></li></ul><hr><h2 id="说说ArrayList的扩容机制?"><a href="#说说ArrayList的扩容机制?" class="headerlink" title="说说ArrayList的扩容机制?"></a>说说ArrayList的扩容机制?</h2><p>以jdk8来讲,使用无参构造方法创建ArrayList时,实际上初始化赋值的是一个空数组;</p><p>当执行add()方法时,第一步用默认的数组容量10和当前数组的size+1的大小进行比较,取大值做参数进入判断是否需要扩容的函数,用该参数与当前数组的容量length做比较,如果容量不够,则进入grow()函数扩容,新容量为原容量的1.5倍(实际上是右移1位)</p><p>grow()函数中有两个if判断,第一个是为了数组初始容量为0时将数组容量扩容成10的。</p><hr><h2 id="说说HashSet、LinkedHashSet-和-TreeSet-三者的异同?"><a href="#说说HashSet、LinkedHashSet-和-TreeSet-三者的异同?" class="headerlink" title="说说HashSet、LinkedHashSet 和 TreeSet 三者的异同?"></a>说说HashSet、LinkedHashSet 和 TreeSet 三者的异同?</h2><ul><li>三者都是Set接口的实现类、都能保证<strong>元素唯一</strong>、都是线程不安全的</li><li>三者的底层数据结构不同,<code>HashSet</code>的底层数据结构是基于HashMap实现的<strong>哈希表</strong>;<code>LinkedHashSe</code>t的<strong>是链表加哈希表</strong>,服从<strong>先进先出</strong>原则;<code>TreeSet</code>是<strong>红黑树</strong>,是有序的,有自然排序规则和自定义排序规则</li><li>基于上述的底层数据结构就对应了使用的场景不同</li></ul><hr><h2 id="说说HashMap-和-Hashtable-的区别"><a href="#说说HashMap-和-Hashtable-的区别" class="headerlink" title="说说HashMap 和 Hashtable 的区别"></a>说说HashMap 和 Hashtable 的区别</h2><p>从线程安全的角度来说,<code>HashMap</code>是线程不安全的,<code>Hashtable</code>是线程安全的,因为其内部的方法基本都经过<code>synchronized</code> 修饰,因此<code>HashMap</code>的效率要高一点;</p><p>另外<code>HashMap</code>对key和value的值可以为null,只能有一个key为null,可以有多个value为null;<code>Hashtable</code>则不可以,会报空指针异常;</p><p>其次二者初始容量大小和扩容量也不同,当不指定容量初始值时,<code>HashMap</code>的默认容量大小为16,每次扩容为之前的两倍,<code>Hashtable</code>的默认容量大小为11,每次扩容为之前的2n+1;当指定容量初始值时,<code>HashMap</code>的大小会扩充至2的幂次方大小,比如指定为5,则会扩充成8,<code>Hashtable</code>则是指定多少就是多少。</p><p>从底层数据结构来讲,JDK1.8之后<code>HashMap</code>在解决哈希冲突时,采用了红黑树,当链表的长度大于阈值(默认为8)时,会转换成红黑树,当然转换之前还有一系列的判断操作。<code>Hashtable</code>则没有。</p><hr><h2 id="说说HashMap和HashSet的区别"><a href="#说说HashMap和HashSet的区别" class="headerlink" title="说说HashMap和HashSet的区别"></a>说说HashMap和HashSet的区别</h2><p><code>HashSet</code>的底层就是基于<code>HashMap</code>实现的,只不过<code>HashMap</code>实现了Map接口,存储的是<strong>键值对</strong>,<code>HashSet</code>实现了Set接口,存储的是<strong>对象</strong>;</p><hr><h2 id="说说HashMap和TreeMap的区别"><a href="#说说HashMap和TreeMap的区别" class="headerlink" title="说说HashMap和TreeMap的区别"></a>说说HashMap和TreeMap的区别</h2><p><code>HashMap</code>和<code>TreeMap</code>都实现了AbstractMap接口,但是<code>TreeMap</code>还额外实现了<code>navigableMap</code>接口和<code>SortedMap</code>;</p><p>实现<code>navigableMap</code>接口让<code>TreeMap</code>有了对集合内元素搜索的能力;</p><p>实现<code>SortedMap</code>接口让<code>TreeMap</code>有了对集合中的元素根据键排序的能力。默认是按 key 的升序排序;</p><hr><h1 id="事务的特性"><a href="#事务的特性" class="headerlink" title="事务的特性"></a>事务的特性</h1><ul><li><strong>原子性</strong>(<code>Atomicity</code>) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;</li><li><strong>一致性</strong>(<code>Consistency</code>): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;</li><li><strong>隔离性</strong>(<code>Isolation</code>): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;</li><li><strong>持久性</strong>(<code>Durabilily</code>): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。</li></ul><p>简称<strong>ACID</strong></p><p>🌈 这里要额外补充一点:<strong>只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!</strong></p><hr><h1 id="ACID是靠什么保证的"><a href="#ACID是靠什么保证的" class="headerlink" title="ACID是靠什么保证的"></a>ACID是靠什么保证的</h1><p>A原子性由<strong>undolog日志</strong>来保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的SQL</p><p>C一致性上面提到了,是由其他三大特性保证,程序代码需要保证业务上的一致性</p><p>I隔离性是由MVCC来保证</p><p>D持久性由<strong>redolog日志</strong>来保证,mysql修改数据的时候会在redolog中记录一份日志数据,就算数据没有保存成功,只要日志保存成功了,数据仍然不会丢失</p><p>rends</p><hr><h1 id="SQL"><a href="#SQL" class="headerlink" title="SQL"></a>SQL</h1><ol><li>_ :下划线 代表匹配任意一个字符;</li><li>% :百分号 代表匹配0个或多个字符;</li><li>[]: 中括号 代表匹配其中的任意一个字符;</li><li>[^]: ^尖冒号 代表 非,取反的意思;不匹配中的任意一个字符。</li></ol><p>tips:面试常问的一个问题:你了解哪些数据库优化技术? SQL语句优化也属于数据库优化一部分,而我们的like模糊查询会引起全表扫描,速度比较慢,应该尽量避免使用like关键字进行模糊查询。</p><p>聚合函数结果作为筛选条件时,不能用where,而是用having语法,配合重命名即可;</p><h1 id="并发"><a href="#并发" class="headerlink" title="并发"></a>并发</h1><h2 id="线程的实现方式"><a href="#线程的实现方式" class="headerlink" title="线程的实现方式"></a>线程的实现方式</h2><p>线程的创建分为4种方式</p><ul><li>继承Thread类,重写run方法(启动线程是调用start方法,如果直接调用run方法则是当前线程执行run中的逻辑)</li><li>实现Runnable接口,重写run方法</li><li>实现Callable,重写call方法,配合FutureTask</li><li>基于线程池构建线程</li></ul><p>追其底层实际上都是基于Runnable实现的</p><ul><li>Thread类本身就实现了Runnable接口</li><li>FutureTask实现了RunnableFuture接口,而该接口又继承了Runnable</li><li>worker线程也实现了Runnable</li></ul><h2 id="Java中线程的状态"><a href="#Java中线程的状态" class="headerlink" title="Java中线程的状态"></a>Java中线程的状态</h2><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230312162536517.png" alt="image-20230312162536517"></p><p>Java中一共有6种线程状态,new状态就是新建状态,Thread对象被创建了但是还没有执行start方法;调用了start方法之后就变成了Runnable运行或就绪状态,当线程获取sychronized锁失败时会进入Blocked阻塞状态;还有两种等待状态Wating状态和Timed_Wating,前者需要手动唤醒,后者到达时间后自动唤醒,最后是结束状态Terminated</p><hr><h2 id="停止线程"><a href="#停止线程" class="headerlink" title="停止线程"></a>停止线程</h2><ul><li>stop方法 不推荐</li><li>共享变量,设置一个共享变量,改变这个变量的值来让线程判断是否停止</li><li>interrupt方法,中断标记位,默认为false,wating状态会抛出异常,也可停</li></ul><hr><h2 id="sleep和wait方法的区别"><a href="#sleep和wait方法的区别" class="headerlink" title="sleep和wait方法的区别"></a>sleep和wait方法的区别</h2><ul><li>sleep是Thread类中的static方法,wait是Object类中的方法</li><li>sleep属于Time_Waiting,会自动唤醒,wait属于Waiting,需要手动唤醒</li><li>sleep在持有锁时执行,不会释放锁资源,wait会释放</li><li>sleep可以在持有锁或不持有锁的情况下执行,wait必须在持有锁的情况下执行</li></ul><hr><h2 id="synchonized是什么?有什么用"><a href="#synchonized是什么?有什么用" class="headerlink" title="synchonized是什么?有什么用"></a>synchonized是什么?有什么用</h2><p>synchonized是Java中的一个关键字,翻译成中文是同步的意思,主要解决多个线程之间的同步性,可以保证被他修饰的方法或代码块在任意时刻只能有一个线程执行</p><p>使用方式有以下三种:</p><ul><li>修饰实例方法</li><li>修饰静态方法(实际上是锁当前类,因为静态方法是归整个类所有的)</li><li>修饰代码块</li></ul><hr><h2 id="synchronized-和-volatile-有什么区别?"><a href="#synchronized-和-volatile-有什么区别?" class="headerlink" title="synchronized 和 volatile 有什么区别?"></a>synchronized 和 volatile 有什么区别?</h2><p><code>synchronized</code> 关键字和 <code>volatile</code> 关键字是两个互补的存在,而不是对立的存在!</p><ul><li><code>volatile</code> 关键字是线程同步的轻量级实现,所以 <code>volatile</code>性能肯定比<code>synchronized</code>关键字要好 。但是 <code>volatile</code> 关键字只能用于变量而 <code>synchronized</code> 关键字可以修饰方法以及代码块 。</li><li><code>volatile</code> 关键字能保证数据的可见性,但不能保证数据的原子性。<code>synchronized</code> 关键字两者都能保证。</li><li><code>volatile</code>关键字主要用于解决变量在多个线程之间的可见性,而 <code>synchronized</code> 关键字解决的是多个线程之间访问资源的同步性</li></ul><hr><h2 id="什么是ThreadLocal"><a href="#什么是ThreadLocal" class="headerlink" title="什么是ThreadLocal"></a>什么是ThreadLocal</h2><p>ThreadLocal可以让每个线程都有自己专属的本地变量,存储每个线程的私有数据。</p><hr><h2 id="ThreadLocal原理"><a href="#ThreadLocal原理" class="headerlink" title="ThreadLocal原理"></a>ThreadLocal原理</h2><p>Thread类中有一个叫ThreadLocals的变量,是ThreadLocalMap类型的变量;ThreadLocal本身不存储数据,像是一个工具类,通过ThreadLocal去操作ThreadLocalMap;</p><p>ThreadLocalMap是基于Entry[]实现的,key就是ThreadLocal对象,value就是set方法设置的值</p><hr><h2 id="ThreadLocal内存泄漏"><a href="#ThreadLocal内存泄漏" class="headerlink" title="ThreadLocal内存泄漏"></a>ThreadLocal内存泄漏</h2><p>因为ThreadLocalMap的key是弱引用,value是强引用,当ThreadLocal没有被强引用的情况下,key会被GC清理掉,导致出现key为null的Entry,及时调用remove()方法即可</p><hr><h2 id="线程池的常见参数"><a href="#线程池的常见参数" class="headerlink" title="线程池的常见参数"></a>线程池的常见参数</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs Java"><span class="hljs-keyword">public</span> <span class="hljs-title function_">ThreadPoolExecutor</span><span class="hljs-params">(<span class="hljs-type">int</span> corePoolSize,//线程池的核心线程数量</span><br><span class="hljs-params"> <span class="hljs-type">int</span> maximumPoolSize,//线程池的最大线程数</span><br><span class="hljs-params"> <span class="hljs-type">long</span> keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间</span><br><span class="hljs-params"> TimeUnit unit,//时间单位</span><br><span class="hljs-params"> BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列</span><br><span class="hljs-params"> ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可</span><br><span class="hljs-params"> RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务</span><br><span class="hljs-params"> )</span><br></code></pre></td></tr></table></figure><ul><li><p>corePoolSize : 核心线程大小。线程池一直运行,核心线程就不会停止。</p></li><li><p>maximumPoolSize :线程池最大线程数量。非核心线程数量=maximumPoolSize-corePoolSize</p></li><li><p>keepAliveTime :非核心线程的心跳时间。如果非核心线程在keepAliveTime内没有运行任务,非核心线程会消亡。</p></li><li><p>workQueue :阻塞队列。ArrayBlockingQueue,LinkedBlockingQueue等,用来存放线程任务。</p></li><li><p>defaultHandler :饱和策略。ThreadPoolExecutor类中一共有4种饱和策略。通过实现</p><p>RejectedExecutionHandler</p><p>接口。</p><ul><li>AbortPolicy : 线程任务丢弃报错。默认饱和策略。</li><li>DiscardPolicy : 线程任务直接丢弃不报错。</li><li>DiscardOldestPolicy : 将workQueue<strong>队首任务丢弃</strong>,将最新线程任务重新加入队列执行。</li><li>CallerRunsPolicy :线程池之外的线程直接调用run方法执行。</li></ul></li><li><p>ThreadFactory :线程工厂。新建线程工厂。</p></li></ul><hr><h2 id="线程池执行流程"><a href="#线程池执行流程" class="headerlink" title="线程池执行流程"></a>线程池执行流程</h2><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230312222012885.png" alt="image-20230312222012885"></p><p>优先级:核心线程 阻塞队列 非核心线程</p><hr><h2 id="线程池的状态"><a href="#线程池的状态" class="headerlink" title="线程池的状态"></a>线程池的状态</h2><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20230316231548540.png" alt="image-20230316231548540"></p>]]></content>
<categories>
<category>Java</category>
<category>面试</category>
</categories>
<tags>
<tag>面试</tag>
</tags>
</entry>
<entry>
<title>剑指Offer21-调整数组顺序使奇数位于偶数前面</title>
<link href="/2022/09/06/%E5%89%91%E6%8C%87Offer21-%E8%B0%83%E6%95%B4%E6%95%B0%E7%BB%84%E9%A1%BA%E5%BA%8F%E4%BD%BF%E5%A5%87%E6%95%B0%E4%BD%8D%E4%BA%8E%E5%81%B6%E6%95%B0%E5%89%8D%E9%9D%A2/"/>
<url>/2022/09/06/%E5%89%91%E6%8C%87Offer21-%E8%B0%83%E6%95%B4%E6%95%B0%E7%BB%84%E9%A1%BA%E5%BA%8F%E4%BD%BF%E5%A5%87%E6%95%B0%E4%BD%8D%E4%BA%8E%E5%81%B6%E6%95%B0%E5%89%8D%E9%9D%A2/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p><a href="https://leetcode.cn/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/">剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 - 力扣(LeetCode)</a></p><p>输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数在数组的前半部分,所有偶数在数组的后半部分。</p><p><strong>示例:</strong></p><p>输入:nums = [1,2,3,4]<br>输出:[1,3,2,4]<br>注:[3,1,2,4] 也是正确的答案之一。</p><p><strong>提示:</strong></p><p>0 <= nums.length <= 50000<br>0 <= nums[i] <= 10000</p><hr><h1 id="错误想法"><a href="#错误想法" class="headerlink" title="错误想法"></a>错误想法</h1><p>初始想法采用冒泡排序等排序算法的变性来解题,改变交换数组的条件即可,对于简单数组可行,数组过长则超时</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-type">int</span>[] exchange(<span class="hljs-type">int</span>[] nums) {<br> <span class="hljs-type">int</span> <span class="hljs-variable">n</span> <span class="hljs-operator">=</span> nums.length - <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">while</span> (n != <span class="hljs-number">0</span>){<br> <span class="hljs-type">int</span> <span class="hljs-variable">last</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">j</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; j < n; j++){<br> <span class="hljs-keyword">if</span> (nums[j] % <span class="hljs-number">2</span> == <span class="hljs-number">0</span> && nums[j + <span class="hljs-number">1</span>] % <span class="hljs-number">2</span> == <span class="hljs-number">1</span>){<br> swap(nums, j, j + <span class="hljs-number">1</span>);<br> last = j;<br> }<br> }<br> n = last;<br> }<br> <span class="hljs-keyword">return</span> nums;<br> }<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">swap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> i, <span class="hljs-type">int</span> j)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> a[i];<br> a[i] = a[j];<br> a[j] = temp;<br> }<br>}<br></code></pre></td></tr></table></figure><hr><h1 id="正确思路:双指针"><a href="#正确思路:双指针" class="headerlink" title="正确思路:双指针"></a>正确思路:双指针</h1><p>左指针寻找偶数,右指针寻找奇数,均找到后交换</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Solution</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-type">int</span>[] exchange(<span class="hljs-type">int</span>[] nums) {<br> <span class="hljs-type">int</span> <span class="hljs-variable">left</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>, right = nums.length - <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">while</span> (left < right){<br> <span class="hljs-comment">//注意left < right</span><br> <span class="hljs-keyword">while</span> (left < right && nums[right] % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>){<br> right--;<br> }<br> <span class="hljs-keyword">while</span> (left < right && nums[left] % <span class="hljs-number">2</span> == <span class="hljs-number">1</span>){<br> left++;<br> }<br> swap(nums, left, right);<br> }<br> <span class="hljs-keyword">return</span> nums;<br> }<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">swap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> i, <span class="hljs-type">int</span> j)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> a[i];<br> a[i] = a[j];<br> a[j] = temp;<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>加上 left < right 是为了防止后续的序列中已经排好序了 造成 left 越过了 right</p></blockquote><blockquote><p>与快排不一样,这里不需要注意先左再右还是先右再左,因为不需要进行比较,不会产生比较值位置错误</p></blockquote><p><a href="https://blog.gutaicheng.top/2022/07/24/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F/">快速排序 - GuTaicheng’s Blog</a></p>]]></content>
<categories>
<category>算法与数据结构</category>
<category>LeetCode</category>
</categories>
<tags>
<tag>剑指Offer</tag>
</tags>
</entry>
<entry>
<title>剑指Offer15-二进制中1的个数</title>
<link href="/2022/09/06/%E5%89%91%E6%8C%87Offer15-%E4%BA%8C%E8%BF%9B%E5%88%B6%E4%B8%AD1%E7%9A%84%E4%B8%AA%E6%95%B0/"/>
<url>/2022/09/06/%E5%89%91%E6%8C%87Offer15-%E4%BA%8C%E8%BF%9B%E5%88%B6%E4%B8%AD1%E7%9A%84%E4%B8%AA%E6%95%B0/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p><a href="https://leetcode.cn/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/">剑指 Offer 15. 二进制中1的个数 - 力扣(LeetCode)</a></p><p>编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量).)。</p><p>提示:</p><p>请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。<br>在 Java 中,编译器使用 二进制补码 记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。</p><p>示例 1:</p><p>输入:n = 11 (控制台输入 00000000000000000000000000001011)<br>输出:3<br>解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 ‘1’。</p><hr><p>示例 2:</p><p>输入:n = 128 (控制台输入 00000000000000000000000010000000)<br>输出:1<br>解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 ‘1’。</p><hr><p>示例 3:</p><p>输入:n = 4294967293 (控制台输入 11111111111111111111111111111101,部分语言中 n = -3)<br>输出:31<br>解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 ‘1’。</p><hr><p>提示:</p><p>输入必须是长度为 32 的 二进制串 。</p><h1 id="方法一"><a href="#方法一" class="headerlink" title="方法一"></a>方法一</h1><p>进行与运算(同真为真,其余为假)</p><p>“1”的二进制就为1(00000000000000000000001),将n与1进行&运算,则可以判断n的最右边的数字是否为1</p><p>示例:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java">n:<span class="hljs-number">00000000000000000000000000001001</span><br><span class="hljs-number">1</span>:<span class="hljs-number">00000000000000000000000000000001</span><br>&:<span class="hljs-number">00000000000000000000000000000001</span> = <span class="hljs-number">1</span><br><br>n右移后<br>n:<span class="hljs-number">00000000000000000000000000000100</span><br><span class="hljs-number">1</span>:<span class="hljs-number">00000000000000000000000000000001</span><br>&:<span class="hljs-number">00000000000000000000000000000000</span> = <span class="hljs-number">0</span><br><br></code></pre></td></tr></table></figure><p>代码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">hammingWeight</span><span class="hljs-params">(<span class="hljs-type">int</span> n)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">sum</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">while</span> (n!=<span class="hljs-number">0</span>){<br> sum += n & <span class="hljs-number">1</span>;<br> n = n >>><span class="hljs-number">1</span>;<br> }<br> <span class="hljs-keyword">return</span> sum;<br>}<br></code></pre></td></tr></table></figure><blockquote><p>“>>>”无符号右移</p><p>操作规则:无论正负数,前面补零。</p><p>“>>”右移</p><p>操作规则:正数前面补零,负数前面补1</p><p>“<<”左移</p><p>操作规则:无论正负数,后面补零。</p></blockquote><hr><h1 id="方法二"><a href="#方法二" class="headerlink" title="方法二"></a>方法二</h1><p>巧用n&(n - 1)</p><figure class="highlight makefile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs makefile"><span class="hljs-section">n: 00000000000000000000000000011000</span><br>n - 1:00000000000000000000000000010111<br><span class="hljs-section">&: 00000000000000000000000000010000</span><br><br><span class="hljs-section">n: 00000000000000000000000000010000</span><br>n - 1:00000000000000000000000000001111<br><span class="hljs-section">&: 00000000000000000000000000000000</span><br></code></pre></td></tr></table></figure><p>n - 1: 把n最右边的1置0,该1后面的0置1</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-type">int</span> <span class="hljs-title function_">hammingWeight</span><span class="hljs-params">(<span class="hljs-type">int</span> n)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">sum</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">while</span> (n!=<span class="hljs-number">0</span>){<br>sum++;<br>n = n & (n - <span class="hljs-number">1</span>);<br>}<br><span class="hljs-keyword">return</span> sum;<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>算法与数据结构</category>
<category>LeetCode</category>
</categories>
<tags>
<tag>剑指Offer</tag>
</tags>
</entry>
<entry>
<title>JUC学习</title>
<link href="/2022/09/04/JUC%E5%AD%A6%E4%B9%A0/"/>
<url>/2022/09/04/JUC%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><p><a href="https://www.bilibili.com/video/BV16J411h7Rd">bilibili</a></p><hr><h1 id="1-进程与线程"><a href="#1-进程与线程" class="headerlink" title="1. 进程与线程"></a>1. 进程与线程</h1><h2 id="1-1-进程"><a href="#1-1-进程" class="headerlink" title="1.1 进程"></a>1.1 进程</h2><ul><li>程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的。</li><li>当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。</li><li>进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器 、QQ等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等)</li></ul><hr><h2 id="1-2-线程"><a href="#1-2-线程" class="headerlink" title="1.2 线程"></a>1.2 线程</h2><ul><li>一个进程之内可以分为一到<strong>多个</strong>线程。</li><li>一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU 执行 。</li><li>Java 中,线程作为小调度单位,进程作为资源分配的小单位。 在 windows 中进程是不活动的,只是作 为线程的容器</li></ul><hr><h2 id="1-3-二者对比"><a href="#1-3-二者对比" class="headerlink" title="1.3 二者对比"></a>1.3 二者对比</h2><ul><li>进程基本上相互独立的,而线程存在于进程内,是进程的一个子集 进程拥有共享的资源,如内存空间等,供其内部的线程共享<ul><li>进程间通信较为复杂 同一台计算机的进程通信称为 IPC(Inter-process communication)</li><li>不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP</li></ul></li><li>线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低</li></ul><hr><h2 id="1-4-从-JVM-角度说进程和线程之间的关系"><a href="#1-4-从-JVM-角度说进程和线程之间的关系" class="headerlink" title="1.4 从 JVM 角度说进程和线程之间的关系"></a>1.4 从 JVM 角度说进程和线程之间的关系</h2><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220904162753787.png" alt="image-20220904162753787"></p><p>从上图可以看出:一个进程中可以有多个线程,多个线程共享进程的<strong>堆</strong>和<strong>方法区 (JDK1.8 之后的元空间)**资源,但是每个线程有自己的**程序计数器**、</strong>虚拟机栈** 和 <strong>本地方法栈</strong>。</p><p><strong>总结:</strong> <strong>线程是进程划分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反。</strong></p><hr><h1 id="2-并行与并发"><a href="#2-并行与并发" class="headerlink" title="2. 并行与并发"></a>2. 并行与并发</h1><p>引用 Rob Pike 的一段描述:</p><ul><li>并发(concurrent)是同一时间<strong>应对</strong>(dealing with)多件事情的能力(实际上还是串行)</li><li>并行(parallel)是同一时间<strong>动手做</strong>(doing)多件事情的能力(多核)</li></ul><p>最关键的点是:是否是 <strong>同时</strong> 执行。</p><p>例子</p><ul><li>家庭主妇做饭、打扫卫生、给孩子喂奶,她一个人轮流交替做这多件事,这时就是并发</li><li>家庭主妇雇了个保姆,她们一起这些事,这时既有并发,也有并行(这时会产生竞争,例如锅只有一口,一个人用锅时,另一个人就得等待)</li><li>雇了3个保姆,一个专做饭、一个专打扫卫生、一个专喂奶,互不干扰,这时是并行</li></ul><hr><h1 id="3-同步与异步"><a href="#3-同步与异步" class="headerlink" title="3. 同步与异步"></a>3. 同步与异步</h1><ul><li><strong>同步</strong> : 发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。</li><li><strong>异步</strong> :调用在发出之后,不用等待返回结果,该调用直接返回。</li></ul><p>例子:</p><p>你不可以在水烧开之前喝水–同步</p><p>你可以在烧水的同时刷牙–异步</p><hr><h2 id="3-1-设计"><a href="#3-1-设计" class="headerlink" title="3.1 设计"></a>3.1 设计</h2><p>多线程可以让方法执行变为异步的(即不要巴巴干等着)比如说读取磁盘文件时,假设读取操作花费了 5 秒钟,如<br>果没有线程调度机制,这 5 秒 cpu 什么都做不了,其它代码都得暂停…</p><ul><li>比如在项目中,视频文件需要转换格式等操作比较费时,这时开一个新线程处理视频转换,避免阻塞主线程</li><li>tomcat 的异步 servlet 也是类似的目的,让用户线程处理耗时较长的操作,避免阻塞</li><li>tomcat 的工作线程 ui 程序中,开线程进行其他操作,避免阻塞 ui 线程</li></ul><hr><h2 id="3-2-结论"><a href="#3-2-结论" class="headerlink" title="3.2 结论"></a>3.2 结论</h2><ol><li>单核 cpu 下,多线程不能实际提高程序运行效率,只是为了能够在不同的任务之间切换,不同线程轮流使用 cpu ,不至于一个线程总占用 cpu,别的线程没法干活</li><li>多核 cpu 可以并行跑多个线程,但能否提高程序运行效率还是要分情况的<ul><li>有些任务,经过精心设计,将任务拆分,并行执行,当然可以提高程序的运行效率。但不是所有计算任 务都能拆分(参考后文的【阿姆达尔定律】)</li><li>也不是所有任务都需要拆分,任务的目的如果不同,谈拆分和效率没啥意义</li></ul></li><li>IO 操作不占用 cpu,只是我们一般拷贝文件使用的是【阻塞 IO】,这时相当于线程虽然不用 cpu,但需要一 直等待 IO 结束,没能充分利用线程。所以才有后面的【非阻塞 IO】和【异步 IO】优化</li></ol><hr><h1 id="4-Java线程"><a href="#4-Java线程" class="headerlink" title="4. Java线程"></a>4. Java线程</h1><h2 id="4-1-创建和运行线程"><a href="#4-1-创建和运行线程" class="headerlink" title="4.1 创建和运行线程"></a>4.1 创建和运行线程</h2><h3 id="4-1-1-方法一:直接使用Thread或者继承Thread"><a href="#4-1-1-方法一:直接使用Thread或者继承Thread" class="headerlink" title="4.1.1 方法一:直接使用Thread或者继承Thread"></a>4.1.1 方法一:直接使用Thread或者继承Thread</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CreateThread</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">Thread</span> <span class="hljs-variable">t</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(){<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span>{<br> System.out.println(<span class="hljs-string">"running"</span>);<br> }<br> };<br> <span class="hljs-comment">//设置线程名称</span><br> t.setName(<span class="hljs-string">"T1"</span>);<br> t.start();<br> System.out.println(<span class="hljs-string">"running"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><p>或者</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CreateThread</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">Thread</span> <span class="hljs-variable">myThread</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyThread</span>();<br> <span class="hljs-comment">// 启动线程</span><br> myThread.start();<br> }<br>}<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Thread</span>{<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> System.out.println(<span class="hljs-string">"my thread running..."</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><p><strong>Java不支持多继承,如果继承了Thread类,那么就不能再继承其他类。另外任务与代码没有分离,当多个线程执行一样的任务时需要多份任务代码</strong></p><hr><h3 id="4-1-2-方法二:-使用Runnable配合Thread或者实现实现Runnable接口"><a href="#4-1-2-方法二:-使用Runnable配合Thread或者实现实现Runnable接口" class="headerlink" title="4.1.2 方法二: 使用Runnable配合Thread或者实现实现Runnable接口"></a>4.1.2 方法二: 使用Runnable配合Thread或者实现实现Runnable接口</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test2</span> {<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><span class="hljs-comment">//创建线程任务</span><br><span class="hljs-type">Runnable</span> <span class="hljs-variable">r</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Runnable</span>() {<br><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br>System.out.println(<span class="hljs-string">"Runnable running"</span>);<br>}<br>};<br><span class="hljs-comment">//将Runnable对象传给Thread</span><br><span class="hljs-type">Thread</span> <span class="hljs-variable">t</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(r, <span class="hljs-string">"T2"</span>);<br><span class="hljs-comment">//启动线程</span><br>t.start();<br>}<br>}<br></code></pre></td></tr></table></figure><p>或者</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">CreateThread</span> {<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyRunnable</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> System.out.println(<span class="hljs-string">"my runnable running..."</span>);<br> }<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">MyRunnable</span> <span class="hljs-variable">myRunnable</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyRunnable</span>();<br> <span class="hljs-type">Thread</span> <span class="hljs-variable">thread</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(myRunnable);<br> thread.start();<br> }<br>}<br></code></pre></td></tr></table></figure><p>通过实现Runnable接口,并且实现run()方法。在创建线程时作为参数传入该类的实例即可</p><hr><h3 id="4-1-3-原理之-Thread-与-Runnable-的关系"><a href="#4-1-3-原理之-Thread-与-Runnable-的关系" class="headerlink" title="4.1.3 原理之 Thread 与 Runnable 的关系"></a>4.1.3 原理之 Thread 与 Runnable 的关系</h3><p><strong>小结</strong></p><ul><li>方法1 是把线程和任务合并在了一起</li><li>方法2 是把线程和任务分开了</li><li>用 Runnable 更容易与线程池等高级 API 配合 用 Runnable 让任务类脱离了 Thread 继承体系,更灵活</li></ul><hr><h3 id="4-1-4-方法三:FutureTask-配合-Thread或者实现Callable接口传参"><a href="#4-1-4-方法三:FutureTask-配合-Thread或者实现Callable接口传参" class="headerlink" title="4.1.4 方法三:FutureTask 配合 Thread或者实现Callable接口传参"></a>4.1.4 方法三:FutureTask 配合 Thread或者实现Callable接口传参</h3><p><strong>FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况(Runnable的run方法没有返回值)</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Test3</span> {<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> ExecutionException, InterruptedException {<br> <span class="hljs-comment">//需要传入一个Callable对象</span><br>FutureTask<Integer> task = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FutureTask</span><Integer>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Callable</span><Integer>() {<br><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> Integer <span class="hljs-title function_">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br>System.out.println(<span class="hljs-string">"线程执行!"</span>);<br>Thread.sleep(<span class="hljs-number">1000</span>);<br><span class="hljs-keyword">return</span> <span class="hljs-number">100</span>;<br>}<br>});<br><br><span class="hljs-type">Thread</span> <span class="hljs-variable">r1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(task, <span class="hljs-string">"t2"</span>);<br>r1.start();<br><span class="hljs-comment">//获取线程中方法执行后的返回结果</span><br>System.out.println(task.get());<br>}<br>}<br></code></pre></td></tr></table></figure><p>或者</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FutureTaskTest</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> ExecutionException, InterruptedException {<br> FutureTask<String> futureTask = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FutureTask</span><>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">MyCall</span>());<br> <span class="hljs-type">Thread</span> <span class="hljs-variable">thread</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(futureTask);<br> thread.start();<br> <span class="hljs-comment">// 获得线程运行后的返回值</span><br> System.out.println(futureTask.get());<br> }<br>}<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyCall</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Callable</span><String> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"hello world"</span>;<br> }<br>}<br></code></pre></td></tr></table></figure><hr><h3 id="4-1-5-总结"><a href="#4-1-5-总结" class="headerlink" title="4.1.5 总结"></a>4.1.5 总结</h3><p>使用<strong>继承方式的好处是方便传参</strong>,你可以在子类里面添加成员变量,通过set方法设置参数或者通过构造函数进行传递,而如果使用Runnable方式,则只能使用主线程里面被声明为final的变量。<strong>不好的地方是Java不支持多继承</strong>,如果继承了Thread类,那么子类不能再继承其他类,而Runable则没有这个限制。<strong>前两种方式都没办法拿到任务的返回结果,但是Futuretask方式可以</strong></p><hr><h2 id="4-2-原理之线程运行"><a href="#4-2-原理之线程运行" class="headerlink" title="4.2 原理之线程运行"></a>4.2 原理之线程运行</h2><h3 id="4-2-1-栈与栈帧"><a href="#4-2-1-栈与栈帧" class="headerlink" title="4.2.1 栈与栈帧"></a>4.2.1 栈与栈帧</h3><p>Java Virtual Machine Stacks (Java 虚拟机栈) 我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?</p><ul><li>其实就是线程,每个线程启动后,虚拟机就会为其分配一块<strong>栈内存</strong></li><li>每个栈由多个栈帧(Frame)组成,对应着每次<strong>方法调用时所占用的内存</strong></li><li>每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法</li><li>线程之间的栈内存都是私有的,独立的,互补干涉</li></ul><p><a href="https://blog.gutaicheng.top/2022/08/06/JVM%E5%AD%A6%E4%B9%A0/#1-2-%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88">jvm学习-虚拟机栈 - GuTaicheng’s Blog</a></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220907101411880.png" alt="image-20220907101411880"></p><hr><h3 id="4-2-2-线程上下文切换-Thread-Context-Switch"><a href="#4-2-2-线程上下文切换-Thread-Context-Switch" class="headerlink" title="4.2.2 线程上下文切换(Thread Context Switch)"></a>4.2.2 线程上下文切换(Thread Context Switch)</h3><p>因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码</p><ul><li>线程的 cpu 时间片用完</li><li>垃圾回收 有更高优先级的线程需要运行</li><li>线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法</li></ul><p>当 Context Switch 发生时,需要由操作系统<strong>保存当前线程的状态</strong>,并恢复另一个线程的状态,Java 中对应的概念 就是<strong>程序计数器</strong>(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的</p><ul><li>状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等</li><li>Context Switch 频繁发生会影响性能</li></ul><hr><h2 id="4-3-常用方法"><a href="#4-3-常用方法" class="headerlink" title="4.3 常用方法"></a>4.3 常用方法</h2><h3 id="4-3-1-start-与run"><a href="#4-3-1-start-与run" class="headerlink" title="4.3.1 start()与run()"></a>4.3.1 start()与run()</h3><p>直接调用线程中的run方法的话,可以执行run方法中的代码,但是<strong>实际上还是在主线程中运行</strong>,不是新建的线程中;</p><p>所以如果想要在所创建的线程中执行run方法,<strong>需要使用Thread对象的start方法。</strong></p><p><strong>调用 run:</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">Thread</span> <span class="hljs-variable">t1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(<span class="hljs-string">"t1"</span>) {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> log.debug(Thread.currentThread().getName());<br> FileReader.read(Constants.MP4_FULL_PATH);<br> }<br> };<br> t1.run();<br> log.debug(<span class="hljs-string">"do other things ..."</span>);<br>}<br><span class="hljs-comment">/**</span><br><span class="hljs-comment">19:39:14 [main] c.TestStart - main</span><br><span class="hljs-comment">19:39:14 [main] c.FileReader - read [1.mp4] start ...</span><br><span class="hljs-comment">19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms</span><br><span class="hljs-comment">19:39:18 [main] c.TestStart - do other things ...</span><br><span class="hljs-comment">t1.start();</span><br><span class="hljs-comment">**/</span><br></code></pre></td></tr></table></figure><p><strong>调用start</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//上代码的t1.run();替换即可</span><br>t1.start();<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment">19:41:30 [main] c.TestStart - do other things ...</span><br><span class="hljs-comment">19:41:30 [t1] c.TestStart - t1</span><br><span class="hljs-comment">19:41:30 [t1] c.FileReader - read [1.mp4] start ...</span><br><span class="hljs-comment">19:41:35 [t1] c.FileReader - read [1.mp4] end ... cost: 4542 ms</span><br><span class="hljs-comment">**/</span><br></code></pre></td></tr></table></figure><hr><h3 id="4-3-2-sleep-与yield"><a href="#4-3-2-sleep-与yield" class="headerlink" title="4.3.2 sleep()与yield()"></a>4.3.2 sleep()与yield()</h3><h4 id="sleep"><a href="#sleep" class="headerlink" title="sleep"></a>sleep</h4><ol><li><p>调用 sleep 会让当前线程从 <strong>Running 进入 Timed Waiting 状态(阻塞)</strong>,可通过state()方法查看</p></li><li><p>其它线程可以使用 <strong>interrupt</strong> 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException</p></li><li><p>睡眠结束后的线程未必会立刻得到执行</p></li><li><p>建议用 <strong>TimeUnit 的 sleep</strong> 代替 Thread 的 sleep 来获得更好的可读性 。如:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//休眠一秒</span><br>TimeUnit.SECONDS.sleep(<span class="hljs-number">1</span>);<br><span class="hljs-comment">//休眠一分钟</span><br>TimeUnit.MINUTES.sleep(<span class="hljs-number">1</span>);<br></code></pre></td></tr></table></figure></li></ol><hr><h4 id="yield-让出当前线程"><a href="#yield-让出当前线程" class="headerlink" title="yield(让出当前线程)"></a>yield(让出当前线程)</h4><ol><li>调用 yield 会让当前线程从 <strong>Running 进入 Runnable 就绪状态</strong>(仍然有可能被执行),然后调度执行其它线程</li><li>具体的实现依赖于<strong>操作系统的任务调度</strong></li></ol><blockquote><p>简单说:我不干了,你们比我牛逼你上,你们都不行我就继续</p></blockquote><p>阻塞:CUP不会去考虑执行</p><p>就绪:CUP优先考虑</p><hr><h4 id="线程优先级"><a href="#线程优先级" class="headerlink" title="线程优先级"></a>线程优先级</h4><ul><li>线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它</li><li>如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用</li><li>设置方法:</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">thread1.setPriority(Thread.MAX_PRIORITY); <span class="hljs-comment">//设置为优先级最高</span><br></code></pre></td></tr></table></figure><hr><h3 id="4-3-3-join方法"><a href="#4-3-3-join方法" class="headerlink" title="4.3.3 join方法"></a>4.3.3 join方法</h3><p>用于等待某个线程结束。哪个线程内调用join()方法,就等待哪个线程结束,然后再去执行其他线程。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-variable">r</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException {<br>test1();<br>}<br><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test1</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException {<br> log.debug(<span class="hljs-string">"开始"</span>);<br> <span class="hljs-type">Thread</span> <span class="hljs-variable">t1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(() -> {<br> log.debug(<span class="hljs-string">"开始"</span>);<br> sleep(<span class="hljs-number">1</span>);<br> log.debug(<span class="hljs-string">"结束"</span>);<br> r = <span class="hljs-number">10</span>;<br> });<br> t1.start();<br> <span class="hljs-comment">//t1.join();</span><br> log.debug(<span class="hljs-string">"结果为:{}"</span>, r);<br> log.debug(<span class="hljs-string">"结束"</span>);<br>}<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment">输出结果为 r = 0</span><br><span class="hljs-comment">因为主线程要立即打印 r 的值 这时t1 线程还没有结束,所以打印的为 0</span><br><span class="hljs-comment"></span><br><span class="hljs-comment">如果取消 t1.join()的注释,则打印结果为 r = 10 </span><br><span class="hljs-comment">**/</span><br></code></pre></td></tr></table></figure><p>可以应用于线程同步(需要结果)</p><p>多个线程同时运行并且join时,总等待时间是运行时间<strong>最长</strong>的线程的时间</p><p>join(long n)还可以携带参数,表示最多等待的时间,如果超过这个时间没有执行完就不等了,如果在这个时间内执行完了就直接跳过</p>]]></content>
<categories>
<category>Java</category>
<category>JUC</category>
</categories>
<tags>
<tag>JUC</tag>
<tag>并发</tag>
</tags>
</entry>
<entry>
<title>Redis学习</title>
<link href="/2022/08/15/Redis%E5%AD%A6%E4%B9%A0/"/>
<url>/2022/08/15/Redis%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><p>本博客是根据<a href="https://www.bilibili.com/video/BV1CJ411m7Gc"><strong>黑马程序员Redis入门到精通</strong></a>教学视频学习时,所做的笔记</p><hr><h1 id="1-Redis入门"><a href="#1-Redis入门" class="headerlink" title="1. Redis入门"></a>1. Redis入门</h1><h2 id="1-1-Redis简介"><a href="#1-1-Redis简介" class="headerlink" title="1.1 Redis简介"></a>1.1 Redis简介</h2><p><strong>高性能键值对(key-value)数据库</strong></p><hr><h2 id="1-2-Redis下载与安装"><a href="#1-2-Redis下载与安装" class="headerlink" title="1.2 Redis下载与安装"></a>1.2 Redis下载与安装</h2><h3 id="1-2-1-windows"><a href="#1-2-1-windows" class="headerlink" title="1.2.1 windows"></a>1.2.1 windows</h3><p>下载链接:<a href="https://github.com/microsoftarchive/redis/tags">Tags · microsoftarchive/redis (github.com)</a></p><p>解压即可</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815175728584.png" alt="image-20220815175728584"></p><hr><h3 id="1-2-2-linux"><a href="#1-2-2-linux" class="headerlink" title="1.2.2 linux"></a>1.2.2 linux</h3><ul><li>安装</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//下载安装包</span><br>wget http:<span class="hljs-comment">//download.redis.io/releases/redis-4.0.0.tar.gz</span><br><br><span class="hljs-comment">//解压</span><br>tar –xvf redis-<span class="hljs-number">4.0</span><span class="hljs-number">.0</span>.tar.gz<br><br><span class="hljs-comment">//进入解压后的目录</span><br>cd redis-<span class="hljs-number">4.0</span><span class="hljs-number">.0</span>.tar.gz<br><br><span class="hljs-comment">//编译安装</span><br>make install<br></code></pre></td></tr></table></figure><ul><li>服务端启动</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//默认启动 可以指定端口</span><br>redis-server<br>redis-server –-port <span class="hljs-number">6379</span><br>redis-server –-port <span class="hljs-number">6380</span><br><br><span class="hljs-comment">//配置文件启动</span><br>redis-server redis.conf<br> <br><span class="hljs-comment">/*************配置文件*******************/</span><br>port <span class="hljs-number">6379</span><br>daemonize yes <span class="hljs-comment">//守护进程方式启动</span><br>logfile <span class="hljs-string">"6379.log"</span> <span class="hljs-comment">//设定日志文件名称</span><br>dir /redis-<span class="hljs-number">4.0</span><span class="hljs-number">.0</span>/data <span class="hljs-comment">//自定义目录</span><br><span class="hljs-comment">/********************************/</span><br></code></pre></td></tr></table></figure><ul><li>客户端连接</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//默认连接;127.0.0.1 6379</span><br>redis-cli<br><br><span class="hljs-comment">//连接指定服务器</span><br>redis-cli -h <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><br>redis-cli –port <span class="hljs-number">6379</span><br>redis-cli -h <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span> –port <span class="hljs-number">6379</span><br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818165046688.png" alt="image-20220818165046688"></p><hr><h2 id="1-3-Redis常用指令"><a href="#1-3-Redis常用指令" class="headerlink" title="1.3 Redis常用指令"></a>1.3 Redis常用指令</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">//信息添加</span><br>set key value<br><br><span class="hljs-comment">//信息查询</span><br>get key<br><br><span class="hljs-comment">//清屏</span><br>clear<br><br><span class="hljs-comment">//退出</span><br>quit<br>exit<br><br><span class="hljs-comment">//帮助</span><br>help 命令名称<br>help @组名 <span class="hljs-comment">//tab快速切换</span><br></code></pre></td></tr></table></figure><p>示例</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815181101701.png" alt="image-20220815181101701"></p><hr><h1 id="2-Redis数据类型"><a href="#2-Redis数据类型" class="headerlink" title="2. Redis数据类型"></a>2. Redis数据类型</h1><ul><li>Redis 自身是一个 Map,其中所有的数据都是采用 key : value 的形式存储</li><li>数据类型指的是存储的数据的类型,也就是 value 部分的类型,<strong>key 部分永远都是字符串</strong></li></ul><hr><h2 id="2-1-String"><a href="#2-1-String" class="headerlink" title="2.1 String"></a>2.1 String</h2><ul><li>存储的数据:单个数据,最简单的数据存储类型,也是最常用的数据存储类型</li><li>存储数据的格式:一个存储空间保存一个数据</li><li>存储内容:通常使用字符串,如果字符串以整数的形式展示,可以作为数字操作使用</li></ul><hr><h3 id="2-1-1-基本操作"><a href="#2-1-1-基本操作" class="headerlink" title="2.1.1 基本操作"></a>2.1.1 基本操作</h3><ul><li><p>添加修改</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gams"><span class="hljs-keyword">set</span> key <span class="hljs-comment">value</span><br></code></pre></td></tr></table></figure></li><li><p>获取数据</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-built_in">get</span> <span class="hljs-built_in">key</span><br></code></pre></td></tr></table></figure></li><li><p>删除数据</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-built_in">del</span> <span class="hljs-built_in">key</span><br></code></pre></td></tr></table></figure></li><li><p>添加/修改多个数据</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gams"><span class="hljs-function"><span class="hljs-title">mset</span></span> key1 value1 key2 value2 key3 value3....<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815195054854.png" alt="image-20220815195054854"></p></li><li><p>获取多个数据</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gams"><span class="hljs-function"><span class="hljs-title">mget</span></span> key1 key2 key3...<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815195106373.png" alt="image-20220815195106373"></p></li><li><p>获取数据字符个数</p><figure class="highlight gauss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs gauss"><span class="hljs-built_in">strlen</span> <span class="hljs-built_in">key</span><br><span class="hljs-comment">//返回的是字符个数</span><br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815195210687.png" alt="image-20220815195210687"></p></li><li><p>追加信息到原始信息后部(如果存在就追加,否则新建)</p><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs processing"><span class="hljs-built_in">append</span> <span class="hljs-built_in">key</span> value<br><span class="hljs-comment">//返回的是追加后的字符个数</span><br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815195330828.png" alt="image-20220815195330828"></p></li></ul><hr><h3 id="2-1-2-扩展操作"><a href="#2-1-2-扩展操作" class="headerlink" title="2.1.2 扩展操作"></a>2.1.2 扩展操作</h3><figure class="highlight gauss"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs gauss"><span class="hljs-comment">//增长指令,只有当value为数字时才能增长</span><br>incr <span class="hljs-built_in">key</span> <br>incrby <span class="hljs-built_in">key</span> increment <br>incrbyfloat <span class="hljs-built_in">key</span> increment <br><br><span class="hljs-comment">//减少指令,有当value为数字时才能减少</span><br>decr <span class="hljs-built_in">key</span> <br>decrby <span class="hljs-built_in">key</span> increment<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815200053917.png" alt="image-20220815200053917"></p><ul><li>string在redis内部存储默认就是一个<strong>字符串</strong>,当遇到增减类操作incr,decr时会<strong>转成数值型</strong>进行计算。</li><li>redis所有的操作都是<strong>原子性</strong>的,采用<strong>单线程</strong>处理所有业务,命令是一个一个执行的,因此无需考虑并发带来的数据影响。</li><li>注意:按数值进行操作的数据,如果原始数据不能转成数值,或超越了redis 数值上限范围,将报错。 9223372036854775807(java中long型数据最大值,Long.MAX_VALUE)</li></ul><p><strong>tips:</strong></p><ul><li>redis用于控制数据库表主键id,为数据库表主键<strong>提供生成策略</strong>,保障数据库表的主键<strong>唯一性</strong></li><li>此方案适用于所有数据库,且支持数据库集群</li></ul><p><strong>设置数据具有指定的生命周期</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs awk"><span class="hljs-regexp">//</span>按秒设置数据指定的生命周期<br>setex key seconds value<br><br><span class="hljs-regexp">//</span>按毫秒设置<br>psetex key milliseconds value<br><br>value不重要<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815200654589.png" alt="image-20220815200654589"></p><p><strong>tips</strong></p><ul><li>redis 控制数据的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作</li></ul><hr><h3 id="2-1-3-命名规范"><a href="#2-1-3-命名规范" class="headerlink" title="2.1.3 命名规范"></a>2.1.3 命名规范</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815201703550.png" alt="image-20220815201703550"></p><p><strong>例如</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">set</span> user:<span class="hljs-built_in">id</span>:95001:age 22<br>//意思是 user表下 主键<span class="hljs-built_in">id</span>=95001 的age 设置为22<br></code></pre></td></tr></table></figure><hr><h2 id="2-2-Hash"><a href="#2-2-Hash" class="headerlink" title="2.2 Hash"></a>2.2 Hash</h2><ul><li>新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息</li><li>需要的存储结构:一个存储空间保存多个键值对数据</li><li>hash类型:底层使用哈希表结构实现数据存储</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815234452249.png" alt="image-20220815234452249"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815234505171.png" alt="image-20220815234505171"></p><p><strong>hash存储结构优化</strong></p><ul><li>如果field数量较少,存储结构优化为类数组结构</li><li>如果field数量较多,存储结构使用HashMap结构</li></ul><hr><h3 id="2-2-1-基本操作"><a href="#2-2-1-基本操作" class="headerlink" title="2.2.1 基本操作"></a>2.2.1 基本操作</h3><ul><li><p>添加/修改数据</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript">hset key field value <span class="hljs-comment">//插入(如果已存在同名的field,会被覆盖)</span><br>hsetnx key field value <span class="hljs-comment">//插入(如果已存在同名的field,不会被覆盖)</span><br></code></pre></td></tr></table></figure></li><li><p>获取数据</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript">hget key field <span class="hljs-comment">//获取指定field的数据</span><br>hgetall key <span class="hljs-comment">//获取指定key下的所有数据</span><br></code></pre></td></tr></table></figure></li><li><p>删除数据</p><figure class="highlight q"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs q"><span class="hljs-built_in">hdel</span> <span class="hljs-built_in">key</span> field1 [field2]<br></code></pre></td></tr></table></figure></li><li><p>添加/修改多个数据</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gams"><span class="hljs-function"><span class="hljs-title">hmset</span></span> key field1 value1 field2 value2 ...<br></code></pre></td></tr></table></figure></li><li><p>获取多个数据</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gams"><span class="hljs-function"><span class="hljs-title">hmget</span></span> key field1 field2 ...<br></code></pre></td></tr></table></figure></li><li><p>获取哈希表中字段的数量</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">hlen key</span><br></code></pre></td></tr></table></figure></li><li><p>获取哈希表中是否存在指定的字段</p><figure class="highlight armasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs armasm"><span class="hljs-symbol">hexists</span> key <span class="hljs-meta">field</span><br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220815235925350.png" alt="image-20220815235925350"></p></li></ul><hr><h3 id="2-2-2-操作注意事项"><a href="#2-2-2-操作注意事项" class="headerlink" title="2.2.2 操作注意事项"></a>2.2.2 操作注意事项</h3><ul><li>hash类型下的value<strong>只能存储字符串</strong>,不允许存储其他数据类型,<strong>不存在嵌套现象</strong>。如果数据未获取到, 对应的值为(nil)</li><li>每个 hash 可以存储 2^32 - 1 个键值</li><li>hash类型十分贴近对象的数据存储形式,并且可以灵活添加删除对象属性。但hash设计初衷不是为了存储大量对象而设计的,<strong>切记不可滥用</strong>,更<strong>不可以将hash作为对象列表使用</strong></li><li>hgetall 操作可以获取全部属性,如果内部field过多,遍历整体<strong>数据效率就很会低</strong>,有可能成为数据访问瓶颈</li></ul><h3 id="2-2-3-购物车场景的设计"><a href="#2-2-3-购物车场景的设计" class="headerlink" title="2.2.3 购物车场景的设计"></a>2.2.3 购物车场景的设计</h3><ul><li>每个用户的购物车中的商品记录保存成两条field</li><li>field专用于保存购买数量<ul><li>命名格式:商品id:nums</li><li>保存数据:数值</li></ul></li><li>field2专用于保存购物车中商品的信息hash的id</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816002529451.png" alt="image-20220816002529451"></p><hr><h2 id="2-3-List"><a href="#2-3-List" class="headerlink" title="2.3 List"></a>2.3 List</h2><ul><li>数据存储需求:存储多个数据,并对数据进入存储空间的<strong>顺序进行区分</strong></li><li>需要的存储结构:一个存储空间保存多个数据,且通过数据可以体现进入<strong>顺序</strong></li><li>list类型:保存多个数据,底层使用<strong>双向链表</strong>存储结构实现</li><li><strong>元素有序,且可重</strong></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816003057238.png" alt="image-20220816003057238"> </p><hr><h3 id="2-3-1-基本操作"><a href="#2-3-1-基本操作" class="headerlink" title="2.3.1 基本操作"></a>2.3.1 基本操作</h3><ul><li><p>添加/修改数据</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//lpush为从左边添加,rpush为从右边添加</span><br>lpush key value1 value2 value3...<br>rpush key value1 value2 value3...<br></code></pre></td></tr></table></figure></li><li><p>获取并移除数据</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">lpop key <span class="hljs-comment">//从左边移除</span><br>rpop key <span class="hljs-comment">//从右边移除</span><br></code></pre></td></tr></table></figure></li><li><p>获取数据</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//获取 从 start 到 stop 之间的数据,如果不知道list有多少个元素,stop的值可以为-1,代表倒数第一个元素</span><br>lrange key start stop<br><br><span class="hljs-comment">//获取 索引为 index 的数据 即类似a[0]</span><br>lindex key index<br><br><span class="hljs-comment">//获取list长度</span><br>llen key<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816004939734.png" alt="image-20220816004939734"></p></li></ul><hr><h3 id="2-3-2-扩展操作"><a href="#2-3-2-扩展操作" class="headerlink" title="2.3.2 扩展操作"></a>2.3.2 扩展操作</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//规定时间内获取并移除数据,b=block,给定一个时间,如果在指定时间内放入了元素,就移除</span><br>blpop key1 key2... timeout<br>brpop key1 key2... timeout<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816005707219.png" alt="image-20220816005707219"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//移除指定元素 count:移除的个数 value:移除的值。 移除多个相同元素时,从左边开始移除</span><br>lrem key count value<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816011027059.png" alt="image-20220816011027059"></p><h3 id="2-3-2-操作注意事项"><a href="#2-3-2-操作注意事项" class="headerlink" title="2.3.2 操作注意事项"></a>2.3.2 操作注意事项</h3><ul><li>list中保存的数据都是string类型的,数据总容量是有限的,最多2^32 - 1 个元素 (4294967295)。</li><li>list具有索引的概念,但是操作数据时通常以<strong>队列</strong>的形式进行入队出队(rpush, rpop)操作,或以<strong>栈</strong>的形式进行入栈出栈(lpush, lpop)操作</li><li>获取全部数据操作结束索引设置为-1 (倒数第一个元素)</li><li>list可以对数据进行分页操作,通常第一页的信息来自于list,第2页及更多的信息通过数据库的形式加载</li></ul><hr><h2 id="2-4-Set"><a href="#2-4-Set" class="headerlink" title="2.4 Set"></a>2.4 Set</h2><ul><li>新的存储需求:存储<strong>大量的数据</strong>,在查询方面提供更高的效率</li><li>需要的存储结构:能够保存<strong>大量的数据</strong>,高效的内部存储机制,便于查询</li><li>set类型:<strong>与hash存储结构完全相同,仅存储键,不存储值(nil),并且值是不允许重复的</strong></li><li><strong>不重复且无序</strong></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816165530111.png" alt="image-20220816165530111"></p><hr><h3 id="2-4-1-基本操作"><a href="#2-4-1-基本操作" class="headerlink" title="2.4.1 基本操作"></a>2.4.1 基本操作</h3><ul><li><p>添加元素</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">sadd key member1 member2...<br></code></pre></td></tr></table></figure></li><li><p>查看元素</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">smembers key<br></code></pre></td></tr></table></figure></li><li><p>移除元素</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">srem key member<br></code></pre></td></tr></table></figure></li><li><p>查看元素个数</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">scard key<br></code></pre></td></tr></table></figure></li><li><p>查看某个元素是否存在</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">sismember key member<br></code></pre></td></tr></table></figure></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816180224791.png" alt="image-20220816180224791"></p><hr><h3 id="2-4-2-扩展操作"><a href="#2-4-2-扩展操作" class="headerlink" title="2.4.2 扩展操作"></a>2.4.2 扩展操作</h3><ul><li><p>求两个集合的交、并、差集</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//交</span><br>sinter key1 [key2]<br><br><span class="hljs-comment">//并</span><br>sunion key1 [key2]<br><br><span class="hljs-comment">//差 key1 - key2 有顺序的</span><br>sdiff key1 [key2]<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816180725380.png" alt="image-20220816180725380"></p></li><li><p>求两个集合的交、并、差集并存储到指定集合中</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//交</span><br>sinterstore destination key1 key2...<br><br><span class="hljs-comment">//并</span><br>sunionstore destination key1 key2...<br><br><span class="hljs-comment">//差</span><br>sdiffstore destination key1 key2...<br><br><span class="hljs-comment">/**同上就不演示了**/</span><br></code></pre></td></tr></table></figure></li><li><p>将指定数据从原始集合中移动到目标集合中</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs awk"><span class="hljs-regexp">//</span>从 source 中 移到 destination里 移动的值为member<br>smove source destination member<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816181152078.png" alt="image-20220816181152078"></p></li><li><p>随机获取集合中指定数量的数据</p><figure class="highlight q"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs q"><span class="hljs-comment">//获取个数为 count</span><br>srandmember <span class="hljs-built_in">key</span> [<span class="hljs-built_in">count</span>]<br></code></pre></td></tr></table></figure></li><li><p>随机获取集合中的某个数据并将该数据移出集合</p><figure class="highlight q"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs q">spop <span class="hljs-built_in">key</span> [<span class="hljs-built_in">count</span>]<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816181500696.png" alt="image-20220816181500696"></p></li></ul><hr><h3 id="2-4-3-注意事项"><a href="#2-4-3-注意事项" class="headerlink" title="2.4.3 注意事项"></a>2.4.3 注意事项</h3><ul><li>set 类型不允许数据重复,如果添加的数据在 set 中已经存在,将只保留一份</li><li>set 虽然与hash的存储结构相同,但是无法启用hash中存储值的空间</li></ul><hr><h3 id="2-4-4-简单权限设置实现"><a href="#2-4-4-简单权限设置实现" class="headerlink" title="2.4.4 简单权限设置实现"></a>2.4.4 简单权限设置实现</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816182057465.png" alt="image-20220816182057465"></p><ul><li>权限1:查询所有、根据id查询</li><li>权限2:查询所有、根据id删除</li><li>用户001:具有权限1、2</li><li>用并集实现</li></ul><hr><h2 id="2-5-sorted-set"><a href="#2-5-sorted-set" class="headerlink" title="2.5 sorted_set"></a>2.5 sorted_set</h2><ul><li><strong>不重但有序(score)</strong></li><li>新的存储需求:数据排序有利于数据的有效展示,需要提供一种可以根据自身特征进行<strong>排序</strong>的方式</li><li>需要的存储结构:新的存储模型,可以保存<strong>可排序</strong>的数据</li><li>sorted_set类型:在set的存储结构基础上添加可排序字段</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816182503734.png" alt="image-20220816182503734"></p><hr><h3 id="2-5-1-基本操作"><a href="#2-5-1-基本操作" class="headerlink" title="2.5.1 基本操作"></a>2.5.1 基本操作</h3><ul><li><p>插入元素,需要指定 score 用于排序</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">zadd key score1 member1 [score2 member2]<br></code></pre></td></tr></table></figure></li><li><p>获取全部数据</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//正序 从 start 到 stop 当末尾添加withscore时,会将元素的score一起打印出来</span><br>zrange key start stop [WITHSCORES]<br><span class="hljs-comment">//反序</span><br>zrevrange key start stop [WITHSCORES]<br></code></pre></td></tr></table></figure></li><li><p>删除数据</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">zrem key member [member ...]<br></code></pre></td></tr></table></figure></li><li><p>按条件获取数据</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//正序 查询 key 中 score在[min , max]之间的数据 其中 limit 是从offset为索引开始位置,count为获取的数目</span><br>zrangebyscore key min max [WITHSCORES] [LIMIT offset count]<br><br><span class="hljs-comment">//反序</span><br>zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]<br></code></pre></td></tr></table></figure></li><li><p>按条件删除数据</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">zremrangebyrank key start stop<br>zremrangebyscore key min max<br></code></pre></td></tr></table></figure></li><li><p>获取集合数据总量</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//集合总个数</span><br>zcard key<br><br><span class="hljs-comment">//score 在min 到 max 范围的个数</span><br>zcount key min max<br></code></pre></td></tr></table></figure></li><li><p>集合交、并操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//交</span><br>zinterstore destination numkeys key [key ...]<br><br><span class="hljs-comment">//并</span><br>zunionstore destination numkeys key [key ...]<br></code></pre></td></tr></table></figure></li></ul><p><strong>注意</strong></p><ul><li>min与max用于限定搜索查询的<strong>条件</strong></li><li>start与stop用于限定<strong>查询范围</strong>,作用于索引,表示开始和结束索引</li><li>offset与count用于限定查询范围,作用于查询结果,表示<strong>开始位置</strong>和<strong>数据总量</strong></li></ul><hr><h3 id="2-5-2-扩展操作"><a href="#2-5-2-扩展操作" class="headerlink" title="2.5.2 扩展操作"></a>2.5.2 扩展操作</h3><ul><li><p>获取数据对应的索引(排名)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 注意是排名 不是具体的 socre </span><br><span class="hljs-comment">//比如说张三的score是100,李四是60</span><br>zrank key member <span class="hljs-comment">//正序 张三返回的是 0</span><br>zrevrank key member<span class="hljs-comment">//反序 张三返回的是1</span><br></code></pre></td></tr></table></figure></li><li><p>score值获取与修改</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//获取score</span><br>zscore key member<br><span class="hljs-comment">//修改</span><br>zincrby key increment member<br></code></pre></td></tr></table></figure></li></ul><hr><h3 id="2-5-3-注意事项"><a href="#2-5-3-注意事项" class="headerlink" title="2.5.3 注意事项"></a>2.5.3 注意事项</h3><ul><li>score保存的数据存储空间是64位,如果是整数范围是-9007199254740992~9007199254740992</li><li>score保存的数据也可以是一个双精度的double值,基于双精度浮点数的特征,<strong>可能会丢失精度</strong>,使用时候要<strong>慎重</strong></li><li>sorted_set 底层存储还是<strong>基于set</strong>结构的,因此数据<strong>不能重复</strong>,如果重复添加相同的数据,score值将被反复覆盖,<strong>保留最后一次</strong>修改的结果 (修改的返回值是0 )</li></ul><hr><h2 id="2-6-数据类型实践案例"><a href="#2-6-数据类型实践案例" class="headerlink" title="2.6 数据类型实践案例"></a>2.6 数据类型实践案例</h2><ul><li>redis用于控制数据库表主键id,为数据库表主键提供生成策略,保障数据库表的主键唯一性<ul><li>string类型,自增时默认转换成数值型计算,单线程,保证唯一性</li></ul></li><li>redis 控制数据的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作<ul><li>redis的时效性</li></ul></li><li>redis应用于各种结构型和非结构型高热度数据访问加速<ul><li>user:id:3506728370:fans</li></ul></li><li>redis 应用于购物车数据存储设计<ul><li>hash</li></ul></li><li>redis 应用于抢购,限购类、限量发放优惠卷、激活码等业务的数据存储设计<ul><li>hash</li></ul></li><li>redis 应用于具有操作先后顺序的数据控制<ul><li>list</li></ul></li><li>redis 应用于最新消息展示<ul><li>list</li></ul></li><li>redis 应用于随机推荐类信息检索,例如热点歌单推荐,热点新闻推荐,热卖旅游线路,应用APP推荐,大V推荐等<ul><li>set的随机获取集合指定数量的数据指令</li></ul></li><li>redis 应用于同类信息的关联搜索,二度关联搜索,深度关联搜索<ul><li>set的交并差等指令</li></ul></li><li>redis 应用于同类型不重复数据的合并、取交集操作<ul><li>set的交并差等指令</li></ul></li><li>redis 应用于同类型数据的快速去重<ul><li>set是无重复的集合</li></ul></li><li>redis 应用于基于黑名单与白名单设定的服务控制<ul><li>set</li></ul></li><li>redis 应用于计数器组合排序功能对应的排名<ul><li>sorted_set</li></ul></li><li>redis 应用于定时任务执行顺序管理或任务过期管理<ul><li>sorted_set</li></ul></li><li>redis 应用于及时任务/消息队列执行管理<ul><li>sorted_set 可设置用户优先级</li></ul></li><li>redis 应用于按次结算的服务控制<ul><li>string 利用incr操作超过最大值抛出异常的形式替代每次判断是否大于最大值</li></ul></li><li>redis 应用于基于时间顺序的数据操作,而不关注具体时间<ul><li>list的双端进出特征</li></ul></li></ul><hr><h1 id="3-通用指令"><a href="#3-通用指令" class="headerlink" title="3. 通用指令"></a>3. 通用指令</h1><h2 id="3-1-key通用指令"><a href="#3-1-key通用指令" class="headerlink" title="3.1 key通用指令"></a>3.1 key通用指令</h2><h3 id="3-1-1-基本操作"><a href="#3-1-1-基本操作" class="headerlink" title="3.1.1 基本操作"></a>3.1.1 基本操作</h3><ul><li><p>查看key是否存在</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs arduino">exists key<br></code></pre></td></tr></table></figure></li><li><p>删除key</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-built_in">del</span> <span class="hljs-built_in">key</span><br></code></pre></td></tr></table></figure></li><li><p>查看key‘的类型</p><figure class="highlight elm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs elm"><span class="hljs-keyword">type</span> key<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816233526230.png" alt="image-20220816233526230"></p></li></ul><hr><h3 id="3-1-2-扩展操作(时效性操作)"><a href="#3-1-2-扩展操作(时效性操作)" class="headerlink" title="3.1.2 扩展操作(时效性操作)"></a>3.1.2 扩展操作(时效性操作)</h3><ul><li><p>设置生命周期</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//秒</span><br>expire key seconds<br><span class="hljs-comment">//毫秒</span><br>pexpire key milliseconds<br></code></pre></td></tr></table></figure></li><li><p>查看有效时间, 如果有有效时间则返回剩余有效时间, 如果为永久有效,则返回-1, 如果Key不存在则返回-2</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//秒</span><br>ttl key<br><span class="hljs-comment">//毫秒</span><br>pttl key<br></code></pre></td></tr></table></figure></li><li><p>将有效的数据设置为永久有效</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">persist key</span><br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816234257716.png" alt="image-20220816234257716"></p></li></ul><hr><h3 id="3-1-3-扩展操作(查询操作)"><a href="#3-1-3-扩展操作(查询操作)" class="headerlink" title="3.1.3 扩展操作(查询操作)"></a>3.1.3 扩展操作(查询操作)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//根据key查询符合条件的数据</span><br>keys pattern<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220816234449916.png" alt="image-20220816234449916"></p><hr><h3 id="3-1-4-其他操作"><a href="#3-1-4-其他操作" class="headerlink" title="3.1.4 其他操作"></a>3.1.4 其他操作</h3><ul><li><p>为key改名</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//重命名key,为了避免覆盖已有数据,尽量少去修改已有key的名字,如果要使用最好使用renamenx</span><br><br>rename key newKey <span class="hljs-comment">//如果newkey已存在 则会覆盖</span><br>renamenx key newKey<span class="hljs-comment">//如果newkey已存在 则不会覆盖</span><br></code></pre></td></tr></table></figure></li><li><p>其他key通用操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">help <span class="hljs-meta">@generic</span><br></code></pre></td></tr></table></figure></li></ul><hr><h2 id="3-2-数据库通用指令"><a href="#3-2-数据库通用指令" class="headerlink" title="3.2 数据库通用指令"></a>3.2 数据库通用指令</h2><ul><li>redis为每个服务提供有16个数据库,编号从0到15</li><li>每个数据库之间的数据相互独立</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220817001316425.png" alt="image-20220817001316425"></p><hr><h3 id="3-2-1-db基本操作"><a href="#3-2-1-db基本操作" class="headerlink" title="3.2.1 db基本操作"></a>3.2.1 db基本操作</h3><ul><li><p>切换数据库</p><figure class="highlight axapta"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs axapta"><span class="hljs-keyword">select</span> <span class="hljs-keyword">index</span><br></code></pre></td></tr></table></figure></li><li><p>其他操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java">quit<span class="hljs-comment">//退出</span><br>ping<span class="hljs-comment">//测试连通性</span><br>echo message <span class="hljs-comment">//输出一个message</span><br></code></pre></td></tr></table></figure></li><li><p>数据移动</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//把当前数据库的 key 移到 db号 数据库中</span><br>move key db<br></code></pre></td></tr></table></figure></li><li><p>数据清除</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java">dbsize <span class="hljs-comment">//查询当前数据库有多少个key</span><br>flushdb <span class="hljs-comment">//清除当前数据库的数据</span><br>flushall <span class="hljs-comment">//清除所有数据库的所有数据</span><br></code></pre></td></tr></table></figure></li></ul><hr><h1 id="4-Jedis"><a href="#4-Jedis" class="headerlink" title="4. Jedis"></a>4. Jedis</h1><ul><li>Java用来连接Redis服务的工具</li><li>需要导入对应jar包或者Maven依赖</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>redis.clients<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>jedis<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>2.9.0<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h2 id="4-1-操作步骤"><a href="#4-1-操作步骤" class="headerlink" title="4.1 操作步骤"></a>4.1 操作步骤</h2><ul><li><p>连接Redis</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//参数为Redis所在的ip地址和端口号</span><br><span class="hljs-type">Jedis</span> <span class="hljs-variable">jedis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Jedis</span>(String host, <span class="hljs-type">int</span> port)<br></code></pre></td></tr></table></figure></li><li><p>操作Redis<strong>(操作redis的指令和redis本身的指令一致)</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">jedis.set(String key, String value);<br>jedis.hset(String key, String field, String value);<br></code></pre></td></tr></table></figure></li><li><p>断开连接</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">jedis.close();<br></code></pre></td></tr></table></figure></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RedisConn</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">//连接Redis</span><br> <span class="hljs-type">Jedis</span> <span class="hljs-variable">jedis</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Jedis</span>(<span class="hljs-string">"127.0.0.1"</span>, <span class="hljs-number">6379</span>);<br> <span class="hljs-comment">//操作Redis</span><br> jedis.set(<span class="hljs-string">"name"</span>, <span class="hljs-string">"GuTaicheng"</span>);<br><br> Map<String,String> map = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span><>();<br> map.put(<span class="hljs-string">"username"</span>, <span class="hljs-string">"GTC"</span>);<br> map.put(<span class="hljs-string">"sex"</span>, <span class="hljs-string">"BOY"</span>);<br> map.put(<span class="hljs-string">"age"</span>, <span class="hljs-string">"22"</span>);<br> jedis.hmset(<span class="hljs-string">"user:009"</span>,map);<br> System.out.println(<span class="hljs-string">"name==>"</span>+jedis.get(<span class="hljs-string">"name"</span>));<br> System.out.println(<span class="hljs-string">"user:009==>"</span>+jedis.hgetAll(<span class="hljs-string">"user:009"</span>).toString());<br> <span class="hljs-comment">//断开连接</span><br> jedis.close();<br> }<br>}<br><br><span class="hljs-comment">//输出</span><br>name==>GuTaicheng<br>user:009==>{age=<span class="hljs-number">22</span>, sex=BOY, username=GTC}<br></code></pre></td></tr></table></figure><hr><h2 id="4-2-Jedis工具类制作"><a href="#4-2-Jedis工具类制作" class="headerlink" title="4.2 Jedis工具类制作"></a>4.2 Jedis工具类制作</h2><ul><li><p>配置文件jedis.properties</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">jedis.host</span>=<span class="hljs-string">127.0.0.1</span><br><span class="hljs-attr">jedis.port</span>=<span class="hljs-string">6379</span><br><span class="hljs-attr">jedis.maxTotal</span>=<span class="hljs-string">30</span><br><span class="hljs-attr">jedis.maxIdle</span>=<span class="hljs-string">10</span><br></code></pre></td></tr></table></figure></li><li><p>静态代码块初始化资源,<strong>放在静态代码块的话,初始化就只会执行一次,不会创建多个Jedis连接</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span>{<br><span class="hljs-comment">//读取配置文件 获得参数值</span><br> <span class="hljs-type">ResourceBundle</span> <span class="hljs-variable">rb</span> <span class="hljs-operator">=</span> ResourceBundle.getBundle(<span class="hljs-string">"jedis"</span>);<br> host = rb.getString(<span class="hljs-string">"jedis.host"</span>);<br> port = Integer.parseInt(rb.getString(<span class="hljs-string">"jedis.port"</span>));<br> maxTotal = Integer.parseInt(rb.getString(<span class="hljs-string">"jedis.maxTotal"</span>));<br> maxIdle = Integer.parseInt(rb.getString(<span class="hljs-string">"jedis.maxIdle"</span>));<br> poolConfig = <span class="hljs-keyword">new</span> <span class="hljs-title class_">JedisPoolConfig</span>();<br> poolConfig.setMaxTotal(maxTotal);<br> poolConfig.setMaxIdle(maxIdle);<br> jedisPool = <span class="hljs-keyword">new</span> <span class="hljs-title class_">JedisPool</span>(poolConfig,host,port);<br>}<br></code></pre></td></tr></table></figure></li><li><p>获取连接;对外访问接口,提供jedis连接对象,连接从连接池获取</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//调用getJedis即可获得连接对象</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Jedis <span class="hljs-title function_">getJedis</span><span class="hljs-params">()</span>{<br> <span class="hljs-type">Jedis</span> <span class="hljs-variable">jedis</span> <span class="hljs-operator">=</span> jedisPool.getResource();<br> <span class="hljs-keyword">return</span> jedis;<br>}<br></code></pre></td></tr></table></figure></li></ul><blockquote><p>ps:这里是很好的单例模式,可以参考本站另外一篇博客:<a href="https://blog.gutaicheng.top/2022/08/06/JVM%E5%AD%A6%E4%B9%A0/#3-4-4-2-%E7%BB%83%E4%B9%A0%E4%BA%8C">jvm学习 - GuTaicheng’s Blog</a></p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JedisUtil</span> {<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">Jedis</span> <span class="hljs-variable">jedis</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">String</span> <span class="hljs-variable">host</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> port;<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> maxTotal;<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> maxIdle;<br> <span class="hljs-comment">//使用静态代码块,只加载一次</span><br> <span class="hljs-keyword">static</span> {<br> <span class="hljs-comment">//读取配置文件</span><br> <span class="hljs-type">ResourceBundle</span> <span class="hljs-variable">resourceBundle</span> <span class="hljs-operator">=</span> ResourceBundle.getBundle(<span class="hljs-string">"jedis"</span>);<br> <span class="hljs-comment">//获取配置文件中的数据</span><br> host = resourceBundle.getString(<span class="hljs-string">"jedis.host"</span>);<br> port = Integer.parseInt(resourceBundle.getString(<span class="hljs-string">"jedis.port"</span>));<br> <span class="hljs-comment">//读取最大连接数</span><br> maxTotal = Integer.parseInt(resourceBundle.getString(<span class="hljs-string">"jedis.maxTotal"</span>));<br> <span class="hljs-comment">//读取最大活跃数</span><br> maxIdle = Integer.parseInt(resourceBundle.getString(<span class="hljs-string">"jedis.maxIdle"</span>));<br> <span class="hljs-type">JedisPoolConfig</span> <span class="hljs-variable">jedisPoolConfig</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">JedisPoolConfig</span>();<br> jedisPoolConfig.setMaxTotal(maxTotal);<br> jedisPoolConfig.setMaxIdle(maxIdle);<br> <span class="hljs-comment">//获取连接池</span><br> <span class="hljs-type">JedisPool</span> <span class="hljs-variable">jedisPool</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">JedisPool</span>(jedisPoolConfig, host, port);<br> jedis = jedisPool.getResource();<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Jedis <span class="hljs-title function_">getJedis</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> jedis;<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//获取连接</span><br><span class="hljs-type">Jedis</span> <span class="hljs-variable">jedis</span> <span class="hljs-operator">=</span> JedisUtil.getJedis();<br></code></pre></td></tr></table></figure><h1 id="5-持久化"><a href="#5-持久化" class="headerlink" title="5. 持久化"></a>5. 持久化</h1><h2 id="5-1-持久化简介"><a href="#5-1-持久化简介" class="headerlink" title="5.1 持久化简介"></a>5.1 持久化简介</h2><h3 id="5-1-1-什么是持久化?"><a href="#5-1-1-什么是持久化?" class="headerlink" title="5.1.1 什么是持久化?"></a>5.1.1 什么是持久化?</h3><p>利用<strong>永久性</strong>存储介质(如硬盘)将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化。</p><h3 id="5-1-2-为什么要持久化"><a href="#5-1-2-为什么要持久化" class="headerlink" title="5.1.2 为什么要持久化"></a>5.1.2 为什么要持久化</h3><p><strong>防止</strong>数据的意外<strong>丢失</strong>,确保数据<strong>安全性</strong></p><h3 id="5-1-3-持久化过程保存什么"><a href="#5-1-3-持久化过程保存什么" class="headerlink" title="5.1.3 持久化过程保存什么"></a>5.1.3 持久化过程保存什么</h3><ul><li>将当前<strong>数据状态</strong>进行保存,<strong>快照</strong>形式,存储数据结果,存储格式简单,关注点在<strong>数据</strong></li><li>将数据的<strong>操作过程</strong>进行保存,<strong>日志</strong>形式,存储操作过程,存储格式复杂,关注点在数据的操作<strong>过程</strong></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818154415461.png" alt="image-20220818154415461.png"></p><hr><h2 id="5-2-RDB"><a href="#5-2-RDB" class="headerlink" title="5.2 RDB"></a>5.2 RDB</h2><h3 id="5-2-1-启动方式"><a href="#5-2-1-启动方式" class="headerlink" title="5.2.1 启动方式"></a>5.2.1 启动方式</h3><h4 id="5-2-1-1-save指令"><a href="#5-2-1-1-save指令" class="headerlink" title="5.2.1.1 save指令"></a>5.2.1.1 save指令</h4><ul><li><p>命令</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">save</span><br></code></pre></td></tr></table></figure></li><li><p>作用:<strong>手动</strong>执行一次保存操作</p></li></ul><h5 id="save指令相关配置"><a href="#save指令相关配置" class="headerlink" title="save指令相关配置"></a>save指令相关配置</h5><ul><li>dbfilename dump.rdb<ul><li>说明:设置本地数据库文件名,默认值为 dump.rdb</li><li>经验:通常设置为dump-端口号.rdb</li></ul></li><li>dir<ul><li>说明:设置存储.rdb文件的路径</li><li>经验:通常设置成存储空间较大的目录中,目录名称data</li></ul></li><li>rdbcompression yes<ul><li>说明:设置存储至本地数据库时是否压缩数据,默认为 yes,采用 LZF 压缩</li><li>经验:通常默认为开启状态,如果设置为no,可以节省 CPU 运行时间,但会使存储的文件变大(巨大)</li></ul></li><li>rdbchecksum yes<ul><li>说明:设置是否进行RDB文件格式校验,该校验过程在写文件和读文件过程均进行</li><li>经验:通常默认为开启状态,如果设置为no,可以节约读写性过程约10%时间消耗,但是存储一定的数据损坏风险</li></ul></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs xml">port 6379<br>daemonize yes<br>logfile "6379.log"<br>dir /redis-4.0.0/data<br>dbfilename dump-6379.rdb<br>rdbcompression yes<br>rdbchecksum yes<br></code></pre></td></tr></table></figure><hr><h5 id="save指令工作原理"><a href="#save指令工作原理" class="headerlink" title="save指令工作原理"></a>save指令工作原理</h5><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818165917101.png" alt="image-20220818165917101"></p><blockquote><p>save指令的执行会阻塞当前Redis服务器,直到当前RDB过程完成为止,有可能会造成<strong>长时间阻塞</strong>,线上环境不建议使用</p></blockquote><hr><h4 id="5-2-1-2-bgsave指令"><a href="#5-2-1-2-bgsave指令" class="headerlink" title="5.2.1.2 bgsave指令"></a>5.2.1.2 bgsave指令</h4><ul><li><p>命令</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">bgsave</span><br></code></pre></td></tr></table></figure></li><li><p>作用</p><p>手动启动后台保存操作,但<strong>不是立即执行</strong></p></li></ul><h5 id="bgsave指令相关配置"><a href="#bgsave指令相关配置" class="headerlink" title="bgsave指令相关配置"></a>bgsave指令相关配置</h5><ul><li>其余和save一样</li><li>stop-writes-on-bgsave-error yes<ul><li>说明:后台存储过程中如果出现错误现象,是否停止保存操作</li><li>经验:通常默认为开启状态</li></ul></li></ul><h5 id="bgsave指令工作原理"><a href="#bgsave指令工作原理" class="headerlink" title="bgsave指令工作原理"></a>bgsave指令工作原理</h5><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818170058246.png" alt="image-20220818170058246"></p><hr><h4 id="5-2-1-3-save配置(自动保存)"><a href="#5-2-1-3-save配置(自动保存)" class="headerlink" title="5.2.1.3 save配置(自动保存)"></a>5.2.1.3 save配置(自动保存)</h4><ul><li><p>配置</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs maxima"><span class="hljs-built_in">save</span> <span class="hljs-built_in">second</span> changes<br></code></pre></td></tr></table></figure></li><li><p>作用</p><p>满足<strong>限定时间</strong>范围内key的变化数量达到<strong>指定数量</strong>即进行持久化</p></li><li><p>参数</p><ul><li>second:监控时间范围</li><li>changes:监控key的变化量</li></ul></li><li><p>配置位置</p><p>在<strong>conf文件</strong>中进行配置</p></li></ul><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">port</span> <span class="hljs-number">6379</span><br><span class="hljs-attribute">daemonize</span> yes<br><span class="hljs-attribute">logfile</span> <span class="hljs-string">"6379.log"</span><br><span class="hljs-attribute">dir</span> /redis-<span class="hljs-number">4</span>.<span class="hljs-number">0</span>.<span class="hljs-number">0</span>/data<br><span class="hljs-attribute">dbfilename</span> dump-<span class="hljs-number">6379</span>.rdb<br><span class="hljs-attribute">rdbcompression</span> yes<br><span class="hljs-attribute">rdbchecksum</span> yes<br><span class="hljs-attribute">save</span> <span class="hljs-number">10</span> <span class="hljs-number">2</span> //意味着 <span class="hljs-number">10</span>s 内 如果有两个key发生变化 就自动保存<br></code></pre></td></tr></table></figure><h5 id="save配置原理"><a href="#save配置原理" class="headerlink" title="save配置原理"></a>save配置原理</h5><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818170613220.png" alt="image-20220818170613220"></p><p><strong>注意</strong>:</p><ul><li>save配置要根据实际业务情况进行设置,频度过高或过低都会出现性能问题,结果可能是灾难性的</li><li>save配置中对于second与changes设置通常具有<strong>互补对应</strong>关系(一个大一个小),尽量不要设置成包含性关系</li><li>save配置启动后执行的是<strong>bgsave操作</strong></li></ul><h3 id="5-2-2-三种启动方式对比"><a href="#5-2-2-三种启动方式对比" class="headerlink" title="5.2.2 三种启动方式对比"></a>5.2.2 三种启动方式对比</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818170802480.png" alt="image-20220818170802480"></p><p><strong>RBF优缺点</strong></p><ul><li>优点<ul><li>RDB是一个紧凑压缩的二进制文件,<strong>存储效率较高</strong></li><li>RDB内部存储的是redis在某个时间点的数据快照,非常适合用于<strong>数据备份,全量复制</strong>等场景</li><li>RDB恢复数据的<strong>速度</strong>要比AOF<strong>快</strong>很多</li><li>应用:服务器中每X小时执行bgsave备份,并将RDB文件拷贝到远程机器中,<strong>用于灾难恢复</strong></li></ul></li><li>缺点<ul><li>RDB方式无论是执行指令还是利用配置,<strong>无法做到实时持久化</strong>,具有较大的可能性丢失数据</li><li>bgsave指令每次运行要执行fork操作<strong>创建子进程</strong>,要<strong>牺牲</strong>掉一些<strong>性能</strong></li><li>Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式<strong>无法兼容</strong>现象</li></ul></li></ul><hr><h2 id="5-3-AOF"><a href="#5-3-AOF" class="headerlink" title="5.3 AOF"></a>5.3 AOF</h2><h3 id="5-3-1-AOF简介"><a href="#5-3-1-AOF简介" class="headerlink" title="5.3.1 AOF简介"></a>5.3.1 AOF简介</h3><ul><li>AOF(append only file)持久化:以独立日志的方式记录<strong>每次</strong>写命令,重启时再重新执行AOF文件中命令,以达到恢复数据的目的。与RDB相比可以简单描述为改记录数据为记录数据产生的过程</li><li>AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的<strong>主流</strong>方式</li></ul><hr><h3 id="5-3-2-AOF写数据过程"><a href="#5-3-2-AOF写数据过程" class="headerlink" title="5.3.2 AOF写数据过程"></a>5.3.2 AOF写数据过程</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818182539649.png" alt="image-20220818182539649"></p><hr><h3 id="5-3-3-AOF写数据三种策略-appendfsync"><a href="#5-3-3-AOF写数据三种策略-appendfsync" class="headerlink" title="5.3.3 AOF写数据三种策略(appendfsync)"></a>5.3.3 AOF写数据三种策略(appendfsync)</h3><ul><li><p>always</p><ul><li>每次写入操作均同步到AOF文件中,数据零误差,<strong>性能较低</strong>,<strong>不建议使用</strong></li></ul></li><li><p>everysec</p><ul><li>每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,<strong>性能较高</strong> ,<strong>建议使用</strong>,也是默认配置</li><li>在系统突然宕机的情况下丢失1秒内的数据</li></ul></li><li><p>no</p><ul><li>由操作系统控制每次同步到AOF文件的周期,整体过程<strong>不可控</strong></li></ul></li><li><p>配置</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-attribute">appendonly</span> <span class="hljs-literal">yes</span>|<span class="hljs-literal">no</span><br></code></pre></td></tr></table></figure><ul><li>作用</li><li>是否开启AOF持久化功能,<strong>默认为不开启状态</strong></li></ul></li><li><p>配置</p><figure class="highlight coq"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs coq">appendfsync always|<span class="hljs-type">everysec</span>|<span class="hljs-type">no</span><br></code></pre></td></tr></table></figure><ul><li>作用<ul><li>AOF写数据策略</li></ul></li></ul></li><li><p>配置</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">appendfilename filename</span><br></code></pre></td></tr></table></figure><ul><li>作用<ul><li>AOF持久化文件名,默认文件名未appendonly.aof,建议配置为appendonly-端口号.aof</li></ul></li></ul></li></ul><p>例:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs bash">port 6379<br>daemonize <span class="hljs-built_in">yes</span><br>logfile <span class="hljs-string">"6379.log"</span><br><span class="hljs-built_in">dir</span> /redis-4.0.0/data<br>dbfilename dump-6379.rdb<br>rdbcompression <span class="hljs-built_in">yes</span><br>rdbchecksum <span class="hljs-built_in">yes</span><br>appendonly <span class="hljs-built_in">yes</span><br>appendfsync always<br>appendfilename appendonly-6379.aof<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818183656537.png" alt="image-20220818183656537"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818183652737.png" alt="image-20220818183652737"></p><p>可以简单阅读懂aof</p><hr><h3 id="5-3-4-AOF重写"><a href="#5-3-4-AOF重写" class="headerlink" title="5.3.4 AOF重写"></a>5.3.4 AOF重写</h3><p>随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。AOF文件重<br>写是将Redis进程内的数据转化为写命令同步到新AOF文件的过程。简单说就是将对<strong>同一个数据的若干个条命令执行结</strong><br><strong>果转化成最终结果数据对应的指令进行记录。</strong></p><p>比如上例的:实际上的name是 ”CCC“ 但是在AOF中保存的三次 set 在恢复时也会执行三次,大大的降低了效率</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818183656537.png" alt="image-20220818183656537"></p><hr><h4 id="5-3-4-1-AOF重写作用"><a href="#5-3-4-1-AOF重写作用" class="headerlink" title="5.3.4.1 AOF重写作用"></a>5.3.4.1 AOF重写作用</h4><ul><li>降低磁盘占用量,提高磁盘利用率</li><li>提高持久化效率,降低持久化写时间,提高IO性能</li><li>降低数据恢复用时,提高数据恢复效率</li></ul><hr><h4 id="5-3-4-2-AOF重写规则"><a href="#5-3-4-2-AOF重写规则" class="headerlink" title="5.3.4.2 AOF重写规则"></a>5.3.4.2 AOF重写规则</h4><ul><li><p>进程内已超时的数据不再写入文件</p></li><li><p>忽略<strong>无效指令</strong>重写时使用进程内数据直接生成,这样新的AOF文件</p><p>只保留最终数据的写入命令</p><ul><li>如del key1、 hdel key2、srem key3、set key4 111、set key4 222等</li></ul></li><li><p>对同一数据的多条写命令合并为一条命令</p><ul><li><strong>如lpush list1 a、lpush list1 b、 lpush list1 c 可以转化为:lpush list1 a b c</strong></li><li>为防止数据量过大造成客户端缓冲区溢出,对list、set、hash、zset等类型,每条指令最多写入64个元素</li></ul></li></ul><hr><h4 id="5-3-4-3-AOF重写方式"><a href="#5-3-4-3-AOF重写方式" class="headerlink" title="5.3.4.3 AOF重写方式"></a>5.3.4.3 AOF重写方式</h4><h5 id="手动重写"><a href="#手动重写" class="headerlink" title="手动重写"></a>手动重写</h5><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">bgrewriteaof</span><br></code></pre></td></tr></table></figure><p><strong>简单演示</strong></p><ul><li><p>初始设置</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818184714946.png" alt="image-20220818184714946"></p></li><li><p>重写前</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818184830170.png" alt="image-20220818184830170"></p></li><li><p>重写后</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818184907371.png" alt="image-20220818184907371"></p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818184940177.png" alt="image-20220818184940177"></p><p><strong>bgrewriteaof指令工作原理</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818185119976.png" alt="image-20220818185119976"></p><hr><h5 id="自动重写"><a href="#自动重写" class="headerlink" title="自动重写"></a>自动重写</h5><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs arduino"><span class="hljs-comment">//触发重写的最小大小</span><br><span class="hljs-keyword">auto</span>-aof-rewrite-min-size size <br><span class="hljs-comment">//触发重写须达到的最小百分比</span><br><span class="hljs-keyword">auto</span>-aof-rewrite-percentage percentage<br></code></pre></td></tr></table></figure><ul><li><p>自动重写触发比对参数( 运行指令info Persistence获取具体信息 )</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs awk"><span class="hljs-regexp">//</span>当前.aof的文件大小<br>aof_current_size <br><span class="hljs-regexp">//</span>基础文件大小<br>aof_base_size<br></code></pre></td></tr></table></figure></li><li><p>自动重写触发条件</p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220818184433543.png" alt="image-20220818184433543"></p><hr><h4 id="5-3-4-4-AOF重写工作原理"><a href="#5-3-4-4-AOF重写工作原理" class="headerlink" title="5.3.4.4 AOF重写工作原理"></a>5.3.4.4 AOF重写工作原理</h4><ul><li><p>重写之前的AOF流程</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819004024975.png" alt="image-20220819004024975"></p></li><li><p>基于everysec开启重写后,会有一个重写缓冲区,提示信息是控制台上提示的重写在后台已开始……</p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819003732083.png" alt="image-20220819003732083"></p><ul><li>是由<strong>aof重写缓冲区</strong>来提供数据进行重写</li></ul><hr><h3 id="5-4-RDB-VS-AOF"><a href="#5-4-RDB-VS-AOF" class="headerlink" title="5.4 RDB VS AOF"></a>5.4 RDB VS AOF</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819004257826.png" alt="image-20220819004257826"></p><h4 id="5-4-1-RDB与AOF的选择之惑"><a href="#5-4-1-RDB与AOF的选择之惑" class="headerlink" title="5.4.1 RDB与AOF的选择之惑"></a>5.4.1 RDB与AOF的选择之惑</h4><ul><li><p>对数据<strong>非常敏感</strong>建议使用默认的<strong>AOF</strong>持久化方案</p><ul><li>AOF持久化策略使用<strong>everysecond</strong>,每秒钟fsync一次。该策略redis仍可以保持很好的处理性能,当出现问题时,最多丢失0-1秒内的数据。</li></ul></li><li><p>注意:由于AOF文件<strong>存储体积较大</strong>,且<strong>恢复速度较慢</strong></p></li><li><p>数据呈现<strong>阶段有效性</strong>建议使用RDB持久化方案</p><ul><li>数据可以良好的做到阶段内无丢失(该阶段是开发者或运维人员手工维护的),且<strong>恢复速度较快</strong>,阶段 点数据恢复通常采用RDB方案</li></ul></li><li><p>注意:利用RDB实现紧凑的数据持久化会使Redis降的很低</p></li><li><p>综合比对</p><ul><li>RDB与AOF的选择实际上是在做一种权衡,每种都有利有弊</li><li>如不能承受数分钟以内的数据丢失,对业务数据非常<strong>敏感</strong>,选用<strong>AOF</strong></li><li>如能承受数分钟以内的数据丢失,且追求大数据集的<strong>恢复速度</strong>,选用<strong>RDB</strong></li><li><strong>灾难恢复选用RDB</strong></li><li>双保险策略,同时开启 RDB 和 AOF,重启后,Redis优先使用 AOF 来恢复数据,降低丢失数据</li></ul></li></ul><hr><h1 id="6-事务"><a href="#6-事务" class="headerlink" title="6. 事务"></a>6. 事务</h1><h2 id="6-1-事务简介"><a href="#6-1-事务简介" class="headerlink" title="6.1 事务简介"></a>6.1 事务简介</h2><p>redis事务就是一个命令执行的队列,将一系列预定义命令包装成一个整体(一个队列)。当执行时,一次性<br>按照添加顺序依次执行,中间不会被打断或者干扰。</p><p>一个队列中,一次性、顺序性、排他性的执行一系列命令</p><p>比如说同时有两个线程,线程一设置了一个string=a,当线程一要读取这个key时,线程二修改了这个值,则导致<strong>线程一读取到和预想值不同的值(类似mysql的读脏数据)</strong>,因此需要有事务;</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819012549078.png" alt="image-20220819012549078"></p><hr><h2 id="6-2-事务的基本操作"><a href="#6-2-事务的基本操作" class="headerlink" title="6.2 事务的基本操作"></a>6.2 事务的基本操作</h2><ul><li><p>开启事务</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">multi</span><br></code></pre></td></tr></table></figure><ul><li>作用<ul><li>作设定事务的开启位置,此指令执行后,后续的所有指令均加入到事务中</li></ul></li></ul></li><li><p>取消事务</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">discard</span><br></code></pre></td></tr></table></figure><ul><li>作用<ul><li>终止当前事务的定义,发生在multi之后,exec之前</li></ul></li></ul></li><li><p>执行事务</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">exec</span><br></code></pre></td></tr></table></figure><ul><li>作用<ul><li>设定事务的结束位置,同时执行事务。<strong>与multi成对出现</strong>,成对使用</li></ul></li></ul></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819174736832.png" alt="image-20220819174736832"></p><p><strong>注意:加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令才开始执行</strong></p><hr><h2 id="6-3-事务的工作流程"><a href="#6-3-事务的工作流程" class="headerlink" title="6.3 事务的工作流程"></a>6.3 事务的工作流程</h2><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819175023482.png" alt="image-20220819175023482"></p><hr><h2 id="6-4-事务的注意事项"><a href="#6-4-事务的注意事项" class="headerlink" title="6.4 事务的注意事项"></a>6.4 事务的注意事项</h2><p><strong>定义事务的过程中,命令格式输入错误怎么办?</strong></p><ul><li>语法错误<ul><li>指命令书写格式有误 例如执行了一条不存在的指令</li></ul></li><li>处理结果<ul><li>如果定义的事务中所包含的命令存在语法错误,整体事务中<strong>所有命令均不会执行</strong>。包括那些语法正确的命令</li></ul></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819175516068.png" alt="image-20220819175516068"></p><p><strong>定义事务的过程中,命令执行出现错误怎么办?</strong></p><ul><li>运行错误<ul><li>指命令<strong>格式正确</strong>,但是<strong>无法正确的执行</strong>。例如对list进行incr操作</li></ul></li><li>处理结果<ul><li>能够正确运行的命令会执行,运行错误的命令不会被执行</li></ul></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819175705795.png" alt="image-20220819175705795"></p><p><strong>注意</strong>:已经执行完毕的命令对应的数据<strong>不会自动回滚</strong>,需要程序员自己在代码中实现回滚。</p><ul><li>Redis 命令只会因为错误的语法而失败,或是命令用在了错误类型的键上面,这些问题不能在入队时发现,这也就是说,从实用性的角度来说,<strong>失败的命令是由编程错误造成的</strong>,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中.</li><li>因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。</li></ul><hr><h2 id="6-5-基于特定条件的事务执行"><a href="#6-5-基于特定条件的事务执行" class="headerlink" title="6.5 基于特定条件的事务执行"></a>6.5 基于特定条件的事务执行</h2><h3 id="6-5-1-锁"><a href="#6-5-1-锁" class="headerlink" title="6.5.1 锁"></a>6.5.1 锁</h3><ul><li><p>对 key 添加监视锁,在执行exec前如果key发生了变化,终止事务执行</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gams"><span class="hljs-function"><span class="hljs-title">watch</span></span> key1, key2....<br></code></pre></td></tr></table></figure></li><li><p>取消对<strong>所有</strong>key的监视</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">unwatch</span><br></code></pre></td></tr></table></figure></li><li><p>事务内部 不能执行watch和unwatch,这两个命令在事务外部才有效</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819180550512.png" alt="image-20220819180550512"></p></li></ul><hr><h3 id="6-5-2-分布式锁"><a href="#6-5-2-分布式锁" class="headerlink" title="6.5.2 分布式锁"></a>6.5.2 分布式锁</h3><p>分布式锁其实是一个设计概念,也可以说是一个约定,不是redis自带的,原理是利用 <strong>setnx的不重性</strong>来约定实现的,就是个string类型</p><ul><li><p>使用 setnx 设置一个公共锁</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs awk"><span class="hljs-regexp">//</span>上锁<br>setnx lock-key value<br><span class="hljs-regexp">//</span>释放锁<br>del lock-key<br></code></pre></td></tr></table></figure><ul><li>利用setnx命令的返回值特征,有值(被上锁了)则返回设置失败,无值(没被上锁)则返回设置成功</li><li>操作完毕通过del操作释放锁</li></ul></li><li><p>这个 <strong>lock-key</strong> 就是自定义的一个约定规范,一般是lock-(要锁的key值)</p></li><li><p>实际上就是判断 lock-(要锁的key值) 是否存在,存在返回为0,后台接收到后就不执行,不存在就返回为1,后台接收到继续执行</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819181520608.png" alt="image-20220819181520608"></p></li></ul><hr><h3 id="6-5-3-分布式锁加强"><a href="#6-5-3-分布式锁加强" class="headerlink" title="6.5.3 分布式锁加强"></a>6.5.3 分布式锁加强</h3><ul><li><p>使用 expire 为锁key添加<strong>时间限定</strong>,到时如果不释放,放弃锁</p><figure class="highlight cos"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cos">expire <span class="hljs-keyword">lock</span>-key seconds<br>pexpire <span class="hljs-keyword">lock</span>-key milliseconds<br></code></pre></td></tr></table></figure></li><li><p>由于操作通常都是微秒或毫秒级,因此该锁定时间<strong>不宜设置过大</strong>。具体时间需要业务测试后确认。</p><ul><li>例如:持有锁的操作最长执行时间127ms,最短执行时间7ms。</li><li>测试百万次最长执行时间对应命令的最大耗时,测试百万次网络延迟平均耗时</li><li>锁时间设定推荐:最大耗时<em>120%+平均网络延迟</em>110%</li><li>如果业务最大耗时<<网络平均延迟,通常为2个数量级,取其中单个耗时较长即可</li></ul></li></ul><hr><h1 id="7-删除策略"><a href="#7-删除策略" class="headerlink" title="7. 删除策略"></a>7. 删除策略</h1><h2 id="7-1-过期数据"><a href="#7-1-过期数据" class="headerlink" title="7.1 过期数据"></a>7.1 过期数据</h2><ul><li>Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态<ul><li>XX :具有时效性的数据</li><li>-1 :永久有效的数据</li><li>-2 :<strong>已经过期的数据</strong> 或 被删除的数据 或 未定义的数据</li></ul></li></ul><hr><h2 id="7-2-数据删除策略"><a href="#7-2-数据删除策略" class="headerlink" title="7.2 数据删除策略"></a>7.2 数据删除策略</h2><ul><li>定时删除</li><li>惰性删除</li><li>定期删除</li></ul><p><strong>存储结构</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819193719942.png" alt="image-20220819193719942"></p><p>redis中有两个字典,一个是键值对字典,一个是过期字典,保存键的过期时间</p><p><strong>目标</strong></p><p>在内存占用与CPU占用之间寻找一种<strong>平衡</strong>,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕机或<br>内存泄露</p><hr><h3 id="7-2-1-定时删除"><a href="#7-2-1-定时删除" class="headerlink" title="7.2.1 定时删除"></a>7.2.1 定时删除</h3><ul><li>在设置某个key 的过期时间同时,创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务<strong>立即执行</strong>对键的删除操作</li><li>优点:<strong>节约内存</strong>,到时就删除,快速释放掉不必要的内存占用</li><li>缺点:<strong>CPU压力很大</strong>,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量</li><li>总结:用处理器性能换取存储空间 (<strong>拿时间换空间</strong>)</li></ul><hr><h3 id="7-2-2-惰性删除"><a href="#7-2-2-惰性删除" class="headerlink" title="7.2.2 惰性删除"></a>7.2.2 惰性删除</h3><ul><li>数据到达过期时间,不做处理。等下次访问该数据时<ul><li>如果未过期,返回数据</li><li>发现已过期,删除,返回不存在</li></ul></li><li>优点:<strong>节约CPU性能</strong>,发现必须删除的时候才删除</li><li>缺点:<strong>内存压力很大</strong>,出现长期占用内存的数据</li><li>总结:用存储空间换取处理器性能 (拿空间换时间)</li></ul><hr><h3 id="7-2-3-定期删除"><a href="#7-2-3-定期删除" class="headerlink" title="7.2.3 定期删除"></a>7.2.3 定期删除</h3><ul><li><p>周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度</p></li><li><p>特点1:CPU性能占用设置有峰值,检测频度可自定义设置</p></li><li><p>特点2:内存压力不是很大,长期占用内存的冷数据会被持续清理</p></li><li><p>优点:可以通过限制删除操作执行的时长和频率来减少删除操作对 CPU 的影响。另外定期删除,也能有效释放过期键占用的内存。</p></li><li><p>缺点:难以确定删除操作执行的时长和频率。如果执行的太频繁,定期删除策略变得和定时删除策略一样,对CPU不友好。如果执行的太少,那又和惰性删除一样了,过期键占用的内存不会及时得到释放。另外最重要的是,在获取某个键时,如果某个键的过期时间已经到了,但是还没执行定期删除,那么就会返回这个键的值,这是业务不能忍受的错误。</p></li><li><p>总结:周期性抽查存储空间 (随机抽查,重点抽查)</p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819194542232.png" alt="image-20220819194542232"></p><ul><li>如果删除的key大于 W的四分之一,说明是重点区域,current_db不变,下次继续该区域</li><li>反之,current_db + 1</li></ul><hr><h3 id="7-2-4-删除策略对比"><a href="#7-2-4-删除策略对比" class="headerlink" title="7.2.4 删除策略对比"></a>7.2.4 删除策略对比</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819194801599.png" alt="image-20220819194801599"></p><hr><h2 id="7-3-逐出算法-淘汰策略"><a href="#7-3-逐出算法-淘汰策略" class="headerlink" title="7.3 逐出算法(淘汰策略)"></a>7.3 逐出算法(淘汰策略)</h2><p>如果现在所有的数据都是永久的,那新数据进来就内存不足了,这个适合就要有逐出算法(淘汰策略)</p><ul><li>Redis使用内存存储数据,在执行每一个命令前,会调用<strong>freeMemoryIfNeeded()<strong>检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为</strong>逐出算法</strong></li><li><strong>注意</strong>:逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。当对所有数据尝试完毕后,如果不能达到内存清理的要求,将出现错误信息。</li></ul><h3 id="7-3-1-影响数据逐出的相关配置"><a href="#7-3-1-影响数据逐出的相关配置" class="headerlink" title="7.3.1 影响数据逐出的相关配置"></a>7.3.1 影响数据逐出的相关配置</h3><ul><li><p>最大可使用内存</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">maxmemory</span> <br></code></pre></td></tr></table></figure><p>占用物理内存的比例,默认值为0,表示不限制。生产环境中根据需求设定,通常设置在50%以上。</p></li><li><p>每次选取待删除数据的个数</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">maxmemory-samples</span> <br></code></pre></td></tr></table></figure><p>选取数据时并不会全库扫描,导致严重的性能消耗,降低读写性能。因此采用随机获取数据的方式作为待检测删除数据</p></li><li><p>删除策略</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cmake">maxmemory-<span class="hljs-keyword">policy</span> <br></code></pre></td></tr></table></figure><p>达到最大内存后的,对被挑选出来的数据进行删除的策略</p></li></ul><hr><h3 id="7-3-2-逐出算法的删除策略"><a href="#7-3-2-逐出算法的删除策略" class="headerlink" title="7.3.2 逐出算法的删除策略"></a>7.3.2 逐出算法的删除策略</h3><ul><li>检测易失数据(可能会过期的数据集server.db[i].expires)<ul><li>volatile-lru:挑选<strong>最近最少使用的</strong>数据淘汰,<strong>就是很久没用的</strong></li><li>volatile-lfu:挑选<strong>最近使用次数最少</strong>的数据淘汰,<strong>就是用的很少的</strong></li><li>volatile-ttl:挑选将要过期的数据淘汰</li><li>volatile-random:任意选择数据淘汰</li></ul></li><li>检测全库数据(所有数据集server.db[i].dict )<ul><li>allkeys-lru:挑选<strong>最近最少</strong>使用的数据淘汰,就是很久没用的</li><li>allkeys-lfu:挑选<strong>最近使用次数最少</strong>的数据淘汰,就是用的很少的</li><li>allkeys-random:任意选择数据淘汰</li></ul></li><li>放弃数据驱逐<ul><li>no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发错误OOM(Out Of Memory)</li></ul></li></ul><blockquote><p>LRU:最近使用 Least Recently Used</p><p>LFU(Least Frequently Used)算法,也就是最频繁被访问的数据将来最有可能被访问到</p></blockquote><p>内存淘汰策略可以通过配置文件来修改,Redis.conf对应的配置项是maxmemory-policy 修改对应的值就行,默认是noeviction。</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">maxmemory-<span class="hljs-keyword">policy</span> <span class="hljs-keyword">volatile</span>-lru<br></code></pre></td></tr></table></figure><hr><h1 id="8-redis-conf配置"><a href="#8-redis-conf配置" class="headerlink" title="8. redis.conf配置"></a>8. redis.conf配置</h1><p>参杂在每个章节,需要哪个功能配置哪个</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs markdown">/<span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-emphasis">*配置文件<span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">***/</span></span><br><span class="hljs-strong"><span class="hljs-emphasis">port 6379</span></span><br><span class="hljs-strong"><span class="hljs-emphasis">daemonize yes //守护进程方式启动</span></span><br><span class="hljs-strong"><span class="hljs-emphasis">logfile "6379.log" //设定日志文件名称</span></span><br><span class="hljs-strong"><span class="hljs-emphasis">dir /redis-4.0.0/data //自定义目录</span></span><br><span class="hljs-strong"><span class="hljs-emphasis">/**</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">****</span><span class="hljs-strong">**/</span></span><br></code></pre></td></tr></table></figure><hr><h1 id="9-高级数据类型"><a href="#9-高级数据类型" class="headerlink" title="9. 高级数据类型"></a>9. 高级数据类型</h1><h2 id="9-1-Bitmaps"><a href="#9-1-Bitmaps" class="headerlink" title="9.1 Bitmaps"></a>9.1 Bitmaps</h2><p>对于一个只有是或者不是的状态,可以采用0/1表示;比如:男生用1表示,女士用0表示;是为党员用1表示,不是党员用0表示等等</p><p>因此一个bit大小的数据,就可以保存8个状态,极大的节约了空间。</p><p>实际上还是string,只不过操作的是二进制上某一位的状态,比如:有100个员工打卡,第99位来打卡时,把第99位的0改位1。</p><hr><h3 id="9-1-1-基础操作"><a href="#9-1-1-基础操作" class="headerlink" title="9.1.1 基础操作"></a>9.1.1 基础操作</h3><ul><li><p>获取指定key对应偏移量上的bit值</p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs applescript">getbit key <span class="hljs-built_in">offset</span><br></code></pre></td></tr></table></figure></li><li><p>设置指定key对应偏移量上的bit值,value只能是1或0</p><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs applescript">setbit key <span class="hljs-built_in">offset</span> value<br></code></pre></td></tr></table></figure></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220819235124226.png" alt="image-20220819235124226"></p><hr><h3 id="9-1-2-扩展操作"><a href="#9-1-2-扩展操作" class="headerlink" title="9.1.2 扩展操作"></a>9.1.2 扩展操作</h3><ul><li><p>对指定key按位进行交、并、非、异或操作,并将结果<strong>保存到destKey</strong>中</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs awk">bitop op destKey key1 [key2...]<br><span class="hljs-regexp">//</span>op如下<br></code></pre></td></tr></table></figure><ul><li>and:交</li><li>or:并</li><li>not:非</li><li>xor:异或</li></ul></li><li><p>统计指定key中1的数量</p><figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xquery">bitcount<span class="hljs-built_in"> key</span> [<span class="hljs-keyword">start</span> <span class="hljs-keyword">end</span>]<br></code></pre></td></tr></table></figure></li></ul><p>可以实现统计8月19号到8月30号中间没有打卡的人数.</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820000145969.png" alt="image-20220820000145969"></p><ul><li><p>从没打卡过:用 or</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820000226499.png" alt="image-20220820000226499"></p><p>5 - 4 = 1,一个人从没打过</p></li><li><p>只要有一次没打卡:用 and</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820000349282.png" alt="image-20220820000349282"></p><p>5 - 2 = 3,有三个人有一次没打</p></li></ul><hr><h2 id="9-2-HyperLogLog"><a href="#9-2-HyperLogLog" class="headerlink" title="9.2 HyperLogLog"></a>9.2 HyperLogLog</h2><h3 id="9-2-1-基数"><a href="#9-2-1-基数" class="headerlink" title="9.2.1 基数"></a>9.2.1 基数</h3><ul><li>基数是数据集<strong>去重后元素个数</strong></li><li>HyperLogLog 是用来做基数统计的,运用了LogLog的算法</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820000847462.png" alt="image-20220820000847462"></p><hr><h3 id="9-2-2-基础操作"><a href="#9-2-2-基础操作" class="headerlink" title="9.2.2 基础操作"></a>9.2.2 基础操作</h3><ul><li><p>添加数据</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gams"><span class="hljs-function"><span class="hljs-title">pfadd</span></span> key element1, element2...<br></code></pre></td></tr></table></figure></li><li><p>统计数据</p><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gams"><span class="hljs-function"><span class="hljs-title">pfcount</span></span> key1 key2....<br></code></pre></td></tr></table></figure></li><li><p>合并数据(就是合并多个HyperLogLog到 destkey中)</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">pfmerge</span> destkey sourcekey<span class="hljs-meta"> [sourcekey...]</span><br></code></pre></td></tr></table></figure></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820001110016.png" alt="image-20220820001110016"></p><hr><h3 id="9-2-3-注意事项"><a href="#9-2-3-注意事项" class="headerlink" title="9.2.3 注意事项"></a>9.2.3 注意事项</h3><ul><li>用于进行基数统计,<strong>不是集合,不保存数据</strong>,只<strong>记录数量</strong>而不是具体数据</li><li>核心是基数估算算法,最终数值<strong>存在一定误差</strong></li><li>误差范围:基数估计的结果是一个带有 0.81% 标准错误的近似值</li><li><strong>耗空间极小</strong>,每个hyperloglog key占用了12K的内存用于标记基数</li><li>pfadd命令不是一次性分配12K内存使用,会随着基数的增加内存<strong>逐渐增大</strong></li><li>Pfmerge命令<strong>合并后占用</strong>的存储空间为<strong>12K</strong>,无论合并之前数据量多少</li></ul><hr><h2 id="9-3-GEO"><a href="#9-3-GEO" class="headerlink" title="9.3 GEO"></a>9.3 GEO</h2><p>和位置有关,坐标等等</p><p>可应用于附近的人之类的功能</p><hr><h3 id="9-3-1-基础操作"><a href="#9-3-1-基础操作" class="headerlink" title="9.3.1 基础操作"></a>9.3.1 基础操作</h3><ul><li><p>添加坐标点</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs maxima">geoadd <span class="hljs-built_in">key</span> longitude latitude <span class="hljs-built_in">member</span> [longitude latitude <span class="hljs-built_in">member</span> ...] <br></code></pre></td></tr></table></figure></li><li><p>获取坐标点</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs maxima">geopos <span class="hljs-built_in">key</span> <span class="hljs-built_in">member</span> [<span class="hljs-built_in">member</span> ...] <br></code></pre></td></tr></table></figure></li><li><p>计算坐标点距离</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs css">geodist key member1 member2 <span class="hljs-selector-attr">[unit]</span> <br></code></pre></td></tr></table></figure></li><li><p>根据坐标求范围内的数据</p><figure class="highlight inform7"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs inform7">georadius key longitude latitude radius m|km|ft|mi <span class="hljs-comment">[withcoord]</span> <span class="hljs-comment">[withdist]</span> <span class="hljs-comment">[withhash]</span> <span class="hljs-comment">[count count]</span><br></code></pre></td></tr></table></figure></li><li><p>根据点求范围内的数据</p><figure class="highlight inform7"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs inform7">georadiusbymember key member radius m|km|ft|mi <span class="hljs-comment">[withcoord]</span> <span class="hljs-comment">[withdist]</span> <span class="hljs-comment">[withhash]</span> <span class="hljs-comment">[count count]</span><br></code></pre></td></tr></table></figure></li><li><p>获取指定点对于坐标的hash值</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs maxima">geohash <span class="hljs-built_in">key</span> <span class="hljs-built_in">member</span> [<span class="hljs-built_in">member</span> ...]<br></code></pre></td></tr></table></figure></li></ul><hr><h1 id="10-主从复制"><a href="#10-主从复制" class="headerlink" title="10. 主从复制"></a>10. 主从复制</h1><h2 id="10-1-简介"><a href="#10-1-简介" class="headerlink" title="10.1 简介"></a>10.1 简介</h2><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820011135934.png" alt="多台服务器连接方案"></p><ul><li>提供数据方:master<ul><li>主服务器,主节点,主库</li><li>主客户端</li></ul></li><li>接收数据的方:slave<ul><li>从服务器,从节点,从库</li><li>从客户端</li></ul></li><li>需要解决的问题<ul><li><strong>数据同步</strong></li></ul></li><li>核心工作<ul><li>master的数据<strong>复制</strong>到slave中</li></ul></li></ul><hr><h3 id="10-1-1-什么是主从复制"><a href="#10-1-1-什么是主从复制" class="headerlink" title="10.1.1 什么是主从复制"></a>10.1.1 什么是主从复制</h3><p>主从复制即将master中的数据即时、有效的<strong>复制</strong>到slave中</p><p>特征:一个master可以拥有多个slave,一个slave只对应一个master</p><p>职责:</p><ul><li>master:<ul><li>写数据</li><li>执行写操作时,将出现变化的数据自动<strong>同步</strong>到slave</li><li>读数据(可忽略)</li></ul></li><li>slave:<ul><li>读数据</li><li>写数据(<strong>禁止</strong>)</li></ul></li></ul><hr><h3 id="10-1-2-主从复制的作用"><a href="#10-1-2-主从复制的作用" class="headerlink" title="10.1.2 主从复制的作用"></a>10.1.2 主从复制的作用</h3><ul><li><strong>读写分离</strong>:master写、slave读,提高服务器的读写负载能力</li><li>负载均衡:基于主从结构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数量,通过多个从节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量</li><li>故障恢复:当master出现问题时,由slave提供服务,实现快速的故障恢复</li><li>数据冗余:实现数据热备份,是持久化之外的一种数据冗余方式</li><li>高可用基石:基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案</li></ul><hr><h2 id="10-2-工作流程"><a href="#10-2-工作流程" class="headerlink" title="10.2 工作流程"></a>10.2 工作流程</h2><ul><li>主从复制过程大体可以分为3个阶段<ul><li>建立连接阶段(即准备阶段)</li><li>数据同步阶段</li><li>命令传播阶段</li></ul></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820015330350.png" alt="image-20220820015330350"></p><hr><h3 id="10-2-1-阶段一:建立连接阶段"><a href="#10-2-1-阶段一:建立连接阶段" class="headerlink" title="10.2.1 阶段一:建立连接阶段"></a>10.2.1 阶段一:建立连接阶段</h3><p>建立slave到master的连接,使master能够识别slave,并保存slave端口号</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820015446508.png" alt="image-20220820015446508"></p><hr><h4 id="10-2-1-1-连接方式(slave连接master)"><a href="#10-2-1-1-连接方式(slave连接master)" class="headerlink" title="10.2.1.1 连接方式(slave连接master)"></a>10.2.1.1 连接方式(slave连接master)</h4><ul><li><p>方式一:客户端发送命令</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs xml">slaveof <span class="hljs-tag"><<span class="hljs-name">masterip</span>></span> <span class="hljs-tag"><<span class="hljs-name">masterport</span>></span><br>//例<br>slaveof 127.0.0.1 6379<br></code></pre></td></tr></table></figure></li><li><p>方式二:启动服务器参数</p><figure class="highlight axapta"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs axapta">redis-<span class="hljs-keyword">server</span> -slaveof <masterip> <masterport><br><span class="hljs-comment">//例</span><br>redis-<span class="hljs-keyword">server</span> -slaveof <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span> <span class="hljs-number">6379</span><br></code></pre></td></tr></table></figure></li><li><p>方式三:服务器配置 <strong>(常用</strong>)写在slave的conf文件中,然后直接启动即可</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml">slaveof <span class="hljs-tag"><<span class="hljs-name">masterip</span>></span> <span class="hljs-tag"><<span class="hljs-name">masterport</span>></span><br></code></pre></td></tr></table></figure></li></ul><p>输入<strong>info</strong>可查看系统信息</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820020139930.png" alt="image-20220820020139930"></p><ul><li><p>slave</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820020206164.png" alt="image-20220820020206164"></p></li><li><p>master</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820020246289.png" alt="image-20220820020246289"></p></li></ul><hr><h4 id="10-2-1-2-主从断开连接"><a href="#10-2-1-2-主从断开连接" class="headerlink" title="10.2.1.2 主从断开连接"></a>10.2.1.2 主从断开连接</h4><ul><li><p><strong>slave客户端</strong>发送命令</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-attribute">slaveof</span> <span class="hljs-literal">no</span> one<br></code></pre></td></tr></table></figure><ul><li>说明: slave断开连接后,<strong>不会删除已有数据</strong>,只是不再接受master发送的数据</li></ul></li></ul><hr><h4 id="10-2-1-3-授权访问"><a href="#10-2-1-3-授权访问" class="headerlink" title="10.2.1.3 授权访问"></a>10.2.1.3 授权访问</h4><ul><li><p>master客户端发送命令设置密码</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml">requirepass <span class="hljs-tag"><<span class="hljs-name">password</span>></span><br></code></pre></td></tr></table></figure></li><li><p>master配置文件设置密码</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs routeros">config <span class="hljs-built_in">set</span> requirepass <password> <span class="hljs-built_in"></span><br><span class="hljs-built_in">config </span><span class="hljs-built_in">get</span> requirepass<br></code></pre></td></tr></table></figure></li><li><p>slave客户端发送命令设置密码</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml">auth <span class="hljs-tag"><<span class="hljs-name">password</span>></span><br></code></pre></td></tr></table></figure></li><li><p>slave配置文件设置密码</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml">masterauth <span class="hljs-tag"><<span class="hljs-name">password</span>></span><br></code></pre></td></tr></table></figure></li><li><p>slave启动服务器设置密码</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">redis-<span class="hljs-keyword">server</span> –a <<span class="hljs-keyword">password</span>><br></code></pre></td></tr></table></figure></li></ul><hr><h3 id="10-2-2-阶段二:数据同步阶段"><a href="#10-2-2-阶段二:数据同步阶段" class="headerlink" title="10.2.2 阶段二:数据同步阶段"></a>10.2.2 阶段二:数据同步阶段</h3><h4 id="10-2-2-1-同步流程(简)"><a href="#10-2-2-1-同步流程(简)" class="headerlink" title="10.2.2.1 同步流程(简)"></a>10.2.2.1 同步流程(简)</h4><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820021021376.png" alt="image-20220820021021376"></p><ul><li><p>全量复制</p><ul><li>将master执行bgsave之前,master中所有的数据同步到slave中</li></ul></li><li><p>部分复制(增量复制)</p><ul><li><p>RDB是一个时刻的快照,在生成RDB文件和发送接收RDB文件的这段时间里,<strong>master也会在接收指令存储数据</strong>,这些指令会存放到复制缓冲区里(这里传输其实也会有指令进来,但是很少,因此<strong>做不到百分百</strong>)。</p></li><li><p>将master执行bgsave操作中,新加入的数据(复制缓冲区中的数据)传给slave,slave通过bgrewriteaof指令来恢复数据</p></li></ul></li></ul><hr><h5 id="部分复制"><a href="#部分复制" class="headerlink" title="部分复制"></a>部分复制</h5><p>部分复制的三个核心要素</p><ul><li>服务器的运行 id(run id)</li><li>主服务器的复制积压缓冲区</li><li>主从服务器的复制偏移量(offset)</li></ul><hr><h6 id="服务器的运行-id(run-id)"><a href="#服务器的运行-id(run-id)" class="headerlink" title="服务器的运行 id(run id)"></a>服务器的运行 id(run id)</h6><ul><li>概念:服务器运行ID是每一台服务器每次运行的身份识别码,一台服务器多次运行可以生成<strong>多个</strong>运行id<ul><li>一台服务器重启之后运行id就不同了</li></ul></li><li>组成:运行id由40位字符组成,是一个随机的十六进制字符 例如- -<ul><li>fdc9ff13b9bbaab28db42b3d50f852bb5e3fcdce</li></ul></li><li>作用:运行id被用于在服务器间进行传输,识别身份<ul><li>如果想两次操作均对同一台服务器进行,必须每次操作携带对应的运行id,用于对方识别</li></ul></li><li>实现方式:运行id在每台服务器启动时自动生成的,master在首次连接slave时,会将自己的运行ID发送给slave,slave保存此ID,通过<strong>info Server</strong>命令,可以查看节点的runid</li></ul><hr><h6 id="复制缓冲区(复制积压缓冲区)"><a href="#复制缓冲区(复制积压缓冲区)" class="headerlink" title="复制缓冲区(复制积压缓冲区)"></a>复制缓冲区(复制积压缓冲区)</h6><ul><li>概念:复制缓冲区,又名复制积压缓冲区,是一个<strong>先进先出(FIFO)的队列</strong>,用于存储服务器执行过的命 令,每次传播命令,master都会将传播的命令记录下来,并存储在复制缓冲区</li><li>由来:每台服务器启动时,如果开启有AOF或被连接成为master节点,即创建复制缓冲区</li><li>作用:<strong>用于保存master收到的所有指令</strong>(仅影响数据变更的指令,例如set,select)</li><li>数据来源:当master接收到主客户端的指令时,除了将指令执行,会将该指令存储到缓冲区中</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820135938151.png" alt="image-20220820135938151"></p><p><strong>工作原理</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820140110087.png" alt="image-20220820140110087"></p><hr><h6 id="主从服务器复制偏移量(offset)"><a href="#主从服务器复制偏移量(offset)" class="headerlink" title="主从服务器复制偏移量(offset)"></a>主从服务器复制偏移量(offset)</h6><ul><li>概念:一个数字,描述复制缓冲区中的指令字节位置</li><li>分类:<ul><li>master复制偏移量:记录发送给所有slave的指令字节对应的位置<strong>(多个)</strong></li><li>slave复制偏移量:记录slave接收master发送过来的指令字节对应的位置<strong>(一个)</strong></li></ul></li><li>数据来源: master端:发送一次记录一次 slave端:接收一次记录一次</li><li>作用:<strong>同步信息</strong>,比对master与slave的差异,当slave断线后,恢复数据使用</li></ul><hr><h4 id="10-2-2-2-同步流程(精)"><a href="#10-2-2-2-同步流程(精)" class="headerlink" title="10.2.2.2 同步流程(精)"></a>10.2.2.2 同步流程(精)</h4><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820140610825.png" alt="image-20220820140610825"></p><hr><h4 id="10-2-2-3-注意事项"><a href="#10-2-2-3-注意事项" class="headerlink" title="10.2.2.3 注意事项"></a>10.2.2.3 注意事项</h4><ul><li><p>数据同步阶段master说明</p><ul><li><p>如果master数据量巨大,数据同步阶段应<strong>避开流量高峰期</strong>,<strong>避免</strong>造成master<strong>阻塞</strong>,影响业务正常执行</p></li><li><p>复制缓冲区大小设定不合理,会导致数据溢出。如进行全量复制周期太长,进行部分复制时发现数据已经存在丢失的情况,必须进行第二次全量复制,致使slave陷入<strong>死循环</strong>状态。(比如用户操作高峰期)</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs arduino"><span class="hljs-comment">//设置缓冲区大小</span><br>repl-backlog-size <span class="hljs-number">1</span>mb<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820022435794.png" alt="image-20220820022435794"></p></li><li><p>master单机内存占用主机内存的比例不应过大,建议使用50%-70%的内存,留下30%-50%的内存用于执 行bgsave命令和创建复制缓冲区</p></li></ul></li><li><p>数据同步阶段slave说明</p><ul><li><p>为避免slave进行全量复制、部分复制时服务器响应阻塞或数据不同步,<strong>建议关闭</strong>此期间的对外服务</p><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs coffeescript">slave-serve-stale-data <span class="hljs-literal">yes</span>|<span class="hljs-literal">no</span><br></code></pre></td></tr></table></figure></li><li><p>数据同步阶段,master发送给slave信息<strong>可以理解master是slave的一个客户端</strong>,主动向slave发送命令</p></li><li><p>多个slave同时对master请求数据同步,<strong>master发送的RDB文件增多</strong>,会对带宽造成巨大冲击,如果master带宽不足,因此数据同步需要根据业务需求,适量错峰</p></li><li><p>slave过多时,建议调整拓扑结构,由一主多从结构变为树状结构,中间的节点既是master,也是 slave。注意使用树状结构时,由于层级深度,导致深度越高的slave与最顶层master间数据同步延迟较大,<strong>数据一致性变差,应谨慎选择</strong></p></li></ul></li></ul><hr><h3 id="10-2-3-阶段三:命令传播阶段"><a href="#10-2-3-阶段三:命令传播阶段" class="headerlink" title="10.2.3 阶段三:命令传播阶段"></a>10.2.3 阶段三:命令传播阶段</h3><ul><li><p>当master数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的<br>状态,同步的动作称为命令传播</p></li><li><p>master将接收到的数据变更命令发送给slave,slave接收命令后执行命令</p></li></ul><hr><h4 id="10-2-3-1-心跳机制"><a href="#10-2-3-1-心跳机制" class="headerlink" title="10.2.3.1 心跳机制"></a>10.2.3.1 心跳机制</h4><ul><li>进入<strong>命令传播阶段候</strong>,master与slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线</li><li>master心跳:<ul><li>指令:PING</li><li>周期:由repl-ping-slave-period决定,默认10秒</li><li>作用:判断slave是否在线</li><li>查询:INFO replication 获取slave<strong>最后一次连接时间间隔</strong>,lag项维持在0或1视为正常(过大视为断线)</li></ul></li><li>slave心跳任务<ul><li>指令:REPLCONF ACK {offset}</li><li>周期:1秒</li><li>作用1:汇报slave自己的<strong>复制偏移量</strong>,获取最新的数据变更指令</li><li>作用2:判断master是否在线</li></ul></li></ul><hr><h5 id="心跳阶段注意事项"><a href="#心跳阶段注意事项" class="headerlink" title="心跳阶段注意事项"></a>心跳阶段注意事项</h5><ul><li><p>当slave多数掉线,或延迟过高时,master为保障数据稳定性,将拒绝所有信息同步操作</p><figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs livecodeserver"><span class="hljs-built_in">min</span>-slaves-<span class="hljs-built_in">to</span>-<span class="hljs-built_in">write</span> <span class="hljs-number">2</span> <br><span class="hljs-built_in">min</span>-slaves-<span class="hljs-built_in">max</span>-lag <span class="hljs-number">10</span><br></code></pre></td></tr></table></figure><ul><li>slave数量少于2个,或者所有slave的延迟都大于等于10秒时,强制关闭master写功能,停止数据同步</li></ul></li><li><p>slave数量由slave发送<strong>REPLCONF ACK</strong>命令做确认</p></li><li><p>slave延迟由slave发送<strong>REPLCONF ACK</strong>命令做确认</p></li></ul><hr><h4 id="10-2-3-2-主从复制完整流程"><a href="#10-2-3-2-主从复制完整流程" class="headerlink" title="10.2.3.2 主从复制完整流程"></a>10.2.3.2 主从复制完整流程</h4><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820141236587.png" alt="image-20220820141236587"></p><hr><h2 id="10-3-主从复制常见问题"><a href="#10-3-主从复制常见问题" class="headerlink" title="10.3 主从复制常见问题"></a>10.3 主从复制常见问题</h2><h3 id="10-3-1-频繁的全量复制"><a href="#10-3-1-频繁的全量复制" class="headerlink" title="10.3.1 频繁的全量复制"></a>10.3.1 频繁的全量复制</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820141858696.png" alt="image-20220820141858696"></p><hr><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820141923236.png" alt="image-20220820141923236"></p><hr><h3 id="10-3-2-频繁的网络中断"><a href="#10-3-2-频繁的网络中断" class="headerlink" title="10.3.2 频繁的网络中断"></a>10.3.2 频繁的网络中断</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820142310562.png" alt="image-20220820142310562"></p><hr><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820142318128.png" alt="image-20220820142318128"></p><hr><h3 id="10-3-3-数据不一致"><a href="#10-3-3-数据不一致" class="headerlink" title="10.3.3 数据不一致"></a>10.3.3 数据不一致</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820142411678.png" alt="image-20220820142411678"></p><hr><h1 id="11-哨兵(sentinel)"><a href="#11-哨兵(sentinel)" class="headerlink" title="11. 哨兵(sentinel)"></a>11. 哨兵(sentinel)</h1><h2 id="11-1-简介"><a href="#11-1-简介" class="headerlink" title="11.1 简介"></a>11.1 简介</h2><p>主从模式下,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这种方式并不推荐,实际生产中,我们优先考虑哨兵模式。这种模式下,master 宕机,哨兵会自动选举 master 并将其他的 slave 指向新的 master。</p><p>哨兵(sentinel) 是一个<strong>分布式系统</strong>,用于对主从结构中的每台服务器进行<strong>监控</strong>,当出现故障时通过投票机制<strong>选择</strong>一个哨兵,让这个哨兵去选择一个新的master并将所有slave连接到新的master。</p><hr><h2 id="11-2-哨兵的作用"><a href="#11-2-哨兵的作用" class="headerlink" title="11.2 哨兵的作用"></a>11.2 哨兵的作用</h2><ul><li>监控<ul><li>不断的检查master和slave是否正常运行。 master存活检测、master与slave运行情况检测</li></ul></li><li>通知(提醒)<ul><li>当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知。</li></ul></li><li>自动故障转移<ul><li>断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址</li></ul></li></ul><p><strong>注意:</strong><br>哨兵也是一台<strong>redis服务器</strong>,只是不提供数据服务 通常哨兵配置数量为<strong>单数</strong>(单数是为了在投票时减少票数相同的情况)</p><hr><h2 id="11-3-配置哨兵"><a href="#11-3-配置哨兵" class="headerlink" title="11.3 配置哨兵"></a>11.3 配置哨兵</h2><ul><li><p>配置一拖二的主从结构</p></li><li><p>配置三个哨兵(配置相同,端口不同)</p><ul><li>参看sentinel.conf</li></ul></li><li><p>启动哨兵</p><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs jboss-cli">redis-sentinel sentinel端口号 <span class="hljs-string">.conf</span><br></code></pre></td></tr></table></figure></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820154648982.png" alt="image-20220820154648982"></p><hr><h2 id="11-4-哨兵工作原理"><a href="#11-4-哨兵工作原理" class="headerlink" title="11.4 哨兵工作原理"></a>11.4 哨兵工作原理</h2><h3 id="11-4-1-阶段一:监控阶段"><a href="#11-4-1-阶段一:监控阶段" class="headerlink" title="11.4.1 阶段一:监控阶段"></a>11.4.1 阶段一:监控阶段</h3><ul><li>用于同步各个节点的状态信息<ul><li>获取各个sentinel的状态(是否在线)</li></ul></li><li>获取master的状态<ul><li>master属性<ul><li>runid</li><li>role:master</li><li>各个slave的详细信息</li></ul></li></ul></li><li>获取所有slave的状态(根据master中的slave信息)<ul><li>slave属性<ul><li>runid</li><li>role:slave</li><li>master_host、master_port</li><li>offset</li><li>…</li></ul></li></ul></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820154816190.png" alt="image-20220820154816190"></p><p>这个阶段,第一个sentinel去获取完信息之后保存好,第二个sentinel来获取时在master处有一个sentinels的信息,就相当于发现了“战友”,于是哨兵2和哨兵1之间就会有有一个共享信息的通道</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820155055524.png" alt="image-20220820155055524"></p><hr><h3 id="11-4-2-阶段二:通知阶段"><a href="#11-4-2-阶段二:通知阶段" class="headerlink" title="11.4.2 阶段二:通知阶段"></a>11.4.2 阶段二:通知阶段</h3><ul><li>各个哨兵将得到的信息相互同步(信息对称)</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820155230059.png" alt="image-20220820155230059"></p><hr><h3 id="11-4-3-阶段三:故障转移阶段"><a href="#11-4-3-阶段三:故障转移阶段" class="headerlink" title="11.4.3 阶段三:故障转移阶段"></a>11.4.3 阶段三:故障转移阶段</h3><p>① 当有一个哨兵去监控master时,发现master宕机了。这个时候master的info中有一个flags配置会变成</p><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs avrasm"><span class="hljs-symbol">flags:</span>SRI_S_DOWN<br></code></pre></td></tr></table></figure><p>意为<strong>主观下线</strong>;同时这个哨兵会去通知其他哨兵这个master宕机了;</p><p>② 其他哨兵收到后都去检测master,如果总体下来,认同master确实宕机了的哨兵数量大于哨兵总数量的一半(配置文件可设置),那么flags会变成</p><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs avrasm"><span class="hljs-symbol">flags:</span>SRI_O_DOWN<br></code></pre></td></tr></table></figure><p>意为<strong>客观下线</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820160001720.png" alt="image-20220820160001720"></p><p>③ 开启哨兵之间的投票环节,选举一个负责人进行故障转移工作</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820160108263.png" alt="image-20220820160108263"></p><p>④ 具体选举新的master的条件</p><ul><li>由推选出来的哨兵对当前的slave进行筛选,筛选条件有:<ul><li>服务器列表中挑选备选master</li><li>在线的</li><li>响应块的</li><li>与原master断开时间短的</li><li>优先原则<ul><li>优先级 :按照slave优先级进行排序,slave priority越低,优先级就越高</li><li>offset :如果slave priority相同,那么看replica offset,哪个slave复制了越多的数据,offset越靠后,优先级就越高</li><li>runid : 如果上面两个条件都相同,那么选择一个run id比较小的那个slave</li></ul></li></ul></li></ul><p>⑤ 发送指令( sentinel )</p><ul><li><p>向新的master发送<strong>slaveof no one</strong>(断开与原master的连接)</p></li><li><p>向其他slave发送slaveof 新masterIP端口(让其他slave与新的master相连)</p></li></ul><hr><h2 id="11-5-为什么Redis哨兵集群只有2个节点无法正常工作?"><a href="#11-5-为什么Redis哨兵集群只有2个节点无法正常工作?" class="headerlink" title="11.5 为什么Redis哨兵集群只有2个节点无法正常工作?"></a>11.5 为什么Redis哨兵集群只有2个节点无法正常工作?</h2><p>哨兵集群必须部署2个以上节点。</p><p>如果两个哨兵实例,即两个Redis实例,一主一从的模式。</p><p>则Redis的配置quorum=1,表示一个哨兵认为master宕机即可认为master已宕机。</p><p>但是如果是机器1宕机了,那哨兵1和master都宕机了,虽然哨兵2知道master宕机了,但是这个时候,需要majority,也就是大多数哨兵都是运行的,2个哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=2),2个哨兵都运行着,就可以允许执行故障转移。</p><p>但此时哨兵1没了就只有1个哨兵了了,此时就没有majority来允许执行故障转移,所以故障转移不会执行。</p><hr><h1 id="12-集群(Redis-cluster)"><a href="#12-集群(Redis-cluster)" class="headerlink" title="12. 集群(Redis cluster)"></a>12. 集群(Redis cluster)</h1><h2 id="12-1-简介"><a href="#12-1-简介" class="headerlink" title="12.1 简介"></a>12.1 简介</h2><h3 id="12-1-1-集群架构"><a href="#12-1-1-集群架构" class="headerlink" title="12.1.1 集群架构"></a>12.1.1 集群架构</h3><ul><li>集群就是使用网络将若干台计算机<strong>联通</strong>起来,并提供<strong>统一的管理方式</strong>,使其对外呈现单机的服务效果</li></ul><h3 id="12-1-2-集群作用"><a href="#12-1-2-集群作用" class="headerlink" title="12.1.2 集群作用"></a>12.1.2 集群作用</h3><ul><li>分散单台服务器的访问压力,实现<strong>负载均衡</strong></li><li>分散单台服务器的存储压力,实现<strong>可扩展性</strong></li><li><strong>降低</strong>单台服务器宕机带来的<strong>业务灾难</strong></li></ul><hr><h2 id="12-2-Redis集群结构设计"><a href="#12-2-Redis集群结构设计" class="headerlink" title="12.2 Redis集群结构设计"></a>12.2 Redis集群结构设计</h2><h3 id="12-2-1-数据存储设计"><a href="#12-2-1-数据存储设计" class="headerlink" title="12.2.1 数据存储设计"></a>12.2.1 数据存储设计</h3><ul><li>通过算法设计,计算出key应该保存的位置</li><li>将所有的存储空间计划切割成16384份,每台主机保存一部分 每份代表的是一个存储空间,不是一个key的保存空间</li><li>将key按照计算出的结果放到对应的存储空间</li><li>取模16384,有点像hash</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820163459511.png" alt="image-20220820163459511"></p><hr><h3 id="12-2-2-实现数据分布"><a href="#12-2-2-实现数据分布" class="headerlink" title="12.2.2 实现数据分布"></a>12.2.2 实现数据分布</h3><p>Redis cluster有固定的16384个hash slot(哈希槽),对每个key计算CRC16值,然后对16384取模,可以获取key对应的hash slot。</p><p>Redis cluster中每个master都会持有部分slot(槽),比如有3个master,那么可能每个master持有5000多个hash slot。</p><p>hash slot让node的增加和移除很简单,增加一个master,就将其他master的hash slot移动部分过去,减少一个master,就将它的hash slot移动到其他master上去。每次增加或减少master节点都是对16384取模,而不是根据master数量,这样原本在老的master上的数据不会因master的新增或减少而找不到。并且增加或减少master时Redis cluster移动hash slot的成本是非常低的。</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820163941839.png" alt="image-20220820163941839"></p><hr><h3 id="12-2-3-集群内部节点通讯设计"><a href="#12-2-3-集群内部节点通讯设计" class="headerlink" title="12.2.3 集群内部节点通讯设计"></a>12.2.3 集群内部节点通讯设计</h3><ul><li>各个数据库互相连通,保存各个库中槽的编号数据</li><li>一次命中,直接返回</li><li>一次未命中,告知具体的位置,key再直接去找对应的库保存数据</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820164033391.png" alt="image-20220820164033391"></p><p>如果key在1<del>10中在A之间命中,如果是11</del>20中,告知在B中,再去B中找</p><p>Redis cluster节点间采取gossip协议进行通信,所有节点都持有一份元数据(就是上面的数据编号),不同的节点如果出现了元数据的变更之后U不断地i将元数据发送给其他节点让其他节点进行数据变更。</p><blockquote><p>节点互相之间不断通信,保持整个集群所有节点的数据是完整的。 主要交换故障信息、节点的增加和移除、hash slot信息等。</p></blockquote><p>这种机制的好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力;</p><p>缺点,元数据更新有延时,可能导致集群的一些操作会有一些滞后。</p><hr><h1 id="13-企业级解决方案"><a href="#13-企业级解决方案" class="headerlink" title="13. 企业级解决方案"></a>13. 企业级解决方案</h1><p><a href="https://www.javalearn.cn/#/doc/Redis/%E9%9D%A2%E8%AF%95%E9%A2%98?id=%E7%BC%93%E5%AD%98%E5%BC%82%E5%B8%B8">缓存异常</a></p><h2 id="13-1-缓存预热"><a href="#13-1-缓存预热" class="headerlink" title="13.1 缓存预热"></a>13.1 缓存预热</h2><h3 id="13-1-1-问题排查"><a href="#13-1-1-问题排查" class="headerlink" title="13.1.1 问题排查"></a>13.1.1 问题排查</h3><ul><li>请求数量较高</li><li>主从之间数据吞吐量较大,数据同步操作频度较高</li></ul><hr><h3 id="13-1-2-解决方案"><a href="#13-1-2-解决方案" class="headerlink" title="13.1.2 解决方案"></a>13.1.2 解决方案</h3><ul><li>前置准备工作:<ul><li>日常例行统计数据访问记录,统计访问频度较高的热点数据</li><li>利用LRU数据删除策略,构建数据留存队列 例如:storm与kafka配合</li></ul></li><li>准备工作:<ul><li>将统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据</li><li>利用分布式多服务器同时进行数据读取,提速数据加载过程</li><li>热点数据主从同时预热</li></ul></li><li>实施:<ul><li>使用脚本程序固定触发数据预热过程</li><li>如果条件允许,使用了CDN(内容分发网络),效果会更好</li></ul></li></ul><hr><h3 id="13-1-3-总结"><a href="#13-1-3-总结" class="headerlink" title="13.1.3 总结"></a>13.1.3 总结</h3><p>缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!</p><hr><h2 id="13-2-缓存雪崩"><a href="#13-2-缓存雪崩" class="headerlink" title="13.2 缓存雪崩"></a>13.2 缓存雪崩</h2><h3 id="13-2-1-数据库服务器崩溃"><a href="#13-2-1-数据库服务器崩溃" class="headerlink" title="13.2.1 数据库服务器崩溃"></a>13.2.1 数据库服务器崩溃</h3><ol><li>系统平稳运行过程中,忽然数据库连接量激增</li><li>应用服务器无法及时处理请求</li><li>大量408,500错误页面出现</li><li>客户反复刷新页面获取数据</li><li>数据库崩溃</li><li>应用服务器崩溃</li><li>重启应用服务器无效</li><li>Redis服务器崩溃</li><li>Redis集群崩溃</li><li>重启数据库后再次被瞬间流量放倒</li></ol><hr><h3 id="13-2-2-问题排查"><a href="#13-2-2-问题排查" class="headerlink" title="13.2.2 问题排查"></a>13.2.2 问题排查</h3><ol><li>在一个<strong>较短</strong>的时间内,缓存中较多的key<strong>集中过期</strong></li><li>此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据</li><li>数据库同时接收到大量的请求无法及时处理</li><li>Redis大量请求被积压,开始出现超时现象</li><li>数据库流量激增,数据库崩溃</li><li>重启后仍然面对缓存中无数据可用</li><li>Redis服务器资源被严重占用,Redis服务器崩溃</li><li>Redis集群呈现崩塌,集群瓦解</li><li>应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃</li><li>应用服务器,redis,数据库全部重启,效果不理想</li></ol><hr><h3 id="13-2-3-问题分析"><a href="#13-2-3-问题分析" class="headerlink" title="13.2.3 问题分析"></a>13.2.3 问题分析</h3><ul><li>短时间范围内</li><li>大量key集中过期</li></ul><hr><h3 id="13-2-4-解决方案(道)"><a href="#13-2-4-解决方案(道)" class="headerlink" title="13.2.4 解决方案(道)"></a>13.2.4 解决方案(道)</h3><ol><li>更多的页面静态化处理</li><li>构建<strong>多级缓存架构</strong> Nginx缓存+redis缓存+ehcache缓存</li><li>检测Mysql严重耗时业务进行优化 对数据库的瓶颈排查:例如超时查询、耗时较高事务等</li><li>灾难预警机制 监控redis服务器性能指标<ul><li>CPU占用、CPU使用率</li><li>内存容量</li><li>查询平均响应时间</li><li>线程数</li></ul></li><li>限流、降级 短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问</li></ol><hr><h3 id="13-2-5-解决方案(术)"><a href="#13-2-5-解决方案(术)" class="headerlink" title="13.2.5 解决方案(术)"></a>13.2.5 解决方案(术)</h3><ol><li>LRU与LFU切换</li><li>数据有效期策略调整<ul><li>根据业务数据有效期进行<strong>分类错峰</strong>,A类90分钟,B类80分钟,C类70分钟</li><li>过期时间使用固定时间+随机值的形式,<strong>稀释</strong>集中到期的key的数量</li></ul></li><li><strong>超热</strong>数据使用永久key</li><li>定期维护(自动+人工) 对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时</li><li>加锁 <strong>慎用!</strong></li></ol><hr><h3 id="13-2-6总结"><a href="#13-2-6总结" class="headerlink" title="13.2.6总结"></a>13.2.6总结</h3><p>缓存雪崩就是<strong>瞬间过期数据量太大</strong>,导致对数据库服务器造成压力。如能够<strong>有效避免过期时间集中</strong>,可以有效解决雪崩现象的出现 (约40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。</p><hr><h2 id="13-3-缓存击穿"><a href="#13-3-缓存击穿" class="headerlink" title="13.3 缓存击穿"></a>13.3 缓存击穿</h2><h3 id="13-3-1-数据库服务器崩溃"><a href="#13-3-1-数据库服务器崩溃" class="headerlink" title="13.3.1 数据库服务器崩溃"></a>13.3.1 数据库服务器崩溃</h3><ol><li>系统平稳运行过程中</li><li>数据库连接量<strong>瞬间激增</strong></li><li>Redis服务器无大量key过期</li><li>Redis内存平稳,无波动</li><li>Redis服务器CPU正常</li><li><strong>数据库崩溃</strong></li></ol><hr><h3 id="13-3-2-问题排查"><a href="#13-3-2-问题排查" class="headerlink" title="13.3.2 问题排查"></a>13.3.2 问题排查</h3><ol><li>Redis中<strong>某个key过期,该key访问量巨大</strong></li><li>多个数据请求从服务器直接压到Redis后,均未命中</li><li>Redis在短时间内发起了大量对数据库中同一数据的访问</li></ol><hr><h3 id="13-3-3-问题分析"><a href="#13-3-3-问题分析" class="headerlink" title="13.3.3 问题分析"></a>13.3.3 问题分析</h3><ul><li>单个key高热数据</li><li>key过期</li></ul><hr><h3 id="13-3-4-解决方案(术)"><a href="#13-3-4-解决方案(术)" class="headerlink" title="13.3.4 解决方案(术)"></a>13.3.4 解决方案(术)</h3><ol><li><p>预先设定</p><p>以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,<strong>加大</strong>此类信息key的<strong>过期时长</strong></p><p>注意:购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低的趋势</p></li><li><p>现场调整</p><ul><li>监控访问量,对自然流量激增的数据延长过期时间或设置为永久性key</li></ul></li><li><p>后台刷新数据</p><ul><li>启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失</li></ul></li><li><p>二级缓存</p><ul><li>设置不同的失效时间,保障不会被同时淘汰就行</li></ul></li><li><p>加锁 分布式锁,防止被击穿,但是要注意也是性能瓶颈,<strong>慎重!</strong></p></li></ol><hr><h3 id="13-3-5-总结"><a href="#13-3-5-总结" class="headerlink" title="13.3.5 总结"></a>13.3.5 总结</h3><p>缓存击穿就是<strong>单个高热数据过期的瞬间</strong>,数据访问量较大,未命中redis后,发起了大量对同一数据的数据库问,导致对数据库服务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过期监控难度较高,配合雪崩处理策略即可</p><hr><h2 id="13-4-缓存穿透"><a href="#13-4-缓存穿透" class="headerlink" title="13.4 缓存穿透"></a>13.4 缓存穿透</h2><p>缓存穿透是指用户请求的数据在缓存中不存在即没有命中,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍。如果有恶意攻击者不断请求系统中不存在的数据,会导致短时间大量请求落在数据库上,造成数据库压力过大,甚至导致数据库承受不住而宕机崩溃。</p><blockquote><p>缓存穿透的关键在于在Redis中查不到key值,它和缓存击穿的根本区别在于传进来的key在Redis中是不存在的。假如有黑客传进大量的不存在的key,那么大量的请求打在数据库上是很致命的问题,所以在日常开发中要对参数做好校验,一些非法的参数,不可能存在的key就直接返回错误提示。</p></blockquote><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220820171639971.png" alt="image-20220820171639971"></p><p>解决方法:</p><ul><li>将无效的key存放进Redis中:</li></ul><p>当出现Redis查不到数据,数据库也查不到数据的情况,我们就把这个key保存到Redis中,设置value=”null”,并设置其过期时间极短,后面再出现查询这个key的请求的时候,直接返回null,就不需要再查询数据库了。但这种处理方式是有问题的,假如传进来的这个不存在的Key值每次都是随机的,那存进Redis也没有意义。</p><ul><li>使用布隆过滤器:</li></ul><p>如果布隆过滤器判定某个 key 不存在布隆过滤器中,那么就一定不存在,如果判定某个 key 存在,那么很大可能是存在(存在一定的误判率)。于是我们可以在缓存之前再加一个布隆过滤器,将数据库中的所有key都存储在布隆过滤器中,在查询Redis前先去布隆过滤器查询 key 是否存在,如果不存在就直接返回,不让其访问数据库,从而避免了对底层存储系统的查询压力。</p><blockquote><p>如何选择:针对一些恶意攻击,攻击带过来的大量key是随机,那么我们采用第一种方案就会缓存大量不存在key的数据。那么这种方案就不合适了,我们可以先对使用布隆过滤器方案进行过滤掉这些key。所以,针对这种key异常多、请求重复率比较低的数据,优先使用第二种方案直接过滤掉。而对于空数据的key有限的,重复率比较高的,则可优先采用第一种方式进行缓存。</p></blockquote>]]></content>
<categories>
<category>数据库</category>
<category>Redis</category>
</categories>
<tags>
<tag>Redis</tag>
<tag>缓存</tag>
</tags>
</entry>
<entry>
<title>jvm学习</title>
<link href="/2022/08/06/JVM%E5%AD%A6%E4%B9%A0/"/>
<url>/2022/08/06/JVM%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><p>本博客是根据<a href="https://www.bilibili.com/video/BV1yE411Z7AP"><strong>黑马程序员JVM完整教程</strong></a>教学视频学习时,所做的笔记</p><blockquote><p>ps: 实际字数没那么多,上面显示的是字符数</p></blockquote><hr><h1 id="1-内存结构"><a href="#1-内存结构" class="headerlink" title="1.内存结构"></a>1.内存结构</h1><h2 id="1-1-程序计数器"><a href="#1-1-程序计数器" class="headerlink" title="1.1 程序计数器"></a>1.1 程序计数器</h2><p>Program Counter Register 程序计数器(寄存器)</p><h3 id="1-1-1-作用"><a href="#1-1-1-作用" class="headerlink" title="1.1.1 作用"></a>1.1.1 作用</h3><p>是记住下一条jvm指令的执行地址</p><p>(例如解释器取出3后,4就会存入程序计数器)</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806164540695.png" alt="image-20220806164540695"></p><hr><h3 id="1-1-2-特点"><a href="#1-1-2-特点" class="headerlink" title="1.1.2 特点"></a>1.1.2 特点</h3><p>是线程<strong>私有</strong>的(每个线程都有自己私有的程序计数器);</p><p>不会存在内存溢出</p><hr><h2 id="1-2-虚拟机栈"><a href="#1-2-虚拟机栈" class="headerlink" title="1.2 虚拟机栈"></a>1.2 虚拟机栈</h2><h3 id="1-2-1-定义"><a href="#1-2-1-定义" class="headerlink" title="1.2.1 定义"></a>1.2.1 定义</h3><p>Java Virtual Machine Stacks (Java 虚拟机栈)</p><p>每个线程运行时所需要的内存,称为虚拟机栈</p><p>每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存</p><p>每个线程只能有<strong>一个</strong>活动栈帧,对应着当前正在执行的那个方法</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806165228402.png" alt="image-20220806165228402"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806165238256.png" alt="image-20220806165238256"></p><p>问题辨析</p><ol><li>垃圾回收是否涉及栈内存?</li></ol><p>答:不涉及、垃圾回收是回收的堆内存的无用对象</p><ol start="2"><li>栈内存分配越大越好吗?</li></ol><p>答:不是,会使线程变少</p><ol start="3"><li>方法内的局部变量是否线程安全?</li></ol><p>答:如果方法内局部变量没有逃离方法的作用访问,它是线程安全的</p><p>如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全</p><hr><h3 id="1-2-2-栈内存溢出"><a href="#1-2-2-栈内存溢出" class="headerlink" title="1.2.2 栈内存溢出"></a>1.2.2 栈内存溢出</h3><ul><li>栈帧过多导致栈内存溢出(比如典型的递归、当没有设置正确的递归边界 )</li><li>栈帧过大导致栈内存溢出(很少出现)</li></ul><hr><h2 id="1-3-本地方法栈"><a href="#1-3-本地方法栈" class="headerlink" title="1.3 本地方法栈"></a>1.3 本地方法栈</h2><p>就是有些代码底层是由C/C++实现的就是操作系统的方法,Java只需要用native标记一下即可、比如Object下的clone()、HashCode();</p><h2 id="1-4-堆"><a href="#1-4-堆" class="headerlink" title="1.4 堆"></a>1.4 堆</h2><h3 id="1-4-1-定义"><a href="#1-4-1-定义" class="headerlink" title="1.4.1 定义"></a>1.4.1 定义</h3><p>Heap 堆</p><p>通过 new 关键字,创建对象都会使用堆内存</p><p>特点:</p><p>它是线程共享的,堆中对象都需要考虑线程安全的问题</p><p><strong>有垃圾回收机制</strong></p><hr><h3 id="1-4-2-堆内存溢出"><a href="#1-4-2-堆内存溢出" class="headerlink" title="1.4.2 堆内存溢出"></a>1.4.2 堆内存溢出</h3><p>不断的产生对象、且有人使用它、渐渐的就会产生堆内存溢出</p><p>java.lang.OutofMemoryError :java heap space. 堆内存溢出</p><p>可以使用 -Xmx8m 来指定堆内存大小。</p><hr><h2 id="1-5-方法区"><a href="#1-5-方法区" class="headerlink" title="1.5 方法区"></a>1.5 方法区</h2><h3 id="1-5-1-定义"><a href="#1-5-1-定义" class="headerlink" title="1.5.1 定义"></a>1.5.1 定义</h3><p>Method Area</p><p>The Java Virtual Machine has a <em>method area</em> that is shared among all Java Virtual Machine threads. The method area is analogous to the storage area for compiled code of a conventional language or analogous to the “text” segment in an operating system process. It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors, including the special methods (<a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9">§2.9</a>) used in class and instance initialization and interface initialization.</p><p>Java虚拟机具有一个方法区域,该方法区域在所有Java虚拟机线程中共享。该方法区域类似于存储区域的常规语言代码或类似于操作系统过程中的“文本”段。它存储了每个类结构,例如运行时常数池,字段和方法数据以及方法和构造函数的代码,包括类和实例初始化和接口初始化中使用的特殊方法(§2.9)。</p><p>The method area is created on virtual machine start-up. Although the method area is logically part of the heap, simple implementations may choose not to either garbage collect or compact it. This specification does not mandate the location of the method area or the policies used to manage compiled code. The method area may be of a fixed size or may be expanded as required by the computation and may be contracted if a larger method area becomes unnecessary. The memory for the method area does not need to be contiguous.</p><p>方法区域是在<strong>虚拟机启动时创建</strong>的。尽管方法区域*<strong>在逻辑上是堆的一部分</strong>,但简单的实现可能会选择不收集或压实垃圾。该规范不要求方法区域的位置或用于管理编译代码的策略。方法区域可能具有固定尺寸,也可以根据计算的要求进行扩展,如果不需要较大的方法区域,则可能会收缩。方法区域的内存不需要连续。</p><hr><h3 id="1-5-2-组成"><a href="#1-5-2-组成" class="headerlink" title="1.5.2 组成"></a>1.5.2 组成</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806170008456.png" alt="image-20220806170008456"></p><p>JDK1.8之前方法区用的实际上还是堆的内存;JDK1.8之后直接使用的操作系统的内存空间</p><hr><h3 id="1-5-3-方法区内存溢出"><a href="#1-5-3-方法区内存溢出" class="headerlink" title="1.5.3 方法区内存溢出"></a>1.5.3 方法区内存溢出</h3><p>内存不够都会溢出,只不过1.8之后使用操作系统的内存空间,很难溢出,可以自行设置大小;</p><hr><h3 id="1-5-4-StringTable"><a href="#1-5-4-StringTable" class="headerlink" title="1.5.4 StringTable"></a>1.5.4 StringTable</h3><h4 id="1-5-4-1-面试题"><a href="#1-5-4-1-面试题" class="headerlink" title="1.5.4.1 面试题"></a>1.5.4.1 面试题</h4><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806170243400.png" alt="image-20220806170243400"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806170303658.png" alt="image-20220806170303658"></p><p><strong>System.Out.println(s3 == s4); false</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806170354667.png" alt="image-20220806170354667"></p><p><strong>System.Out.println(s3 == s5); true</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806170435116.png" alt="image-20220806170435116"></p><p>简单来说,就是常量不会变;变量会新建;更详细解释看下节绿字</p><hr><h4 id="1-5-4-2-StringTable特性"><a href="#1-5-4-2-StringTable特性" class="headerlink" title="1.5.4.2 StringTable特性"></a>1.5.4.2 StringTable特性</h4><ul><li>常量池中的字符串仅是符号,第一次用到时才变为对象</li><li>字符串变量拼接的原理是 StringBuilder (1.8)</li><li>字符串常量拼接的原理是编译期优化</li><li>可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池<ul><li>1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回</li><li>1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,<strong>如果没有会把此对象复制一份(此时堆中的和串池中的已经不一样了)</strong>,放入串池, 会把串池中的对象返回</li><li>详细如下图</li></ul></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806170806080.png" alt="image-20220806170806080"></p><p>s仍然在堆中,复制了一份s放入串池并赋给s2;所以s2 == x;s!= x</p><p>在1.8中 没有复制而是直接放入,所以s2 == x;s == x</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806170831409.png" alt="image-20220806170831409"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806170843063.png" alt="image-20220806170843063"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806170853985.png" alt="image-20220806170853985"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806171000537.png" alt="image-20220806171000537"></p><hr><h4 id="1-5-4-3-StringTable位置"><a href="#1-5-4-3-StringTable位置" class="headerlink" title="1.5.4.3 StringTable位置"></a>1.5.4.3 StringTable位置</h4><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806171039427.png" alt="image-20220806171039427"></p><p>1.8串池用的是堆空间,1.6用的是永久代空间</p><hr><h4 id="1-5-4-4-StringTable垃圾回收"><a href="#1-5-4-4-StringTable垃圾回收" class="headerlink" title="1.5.4.4 StringTable垃圾回收"></a>1.5.4.4 StringTable垃圾回收</h4><p>StrnigTable存在垃圾回收机制</p><hr><h4 id="1-5-4-5-StringTable性能调优"><a href="#1-5-4-5-StringTable性能调优" class="headerlink" title="1.5.4.5 StringTable性能调优"></a>1.5.4.5 StringTable性能调优</h4><p>调整 -XX:StringTableSize=桶个数</p><p>桶的个数越多,哈希碰撞越少;因为每存放一个串时都要先查找当前StringTable中是否存在,若存在则不添加,不存在就添加;</p><p>如果存在字符串重复的问题,可以考虑将字符串入池,减少内存占用;</p><hr><h2 id="1-6-直接内存"><a href="#1-6-直接内存" class="headerlink" title="1.6 直接内存"></a>1.6 直接内存</h2><h3 id="1-6-1-定义"><a href="#1-6-1-定义" class="headerlink" title="1.6.1 定义"></a>1.6.1 定义</h3><p>Direct Memory</p><ul><li><p>常见于 NIO 操作时,用于数据缓冲区</p></li><li><p>分配回收成本较高,但读写性能高</p></li><li><p>不受 JVM 内存回收管理</p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220806171447515.png" alt="image-20220806171447515"></p><hr><h3 id="1-6-2-分配和回收原理"><a href="#1-6-2-分配和回收原理" class="headerlink" title="1.6.2 分配和回收原理"></a>1.6.2 分配和回收原理</h3><p>没太理解:<a href="https://www.bilibili.com/video/BV1yE411Z7AP?p=45">45_直接内存_释放原理_哔哩哔哩_bilibili</a></p><ul><li>使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要<strong>主动调用</strong> freeMemory 方法</li><li>ByteBuffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffer 对象,一旦ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean方法调 (弹幕:就是Java对象被垃圾回收会触发直接内存回收)</li><li>用 freeMemory 来释放直接内存</li></ul><hr><h1 id="2-垃圾回收"><a href="#2-垃圾回收" class="headerlink" title="2. 垃圾回收"></a>2. 垃圾回收</h1><h2 id="2-1-如何判断对象可以回收"><a href="#2-1-如何判断对象可以回收" class="headerlink" title="2.1 如何判断对象可以回收"></a>2.1 如何判断对象可以回收</h2><h3 id="2-1-1-引用计数法"><a href="#2-1-1-引用计数法" class="headerlink" title="2.1.1 引用计数法"></a>2.1.1 引用计数法</h3><p><strong>只要一个对象被其他变量所引用,就让它的计数加1</strong>,被引用了两次就让它的计数变成2,当这个变量的计数变成0时,就可以被垃圾回收;</p><p>弊端:当出现如下图的情况,A对象引用了B对象,B对象也引用了A对象,所以A,B的计数均为1,但是没有其他的引用它们俩;<strong>虽然应该被垃圾回收,但是因为计数不为0,则无法进行回收</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811163138061.png" alt="image-20220811163138061"></p><hr><h3 id="2-1-2-可达性计数法"><a href="#2-1-2-可达性计数法" class="headerlink" title="2.1.2 可达性计数法"></a>2.1.2 可达性计数法</h3><ul><li><p>Java 虚拟机中的垃圾回收器采用<strong>可达性分析</strong>来探索所有存活的对象</p></li><li><p>扫描堆中的对象,看是否能够沿着 <strong>GC Root对象(根对象)</strong> 为起点的引用链找到该对象,找不到,表示可以回收<strong>(就是先确定好一定不会被回收的对象作为根对象,然后查找目标对象是否直接或者间接被根对象所引用)</strong></p></li><li><p>哪些对象可以作为 GC Root ?</p><ul><li>GCRoot对象包括:虚拟机栈终点局部变量表引用的对象,方法区中类静态属性引用和常量引用对象,本地方法栈中的对象</li></ul></li></ul><hr><h3 id="2-1-3-五种引用-面试常考"><a href="#2-1-3-五种引用-面试常考" class="headerlink" title="2.1.3 五种引用(面试常考)"></a>2.1.3 五种引用(面试常考)</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811163328291.png" alt="image-20220811163328291"></p><h4 id="2-1-3-1-强引用"><a href="#2-1-3-1-强引用" class="headerlink" title="2.1.3.1 强引用"></a>2.1.3.1 强引用</h4><ul><li><p>只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收</p><p>如上图、只有B、C对象都不引用A1对象时,A1对象才会在垃圾回收时被回收;</p></li></ul><hr><h4 id="2-1-3-2-软引用(SoftReference)"><a href="#2-1-3-2-软引用(SoftReference)" class="headerlink" title="2.1.3.2 软引用(SoftReference)"></a>2.1.3.2 软引用(SoftReference)</h4><ul><li>仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会<strong>再次</strong>出发垃圾回收,回收软引用对象(实在不行了才回收软引用对象)</li><li>可以配合引用队列来释放软引用自身(因为软引用对象自身也是占一点内存的)</li></ul><p><strong>软引用的使用</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811163603544.png" alt="image-20220811163603544"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811163628973.png" alt="image-20220811163628973"></p><p>可见前四次已经被回收了;</p><p><strong>引用队列的使用</strong>:</p><p>如果在垃圾回收时发现内存不足,在回收软引用所指向的对象时,<strong>软引用本身不会被清理(就是上图结果中的null值)</strong></p><p>如果想要<strong>清理软引用</strong>,需要使<strong>用引用队列</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811163843846.png" alt="image-20220811163843846"></p><hr><h4 id="2-1-3-3-弱引用(WeakReference)"><a href="#2-1-3-3-弱引用(WeakReference)" class="headerlink" title="2.1.3.3 弱引用(WeakReference)"></a>2.1.3.3 弱引用(WeakReference)</h4><ul><li><p>仅有弱引用引用该对象时,在垃圾回收时,**(full gc时)无论内存是否充足<strong>,</strong>都会回收**弱引用对象</p></li><li><p>可以配合引用队列来释放弱引用自身</p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811164041023.png" alt="image-20220811164041023"></p><p>结合引用队列同软引用相似</p><hr><h4 id="2-1-3-4-虚引用(PhantomReference)"><a href="#2-1-3-4-虚引用(PhantomReference)" class="headerlink" title="2.1.3.4 虚引用(PhantomReference)"></a>2.1.3.4 虚引用(PhantomReference)</h4><ul><li>必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队由 Reference Handler 线程调用虚引用相关方法释放直接内存</li></ul><hr><h4 id="2-1-3-5-终结器引用(FinalReference)"><a href="#2-1-3-5-终结器引用(FinalReference)" class="headerlink" title="2.1.3.5 终结器引用(FinalReference)"></a>2.1.3.5 终结器引用(FinalReference)</h4><ul><li>无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize方法,第二次 GC 时才能回收被引用对象</li></ul><hr><h2 id="2-2-垃圾回收算法-重要"><a href="#2-2-垃圾回收算法-重要" class="headerlink" title="2.2 垃圾回收算法(重要)"></a>2.2 垃圾回收算法(重要)</h2><h3 id="2-2-1-标记清除算法"><a href="#2-2-1-标记清除算法" class="headerlink" title="2.2.1 标记清除算法"></a>2.2.1 标记清除算法</h3><p><strong>定义</strong>:标记清除算法顾名思义,是指在虚拟机执行垃圾回收的过程中,先采用标记算法确定可回收对象,然后垃圾收集器根据标识清除相应的内容,给堆内存腾出相应的空间</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811164255455.png" alt="image-20220811164255455"></p><p>清除后,对于腾出内存空间并不是将内存空间的字节清0,<strong>而是会把被清除对象所占用内存的起始结束的地址记录下来,放入空闲的地址列表中</strong>,下次分配内存的时候,再选择合适的位置存入,直接覆盖</p><p><strong>优点</strong>:速度快;</p><p><strong>缺点</strong>:容易产生大量的内存碎片,可能无法满足大对象的内存分配,一旦导致无法分配对象,那就会导致jvm启动gc,一旦启动gc,我们的应用程序就会暂停,这就导致应用的响应速度变慢</p><hr><h3 id="2-2-2-标记整理算法"><a href="#2-2-2-标记整理算法" class="headerlink" title="2.2.2 标记整理算法"></a>2.2.2 标记整理算法</h3><p>标记-整理 会将不被GC Root引用的对象回收,清楚其占用的内存空间。然后整理剩余的对象,<strong>速度慢、可以有效避免因内存碎片</strong>而导致的问题,但是因为整体需要消耗一定的时间,所以效率较低</p><hr><h3 id="2-2-3-复制算法"><a href="#2-2-3-复制算法" class="headerlink" title="2.2.3 复制算法"></a>2.2.3 复制算法</h3><p>复制算法将内存分为等大小的两个区域,FROM和TO(<strong>TO中始终为空</strong>)。先将被GC Root引用的对象从FROM放入TO中,再回收不被GC Root引用的对象。然后交换FROM和TO。这样也可以避免内存碎片的问题,但是会占用双倍的内存空间。</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811164455902.png" alt="image-20220811164455902"></p><hr><h2 id="2-3-分代垃圾回收"><a href="#2-3-分代垃圾回收" class="headerlink" title="2.3 分代垃圾回收"></a>2.3 分代垃圾回收</h2><h3 id="2-3-1回收流程"><a href="#2-3-1回收流程" class="headerlink" title="2.3.1回收流程"></a>2.3.1回收流程</h3><p>① 新创建的对象都被放在<strong>新生代的伊甸园</strong>中</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811164541767.png" alt="image-20220811164541767"></p><p>② 当伊甸园空间不足时,会采用<strong>复制算法</strong>进行垃圾回收,这时的回收叫做<strong>Minor GC</strong>;把<strong>伊甸园和幸存区From</strong>存活的对象先复制到幸存区To中,此时<strong>存活的对象寿命+1</strong>,并清理掉未存活的对象,最后再交换幸存区From和幸存区To;</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811164612870.png" alt="image-20220811164612870"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811164720253.png" alt="image-20220811164720253"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811164727571.png" alt="image-20220811164727571"></p><p>③ 再次创建对象,若新生代的伊甸园又满了,则同上;</p><p>④ 如果经历多次垃圾回收,某一对象均未被回收,寿命不断+1,当寿命达到阈值时(最大为15,4bit)就会被放入老年代中;</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811164852925.png" alt="image-20220811164852925"></p><p>⑤ 如果老年代中的内存都满了,就会先触发Minor GC 如果内存还是不足,则会触发<strong>Full GC</strong>,扫描<strong>新生代和老年代中</strong>所有不再使用的对象并回收、</p><p><strong>总结</strong></p><ul><li><p>对象首先分配在伊甸园区域</p></li><li><p>新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的对象年龄加 1并且交换 from to</p></li><li><p><strong>minor gc</strong> <strong>会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行</strong></p></li><li><p>当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)</p></li><li><p>当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STW的时间更长</p></li></ul><hr><h3 id="2-3-2-GC-分析"><a href="#2-3-2-GC-分析" class="headerlink" title="2.3.2 GC 分析"></a>2.3.2 GC 分析</h3><p><a href="https://www.bilibili.com/video/BV1yE411Z7AP?p=66">代码-GC分析</a></p><h4 id="2-3-2-1-大对象处理策略"><a href="#2-3-2-1-大对象处理策略" class="headerlink" title="2.3.2.1 大对象处理策略"></a>2.3.2.1 大对象处理策略</h4><p>当遇到一个<strong>较大的对象</strong>时,就算新生代的<strong>伊甸园</strong>为空,也<strong>无法容纳该对象</strong>时,会将该对象<strong>直接晋升为老年代</strong></p><hr><h4 id="2-3-2-2-线程内存溢出"><a href="#2-3-2-2-线程内存溢出" class="headerlink" title="2.3.2.2 线程内存溢出"></a>2.3.2.2 线程内存溢出</h4><p>某个线程的内存溢出了而抛异常(out of memory),不会让其他的线程结束运行</p><p>这是因为当一个线程<strong>抛出OOM异常后</strong>,<strong>它所占据的内存资源会全部被释放掉</strong>,从而不会影响其他线程的运行,<strong>进程依然正常</strong></p><hr><h2 id="2-4-垃圾回收器"><a href="#2-4-垃圾回收器" class="headerlink" title="2.4 垃圾回收器"></a>2.4 垃圾回收器</h2><p>串行</p><ul><li><p>单线程</p></li><li><p>堆内存较小,适合个人电脑</p></li></ul><hr><p>吞吐量优先</p><ul><li><p>多线程</p></li><li><p>堆内存较大,多核 cpu</p></li><li><p>让单位时间内,STW 的时间最短 0.2 0.2 = 0.4,垃圾回收时间占比最低,这样就称吞吐量高<strong>(少餐多食)</strong></p></li></ul><hr><p>响应时间优先</p><ul><li><p>多线程</p></li><li><p>堆内存较大,多核 cpu</p></li><li><p>尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5<strong>(少食多餐)</strong></p></li></ul><hr><h3 id="2-4-1-串行"><a href="#2-4-1-串行" class="headerlink" title="2.4.1 串行"></a>2.4.1 串行</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811170019886.png" alt="image-20220811170019886"></p><p><strong>安全点</strong>:</p><p>让其他线程都在这个点停下来,以免垃圾回收时移动对象地址,使得其他线程找不到被移动的对象;因为是串行的,所以只有一个垃圾回收线程。且在该线程执行回收工作时,其他线程进入<strong>阻塞</strong>状态</p><p><strong>Serial</strong> <strong>收集器</strong>:</p><p>Serial收集器是最基本的、发展历史最悠久的收集器</p><p>特点:单线程、简单高效(与其他收集器的单线程相比),用于新生代<strong>采用复制算法</strong>。对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程手机效率。收集器进行垃圾回收时,<strong>必须暂停其他所有的工作线程,直到它结束(Stop The World)</strong></p><p><strong>Serial Old</strong> <strong>收集器</strong>:</p><p>Serial Old是Serial收集器的<strong>老年代</strong>版本</p><p>特点:同样是单线程收集器,<strong>采用标记-整理算法</strong></p><hr><h3 id="2-4-2-吞吐量优先"><a href="#2-4-2-吞吐量优先" class="headerlink" title="2.4.2 吞吐量优先"></a>2.4.2 吞吐量优先</h3><p><strong>-XX:+UseParallelGC ~ -XX:+UserParallelOldGC</strong>:</p><p>开启吞吐量优先的回收器,1.8版本默认开启;<strong>UseParallelGC是新生代的,采用复制算法;UserParallelOldGC是在老年代的,采用标记整理算法</strong></p><p> <img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811165910107.png"></p><p><strong>-XX:+UseAdaptiveSizePolicy</strong>:</p><p>开启这个将采用自适应的大小调整策略,调整新生代的大小,包括堆的大小和晋升老年代的阈值大小等;种调节方式称为<strong>GC的自适应调节策略</strong></p><p><strong>-XX:GCTimeRatio=ratio</strong>:</p><p>调整吞吐量的目标,即垃圾回收的时间与总时间的占比(1/(1+ratio)),默认ratio=99,1/(1+ratio))= 0.01,即垃圾回收的时间不能超过总时间的1%(比如总时间100分钟,垃圾回收的时间不能超过1分钟,如果超过1分钟,则GC会自适应的调整大小)</p><p><strong>-XX:MaxGCPauseMillis=ms</strong>:</p><p>指的是暂停的毫秒数,默认200ms,即上图红线<strong>(和Ratio对立,折中选取)</strong></p><p><strong>-XX:ParallelGCThreads=n</strong>:</p><p>设置垃圾回收时运行的线程数</p><hr><h3 id="2-4-3-响应时间优先(CMS)"><a href="#2-4-3-响应时间优先(CMS)" class="headerlink" title="2.4.3 响应时间优先(CMS)"></a>2.4.3 响应时间优先(CMS)</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811165832530.png" alt="image-20220811165832530"></p><p><strong>CMS</strong> <strong>收集器</strong></p><p><strong>Concurrent Mark Sweep</strong>,一种以获取<strong>最短回收停顿时间</strong>为目标的<strong>老年代</strong>收集器</p><p><strong>特点</strong>:基于<strong>标记-清除算法</strong>实现。并发收集、低停顿,但是会产生内存碎片</p><p><strong>应用场景</strong>:适用于注重服务的响应速度,希望系统停顿时间最短,给用户带来更好的体验等场景下。如web程序、b/s服务</p><p><strong>CMS收集器的运行过程分为下列4步:</strong></p><p><strong>初始标记</strong>:标记GC Roots能直接到的对象。速度很快但是<strong>仍存在Stop The World问题</strong></p><p><strong>并发标记</strong>:进行GC Roots Tracing 的过程,找出存活对象且用户线程可并发执行</p><p><strong>重新标记</strong>:为了<strong>修正并发标记期间</strong>因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。仍然存在Stop The World问题</p><p><strong>并发清除</strong>:对标记的对象进行清除回收</p><blockquote><p>CMS收集器的内存回收过程是与用户线程一起<strong>并发执行</strong>的</p></blockquote><p>图解:</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811170156714.png" alt="image-20220811170156714"></p><hr><h3 id="2-4-4-G1-Garbage-First"><a href="#2-4-4-G1-Garbage-First" class="headerlink" title="2.4.4 G1(Garbage First)"></a>2.4.4 G1(Garbage First)</h3><p>JDK1.9默认采用G1垃圾回收器,且废弃的CMS回收器</p><h4 id="2-4-4-1-适用场景"><a href="#2-4-4-1-适用场景" class="headerlink" title="2.4.4.1 适用场景"></a>2.4.4.1 适用场景</h4><ul><li><p>同时注重吞吐量(Throughput)和低延时(Low latency),默认的暂停目标是200ms</p></li><li><p>超大堆内存,会将堆划分为多个大小相等的区域,称为Region,每个区域都可以独立的作为伊甸园或者新生代或者老年代</p></li><li><p>整体上是标记-整理算法,在两个区域之间是复制算法</p></li></ul><hr><h4 id="2-4-4-2-相关jvm参数"><a href="#2-4-4-2-相关jvm参数" class="headerlink" title="2.4.4.2 相关jvm参数"></a>2.4.4.2 相关jvm参数</h4><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811170405861.png" alt="image-20220811170405861"></p><hr><h4 id="2-4-4-3-G1垃圾回收阶段"><a href="#2-4-4-3-G1垃圾回收阶段" class="headerlink" title="2.4.4.3 G1垃圾回收阶段"></a>2.4.4.3 G1垃圾回收阶段</h4><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811170444758.png" alt="image-20220811170444758"></p><p>新生代伊甸园垃圾回收—–>内存不足,新生代回收+并发标记—–>回收新生代伊甸园、幸存区、老年代内存——>新生代伊甸园垃圾回收(重新开始)</p><h5 id="Young-Collection"><a href="#Young-Collection" class="headerlink" title="Young Collection"></a>Young Collection</h5><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811170657465.png" alt="image-20220811170657465"></p><hr><h5 id="Young-Collection-CM"><a href="#Young-Collection-CM" class="headerlink" title="Young Collection + CM"></a>Young Collection + CM</h5><ul><li><p>在 Young GC 时会进行 GC Root 的初始标记</p></li><li><p>并发标记(CM)是从GC Root出发顺着其引用链标记其他对象</p></li><li><p>老年代占用堆空间比例达到阈值时,进行并发标记(不会 STW),由下面的 JVM 参数决定</p></li></ul><blockquote><p>-XX:InitiatingHeapOccupancyPercent=percent (默认45%)<strong>如下图 O 占总的45%时进行并发标记</strong></p></blockquote><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811170809004.png" alt="image-20220811170809004"></p><hr><h5 id="Mixed-Collection"><a href="#Mixed-Collection" class="headerlink" title="Mixed Collection"></a>Mixed Collection</h5><p>会对 E、S、O <strong>进行全面垃圾回收</strong></p><p>最终标记(<strong>Remark见2.4.4.6</strong>)会 STW</p><p>拷贝存活(Evacuation)会 STW</p><p>-XX:MaxGCPauseMillis=ms</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811170917470.png" alt="image-20220811170917470"></p><p>黑线:即新生代回收,包括伊甸园、幸存区</p><p>红线:复制老年代</p><p><strong>问</strong>:为什么有的老年代被拷贝了,有的没拷贝?</p><p><strong>答:</strong>因为指定了最大停顿时间,如果对所有老年代都进行回收,耗时可能过高。为了保证时间不超过设定的停顿时间,会<strong>回收最有价值的老年代</strong>(回收后,能够得到更多内存)</p><hr><h4 id="2-4-4-4-Full-GC"><a href="#2-4-4-4-Full-GC" class="headerlink" title="2.4.4.4 Full GC"></a>2.4.4.4 Full GC</h4><p>G1在老年代内存不足时(老年代所占内存超过阈值)</p><p>如果垃圾产生速度<strong>慢于</strong>垃圾回收速度,不会触发Full GC,还是并发地进行清理</p><p>如果垃圾产生速度<strong>快于</strong>垃圾回收速度,便会触发Full GC,退化到串行,STW时间会很长</p><hr><h4 id="2-4-4-5-Young-Collection跨代引用"><a href="#2-4-4-5-Young-Collection跨代引用" class="headerlink" title="2.4.4.5 Young Collection跨代引用"></a>2.4.4.5 Young Collection跨代引用</h4><p>即老年代引用新生代的问题,因为在新生代回收过程中会沿着GC Root去标记,这些GC Root可能存在于老年代中,而一般老年代中的对象较多,这样每次都需要遍历大量对象;</p><p><strong>解决方法:</strong></p><ul><li><p>卡表与Remembered Set:</p><ul><li>Remembered Set 存在于E中,用于保存新生代对象对应的脏卡,将来对新生代进行垃圾回收时,会根据Remembered Set判断有哪些脏卡,然后再从这些脏卡开始<ul><li>脏卡:O被划分为多个区域(一个区域512K),如果该区域引用了新生代对象,则该区域被称为脏卡</li></ul></li></ul></li><li><p>在引用变更时通过post-write barried + dirty card queue,相当于更新脏卡</p></li><li><p>concurrent refinement threads 更新 Remembered Set</p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811171202341.png" alt="image-20220811171202341"></p><hr><h4 id="2-4-4-6-Reamark重标记"><a href="#2-4-4-6-Reamark重标记" class="headerlink" title="2.4.4.6 Reamark重标记"></a>2.4.4.6 Reamark重标记</h4><p>黑色:已被处理,需要保留的 </p><p>灰色:正在处理中的 </p><p>白色:还未处理的</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811171419280.png" alt="image-20220811171419280"></p><p> ① 这个时候将处理B,处理结果如下:</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811171445088.png" alt="image-20220811171445088"></p><p>② 下一步将处理C,此时正在并发执行标记,用户线程可能改变B和C的引用关系,会产生如下情况:</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811171506442.png" alt="image-20220811171506442"></p><p>③ 此时C已经被处理完了,被标记成了白色,<strong>但是用户线程可能在这时又改变了C的引用:</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811171531614.png" alt="image-20220811171531614"></p><p>④ 此时C应该被标记成黑色,但是标记动作已经结束,所以会产生误差;</p><p><strong>解决方法:采用Remark</strong></p><p>过程如下</p><p>① 之前C未被引用,这时A引用了C,就会给C加一个写屏障,写屏障的指令会被执行<strong>(只有发生引用改变就会执行)</strong>,将C放入一个队列当中,并将C变为 处理中 状态</p><p>② 在并发标记阶段结束以后,重新标记阶段会STW,然后将放在该队列中的对象重新处理发现有强引用引用它,就会处理它</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811171613821.png" alt="image-20220811171613821"></p><hr><h4 id="2-4-4-7-JDK8u20字符串去重"><a href="#2-4-4-7-JDK8u20字符串去重" class="headerlink" title="2.4.4.7 JDK8u20字符串去重"></a>2.4.4.7 JDK8u20字符串去重</h4><p><strong>过程</strong></p><p>① 将所有新分配的字符串(底层是char[])放入一个队列</p><p>② 当新生代回收时,G1并发检查是否有重复的字符串</p><p>③ 如果字符串的值一样,就让他们引用同一个字符串对象</p><p> <img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811171806398.png" alt="image-20220811171806398"></p><p>注意,其与String.intern的区别</p><p><strong>intern关注的是字符串对象</strong></p><p>字符串去重关注的是<strong>char[]</strong></p><p>在JVM内部,使用了不同的字符串标</p><p><strong>优点与缺点</strong></p><ul><li><p>节省了大量内存</p></li><li><p>新生代回收时间略微增加,导致略微多占用CPU</p></li></ul><hr><h4 id="2-4-4-8-JDK-8u40-并发标记类卸载"><a href="#2-4-4-8-JDK-8u40-并发标记类卸载" class="headerlink" title="2.4.4.8 JDK 8u40 并发标记类卸载"></a>2.4.4.8 JDK 8u40 并发标记类卸载</h4><p>在并发标记阶段结束以后,就能知道哪些类不再被使用。如果一个类加载器的所有类都不在使用,则卸载它所加载的所有类</p><p>-XX:+ClassUnloadingWithConcurrentMark 默认启用</p><hr><h4 id="2-4-4-9-JDK-8u60-回收巨型对象"><a href="#2-4-4-9-JDK-8u60-回收巨型对象" class="headerlink" title="2.4.4.9 JDK 8u60 回收巨型对象"></a>2.4.4.9 JDK 8u60 回收巨型对象</h4><ul><li>一个对象大于region的一半时,就称为巨型对象</li><li>G1不会对巨型对象进行拷贝</li><li>回收时被优先考虑</li><li>G1会跟踪老年代所有incoming引用,如果老年代incoming引用为0的巨型对象就可以在新生代垃圾回收时处理掉</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220811172031981.png" alt="image-20220811172031981"></p><hr><h4 id="2-4-4-10-JDK9并发标记起始时间的调整"><a href="#2-4-4-10-JDK9并发标记起始时间的调整" class="headerlink" title="2.4.4.10 JDK9并发标记起始时间的调整"></a>2.4.4.10 JDK9并发标记起始时间的调整</h4><p>2.4.4.4提到当垃圾产生速度快于垃圾回收速度,便会触发Full GC,STW的时间就会变长,这个时候就可以通过调整<strong>提前开始</strong>并发标记来优化</p><ul><li><p>并发标记必须在堆空间占满前完成,否则退化为 FullGC</p></li><li><p>JDK 9 之前需要使用 -XX:InitiatingHeapOccupancyPercent</p></li><li><p>JDK 9 可以动态调整</p></li></ul><p>-XX:InitiatingHeapOccupancyPercent 用来设置初始值</p><p>进行数据采样并动态调整</p><p>总会添加一个安全的空档空间</p><hr><h2 id="2-5-垃圾回收调优"><a href="#2-5-垃圾回收调优" class="headerlink" title="2.5 垃圾回收调优"></a>2.5 垃圾回收调优</h2><h3 id="2-5-1-确定调优领域"><a href="#2-5-1-确定调优领域" class="headerlink" title="2.5.1 确定调优领域"></a>2.5.1 确定调优领域</h3><ul><li>内存</li><li>锁竞争</li><li>CPU占用</li><li>IO</li><li>GC</li></ul><hr><h3 id="2-5-2确定目标"><a href="#2-5-2确定目标" class="headerlink" title="2.5.2确定目标"></a>2.5.2确定目标</h3><p>低延迟/高吞吐量? 选择合适的GC</p><ul><li>CMS(JDK8默认)、 G1(JDK9推荐) 、ZGC(JDK12体验)</li><li>ParallelGC(高吞吐量)</li><li>Zing(另一种虚拟机)</li></ul><hr><h3 id="2-5-3最快的GC是不发生GC"><a href="#2-5-3最快的GC是不发生GC" class="headerlink" title="2.5.3最快的GC是不发生GC"></a>2.5.3最快的GC是不发生GC</h3><p>首先排除减少因为自身编写的代码而引发的内存问题</p><ul><li><p>查看Full GC前后的内存占用,考虑以下几个问题</p></li><li><ul><li><p>数据是不是太多?(比如select *)</p></li><li><p>数据表示是否太臃肿</p></li><li><ul><li>对象图</li><li>对象大小(比如用Integer换成int会小很多)</li></ul></li><li><p>是否存在内存泄漏(采用软、弱引用;第三方缓存实现等)</p></li></ul></li></ul><hr><h3 id="2-5-4新生代调优"><a href="#2-5-4新生代调优" class="headerlink" title="2.5.4新生代调优"></a>2.5.4新生代调优</h3><p><strong>新生代的特点</strong></p><ul><li><p>所有的new操作分配内存都是非常廉价的</p></li><li><p>死亡对象回收零代价</p></li></ul><ul><li>大部分对象用过即死(朝生夕死)</li></ul><ul><li>MInor GC 所用时间远小于Full GC</li></ul><p>问:新生代内存越大越好么?</p><p>答:不是</p><ul><li><p>新生代内存太小:频繁触发Minor GC,会STW,会使得吞吐量下降</p></li><li><p>新生代内存太大:老年代内存占比有所降低,会更频繁地触发Full GC。而且触发Minor GC时,清理新生代所花费的时间会更长</p></li><li><p>新生代内存设置为能容纳所有【并发量*(请求-响应)】的数据为宜</p></li></ul><hr><h3 id="2-5-5幸存区调优"><a href="#2-5-5幸存区调优" class="headerlink" title="2.5.5幸存区调优"></a>2.5.5幸存区调优</h3><ul><li><p>幸存区需要能够保存 <strong>当前活跃对象+需要晋升的对象</strong></p></li><li><p>晋升阈值配置得当,让长时间存活的对象尽快晋升</p></li></ul><p>-XX:MaxTenuringThreshold=threshold<br> -XX:+PrintTenuringDistribution</p><hr><h3 id="2-5-6老年代调优"><a href="#2-5-6老年代调优" class="headerlink" title="2.5.6老年代调优"></a>2.5.6老年代调优</h3><p>以 CMS 为例</p><ul><li><p>CMS 的老年代内存越大越好</p></li><li><p>先尝试不做调优,如果没有 Full GC 那么已经…,否则先尝试调优新生代</p></li><li><p>观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3</p></li></ul><p>-XX:CMSInitiatingOccupancyFraction=percent</p><h1 id="3-类加载与字节码技术"><a href="#3-类加载与字节码技术" class="headerlink" title="3. 类加载与字节码技术"></a>3. 类加载与字节码技术</h1><h2 id="3-1-类文件结构"><a href="#3-1-类文件结构" class="headerlink" title="3.1 类文件结构"></a>3.1 类文件结构</h2><p>首先获得.class字节码文件</p><p>方法:</p><ul><li>在文本文档里写入java代码(文件名与类名一致),将文件类型改为.java</li><li>java终端中,执行javac X:…\XXX.java</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220808172918938.png" alt="image-20220808172918938"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// HelloWorld 示例</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">HelloWorld</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> System.out.println(<span class="hljs-string">"hello world"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><p>以下是字节码文件</p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">0000000</span> ca fe ba be <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">34</span> <span class="hljs-number">00</span> <span class="hljs-number">23</span> <span class="hljs-number">0</span>a <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">00</span> <span class="hljs-number">15</span> <span class="hljs-number">09</span> <br><span class="hljs-attribute">0000020</span> <span class="hljs-number">00</span> <span class="hljs-number">16</span> <span class="hljs-number">00</span> <span class="hljs-number">17</span> <span class="hljs-number">08</span> <span class="hljs-number">00</span> <span class="hljs-number">18</span> <span class="hljs-number">0</span>a <span class="hljs-number">00</span> <span class="hljs-number">19</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>a <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>b <span class="hljs-number">07</span> <br><span class="hljs-attribute">0000040</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>c <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">3</span>c <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">69</span> <span class="hljs-number">74</span> <span class="hljs-number">3</span>e <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">03</span> <span class="hljs-number">28</span> <span class="hljs-number">29</span> <br><span class="hljs-attribute">0000060</span> <span class="hljs-number">56</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">43</span> <span class="hljs-number">6</span>f <span class="hljs-number">64</span> <span class="hljs-number">65</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>f <span class="hljs-number">4</span>c <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">65</span> <span class="hljs-number">4</span>e <br><span class="hljs-attribute">0000100</span> <span class="hljs-number">75</span> <span class="hljs-number">6</span>d <span class="hljs-number">62</span> <span class="hljs-number">65</span> <span class="hljs-number">72</span> <span class="hljs-number">54</span> <span class="hljs-number">61</span> <span class="hljs-number">62</span> <span class="hljs-number">6</span>c <span class="hljs-number">65</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">12</span> <span class="hljs-number">4</span>c <span class="hljs-number">6</span>f <span class="hljs-number">63</span> <br><span class="hljs-attribute">0000120</span> <span class="hljs-number">61</span> <span class="hljs-number">6</span>c <span class="hljs-number">56</span> <span class="hljs-number">61</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> <span class="hljs-number">61</span> <span class="hljs-number">62</span> <span class="hljs-number">6</span>c <span class="hljs-number">65</span> <span class="hljs-number">54</span> <span class="hljs-number">61</span> <span class="hljs-number">62</span> <span class="hljs-number">6</span>c <span class="hljs-number">65</span> <span class="hljs-number">01</span> <br><span class="hljs-attribute">0000140</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">74</span> <span class="hljs-number">68</span> <span class="hljs-number">69</span> <span class="hljs-number">73</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>d <span class="hljs-number">4</span>c <span class="hljs-number">63</span> <span class="hljs-number">6</span>e <span class="hljs-number">2</span>f <span class="hljs-number">69</span> <span class="hljs-number">74</span> <span class="hljs-number">63</span> <br><span class="hljs-attribute">0000160</span> <span class="hljs-number">61</span> <span class="hljs-number">73</span> <span class="hljs-number">74</span> <span class="hljs-number">2</span>f <span class="hljs-number">6</span>a <span class="hljs-number">76</span> <span class="hljs-number">6</span>d <span class="hljs-number">2</span>f <span class="hljs-number">74</span> <span class="hljs-number">35</span> <span class="hljs-number">2</span>f <span class="hljs-number">48</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>c <span class="hljs-number">6</span>f <br><span class="hljs-attribute">0000200</span> <span class="hljs-number">57</span> <span class="hljs-number">6</span>f <span class="hljs-number">72</span> <span class="hljs-number">6</span>c <span class="hljs-number">64</span> <span class="hljs-number">3</span>b <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">6</span>d <span class="hljs-number">61</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">16</span> <br><span class="hljs-attribute">0000220</span> <span class="hljs-number">28</span> <span class="hljs-number">5</span>b <span class="hljs-number">4</span>c <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2</span>f <span class="hljs-number">6</span>c <span class="hljs-number">61</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2</span>f <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> <br><span class="hljs-attribute">0000240</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">3</span>b <span class="hljs-number">29</span> <span class="hljs-number">56</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">61</span> <span class="hljs-number">72</span> <span class="hljs-number">67</span> <span class="hljs-number">73</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">13</span> <br><span class="hljs-attribute">0000260</span> <span class="hljs-number">5</span>b <span class="hljs-number">4</span>c <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2</span>f <span class="hljs-number">6</span>c <span class="hljs-number">61</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2</span>f <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> <br><span class="hljs-attribute">0000300</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">3</span>b <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">4</span>d <span class="hljs-number">65</span> <span class="hljs-number">74</span> <span class="hljs-number">68</span> <span class="hljs-number">6</span>f <span class="hljs-number">64</span> <span class="hljs-number">50</span> <span class="hljs-number">61</span> <span class="hljs-number">72</span> <span class="hljs-number">61</span> <br><span class="hljs-attribute">0000320</span> <span class="hljs-number">6</span>d <span class="hljs-number">65</span> <span class="hljs-number">74</span> <span class="hljs-number">65</span> <span class="hljs-number">72</span> <span class="hljs-number">73</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>a <span class="hljs-number">53</span> <span class="hljs-number">6</span>f <span class="hljs-number">75</span> <span class="hljs-number">72</span> <span class="hljs-number">63</span> <span class="hljs-number">65</span> <span class="hljs-number">46</span> <br><span class="hljs-attribute">0000340</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>c <span class="hljs-number">65</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>f <span class="hljs-number">48</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>c <span class="hljs-number">6</span>f <span class="hljs-number">57</span> <span class="hljs-number">6</span>f <span class="hljs-number">72</span> <span class="hljs-number">6</span>c <span class="hljs-number">64</span><br><span class="hljs-attribute">0000360</span> <span class="hljs-number">2</span>e <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">08</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>d <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">1</span>e <br><span class="hljs-attribute">0000400</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>f <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>b <span class="hljs-number">68</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>c <span class="hljs-number">6</span>f <span class="hljs-number">20</span> <span class="hljs-number">77</span> <span class="hljs-number">6</span>f <span class="hljs-number">72</span> <span class="hljs-number">6</span>c <span class="hljs-number">64</span> <br><span class="hljs-attribute">0000420</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">20</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">21</span> <span class="hljs-number">00</span> <span class="hljs-number">22</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">1</span>b <span class="hljs-number">63</span> <span class="hljs-number">6</span>e <span class="hljs-number">2</span>f <span class="hljs-number">69</span> <span class="hljs-number">74</span> <br><span class="hljs-attribute">0000440</span> <span class="hljs-number">63</span> <span class="hljs-number">61</span> <span class="hljs-number">73</span> <span class="hljs-number">74</span> <span class="hljs-number">2</span>f <span class="hljs-number">6</span>a <span class="hljs-number">76</span> <span class="hljs-number">6</span>d <span class="hljs-number">2</span>f <span class="hljs-number">74</span> <span class="hljs-number">35</span> <span class="hljs-number">2</span>f <span class="hljs-number">48</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>c <br><span class="hljs-attribute">0000460</span> <span class="hljs-number">6</span>f <span class="hljs-number">57</span> <span class="hljs-number">6</span>f <span class="hljs-number">72</span> <span class="hljs-number">6</span>c <span class="hljs-number">64</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2</span>f <span class="hljs-number">6</span>c <span class="hljs-number">61</span> <br><span class="hljs-attribute">0000500</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2</span>f <span class="hljs-number">4</span>f <span class="hljs-number">62</span> <span class="hljs-number">6</span>a <span class="hljs-number">65</span> <span class="hljs-number">63</span> <span class="hljs-number">74</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <br><span class="hljs-attribute">0000520</span> <span class="hljs-number">2</span>f <span class="hljs-number">6</span>c <span class="hljs-number">61</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2</span>f <span class="hljs-number">53</span> <span class="hljs-number">79</span> <span class="hljs-number">73</span> <span class="hljs-number">74</span> <span class="hljs-number">65</span> <span class="hljs-number">6</span>d <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">03</span> <span class="hljs-number">6</span>f <br><span class="hljs-attribute">0000540</span> <span class="hljs-number">75</span> <span class="hljs-number">74</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">15</span> <span class="hljs-number">4</span>c <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2</span>f <span class="hljs-number">69</span> <span class="hljs-number">6</span>f <span class="hljs-number">2</span>f <span class="hljs-number">50</span> <span class="hljs-number">72</span> <br><span class="hljs-attribute">0000560</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">74</span> <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> <span class="hljs-number">65</span> <span class="hljs-number">61</span> <span class="hljs-number">6</span>d <span class="hljs-number">3</span>b <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">13</span> <span class="hljs-number">6</span>a <span class="hljs-number">61</span> <span class="hljs-number">76</span> <br><span class="hljs-attribute">0000600</span> <span class="hljs-number">61</span> <span class="hljs-number">2</span>f <span class="hljs-number">69</span> <span class="hljs-number">6</span>f <span class="hljs-number">2</span>f <span class="hljs-number">50</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">74</span> <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> <span class="hljs-number">65</span> <span class="hljs-number">61</span> <span class="hljs-number">6</span>d <br><span class="hljs-attribute">0000620</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">07</span> <span class="hljs-number">70</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">74</span> <span class="hljs-number">6</span>c <span class="hljs-number">6</span>e <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">15</span> <span class="hljs-number">28</span> <span class="hljs-number">4</span>c <span class="hljs-number">6</span>a <br><span class="hljs-attribute">0000640</span> <span class="hljs-number">61</span> <span class="hljs-number">76</span> <span class="hljs-number">61</span> <span class="hljs-number">2</span>f <span class="hljs-number">6</span>c <span class="hljs-number">61</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">2</span>f <span class="hljs-number">53</span> <span class="hljs-number">74</span> <span class="hljs-number">72</span> <span class="hljs-number">69</span> <span class="hljs-number">6</span>e <span class="hljs-number">67</span> <span class="hljs-number">3</span>b <br><span class="hljs-attribute">0000660</span> <span class="hljs-number">29</span> <span class="hljs-number">56</span> <span class="hljs-number">00</span> <span class="hljs-number">21</span> <span class="hljs-number">00</span> <span class="hljs-number">05</span> <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <br><span class="hljs-attribute">0000700</span> <span class="hljs-number">00</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">08</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">09</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">2</span>f <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <br><span class="hljs-attribute">0000720</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">05</span> <span class="hljs-number">2</span>a b7 <span class="hljs-number">00</span> <span class="hljs-number">01</span> b1 <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>a <span class="hljs-number">00</span> <br><span class="hljs-attribute">0000740</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">04</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>b <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <br><span class="hljs-attribute">0000760</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">05</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">0</span>d <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">09</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>e <span class="hljs-number">00</span> <br><span class="hljs-attribute">0001000</span> <span class="hljs-number">0</span>f <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">09</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">37</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <br><span class="hljs-attribute">0001020</span> <span class="hljs-number">09</span> b2 <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">12</span> <span class="hljs-number">03</span> b6 <span class="hljs-number">00</span> <span class="hljs-number">04</span> b1 <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>a <br><span class="hljs-attribute">0001040</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>a <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">06</span> <span class="hljs-number">00</span> <span class="hljs-number">08</span> <span class="hljs-number">00</span> <span class="hljs-number">07</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>b <br><span class="hljs-attribute">0001060</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">0</span>c <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">09</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">00</span> <span class="hljs-number">11</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <br><span class="hljs-attribute">0001100</span> <span class="hljs-number">00</span> <span class="hljs-number">12</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">05</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">10</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">01</span> <span class="hljs-number">00</span> <span class="hljs-number">13</span> <span class="hljs-number">00</span> <br><span class="hljs-attribute">0001120</span> <span class="hljs-number">00</span> <span class="hljs-number">00</span> <span class="hljs-number">02</span> <span class="hljs-number">00</span> <span class="hljs-number">14</span>Copy<br></code></pre></td></tr></table></figure><p>根据 JVM 规范,<strong>类文件结构</strong>如下</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs abnf">u4 magic<br>u2 minor_version<span class="hljs-comment">; </span><br>u2 major_version<span class="hljs-comment">; </span><br>u2 constant_pool_count<span class="hljs-comment">; </span><br>cp_info constant_pool[constant_pool_count-<span class="hljs-number">1</span>]<span class="hljs-comment">; </span><br>u2 access_flags<span class="hljs-comment">; </span><br>u2 this_class<span class="hljs-comment">; </span><br>u2 super_class<span class="hljs-comment">; </span><br>u2 interfaces_count<span class="hljs-comment">; </span><br>u2 interfaces[interfaces_count]<span class="hljs-comment">; </span><br>u2 fields_count<span class="hljs-comment">; </span><br>field_info fields[fields_count]<span class="hljs-comment">; </span><br>u2 methods_count<span class="hljs-comment">; </span><br>method_info methods[methods_count]<span class="hljs-comment">; </span><br>u2 attributes_count<span class="hljs-comment">; </span><br>attribute_info attributes[attributes_count]<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><h3 id="3-1-1-魔数"><a href="#3-1-1-魔数" class="headerlink" title="3.1.1 魔数"></a>3.1.1 魔数</h3><p>u4 magic</p><p>对应字节码文件的0~3个字节</p><p>0000000 <strong>ca fe ba be</strong> 00 00 00 34 00 23 0a 00 06 00 15 09</p><hr><h3 id="3-1-2-版本"><a href="#3-1-2-版本" class="headerlink" title="3.1.2 版本"></a>3.1.2 版本</h3><p>u2 minor_version;</p><p>u2 major_version;</p><p>0000000 ca fe ba be <strong>00 00 00 34</strong> 00 23 0a 00 06 00 15 09</p><p>34H = 52,代表JDK8</p><hr><h3 id="3-1-3-常量池"><a href="#3-1-3-常量池" class="headerlink" title="3.1.3 常量池"></a>3.1.3 常量池</h3><p>了解即可</p><blockquote><p>查看文档阅读:<a href="https://gitee.com/Gu-taicheng/image/raw/master/document/3_%E7%B1%BB%E5%8A%A0%E8%BD%BD%E4%B8%8E%E5%AD%97%E8%8A%82%E7%A0%81%E6%8A%80%E6%9C%AF.pdf">https://gitee.com/Gu-taicheng/image/raw/master/document/3_%E7%B1%BB%E5%8A%A0%E8%BD%BD%E4%B8%8E%E5%AD%97%E8%8A%82%E7%A0%81%E6%8A%80%E6%9C%AF.pdf</a></p></blockquote><hr><h2 id="3-2-字节码指令"><a href="#3-2-字节码指令" class="headerlink" title="3.2 字节码指令"></a>3.2 字节码指令</h2><h3 id="3-2-1-javap工具"><a href="#3-2-1-javap工具" class="headerlink" title="3.2.1 javap工具"></a>3.2.1 javap工具</h3><p>自己分析类文件结构太麻烦了,Oracle提供了<strong>javap工具</strong>来反编译class文件</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml">javap -v D:\<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Main</span>.</span></span><span class="hljs-keyword">class</span><br></code></pre></td></tr></table></figure><figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><code class="hljs lasso">C:\Thread_study>javap <span class="hljs-params">-v</span> D:\Main.class<br>Classfile /D:\Main.class<br> Last modified <span class="hljs-number">2020</span><span class="hljs-number">-6</span><span class="hljs-number">-6</span>; size <span class="hljs-number">434</span> <span class="hljs-built_in">bytes</span><br> MD5 checksum df1dce65bf6fb0b4c1de318051f4a67e<br> Compiled from <span class="hljs-string">"Demo1.java"</span><br><span class="hljs-keyword">public</span> class com.nyima.JVM.day5.Demo1<br> minor version: <span class="hljs-number">0</span><br> major version: <span class="hljs-number">52</span><br> flags: ACC_PUBLIC, ACC_SUPER<br>Constant pool:<br> #1 = Methodref #6.#15 <span class="hljs-comment">// java/lang/Object."<init>":()V</span><br> #2 = Fieldref #16.#17 <span class="hljs-comment">// java/lang/System.out:Ljava/io/PrintStream;</span><br> #3 = <span class="hljs-built_in">String</span> #18 <span class="hljs-comment">// hello world</span><br> #4 = Methodref #19.#20 <span class="hljs-comment">// java/io/PrintStream.println:(Ljava/lang/String;)V</span><br> #5 = Class #21 <span class="hljs-comment">// com/nyima/JVM/day5/Demo1</span><br> #6 = Class #22 <span class="hljs-comment">// java/lang/Object</span><br> #7 = Utf8 <init><br> #8 = Utf8 ()V<br> #9 = Utf8 Code<br> #10 = Utf8 LineNumberTable<br> #11 = Utf8 main<br> #12 = Utf8 (<span class="hljs-meta">[</span>Ljava/lang/<span class="hljs-built_in">String</span>;)V<br> #13 = Utf8 SourceFile<br> #14 = Utf8 Demo1.java<br> #15 = NameAndType #7:#8 <span class="hljs-comment">// "<init>":()V</span><br> #16 = Class #23 <span class="hljs-comment">// java/lang/System</span><br> #17 = NameAndType #24:#25 <span class="hljs-comment">// out:Ljava/io/PrintStream;</span><br> #18 = Utf8 hello world<br> #19 = Class #26 <span class="hljs-comment">// java/io/PrintStream</span><br> #20 = NameAndType #27:#28 <span class="hljs-comment">// println:(Ljava/lang/String;)V</span><br> #21 = Utf8 com/nyima/JVM/day5/Demo1<br> #22 = Utf8 java/lang/Object<br> #23 = Utf8 java/lang/System<br> #24 = Utf8 out<br> #25 = Utf8 Ljava/io/PrintStream;<br> #26 = Utf8 java/io/PrintStream<br> #27 = Utf8 println<br> #28 = Utf8 (Ljava/lang/<span class="hljs-built_in">String</span>;)V<br>{<br> <span class="hljs-keyword">public</span> com.nyima.JVM.day5.Demo1();<br> descriptor: ()V<br> flags: ACC_PUBLIC<br> Code:<br> <span class="hljs-built_in">stack</span>=<span class="hljs-number">1</span>, locals=<span class="hljs-number">1</span>, args_size=<span class="hljs-number">1</span><br> <span class="hljs-number">0</span>: aload_0<br> <span class="hljs-number">1</span>: invokespecial #1 <span class="hljs-comment">// Method java/lang/Object."<init>":()V</span><br> <span class="hljs-number">4</span>: <span class="hljs-keyword">return</span><br> LineNumberTable:<br> line <span class="hljs-number">7</span>: <span class="hljs-number">0</span><br><br> <span class="hljs-keyword">public</span> static <span class="hljs-literal">void</span> main(java.lang.<span class="hljs-built_in">String</span><span class="hljs-meta">[</span><span class="hljs-meta">]</span>);<br> descriptor: (<span class="hljs-meta">[</span>Ljava/lang/<span class="hljs-built_in">String</span>;)V<br> flags: ACC_PUBLIC, ACC_STATIC<br> Code:<br> <span class="hljs-built_in">stack</span>=<span class="hljs-number">2</span>, locals=<span class="hljs-number">1</span>, args_size=<span class="hljs-number">1</span><br> <span class="hljs-number">0</span>: getstatic #2 <span class="hljs-comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span><br> <span class="hljs-number">3</span>: ldc #3 <span class="hljs-comment">// String hello world</span><br> <span class="hljs-number">5</span>: invokevirtual #4 <span class="hljs-comment">// Method java/io/PrintStream.println:(Ljava/lang/String;)V</span><br><br> <span class="hljs-number">8</span>: <span class="hljs-keyword">return</span><br> LineNumberTable:<br> line <span class="hljs-number">9</span>: <span class="hljs-number">0</span><br> line <span class="hljs-number">10</span>: <span class="hljs-number">8</span><br>}Copy<br></code></pre></td></tr></table></figure><hr><h3 id="3-2-2-图解方法执行流程"><a href="#3-2-2-图解方法执行流程" class="headerlink" title="3.2.2 图解方法执行流程"></a>3.2.2 图解方法执行流程</h3><p><strong>原始代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_1</span> { <br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> { <br><span class="hljs-type">int</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span>; <br><span class="hljs-type">int</span> <span class="hljs-variable">b</span> <span class="hljs-operator">=</span> Short.MAX_VALUE + <span class="hljs-number">1</span>; <br><span class="hljs-type">int</span> <span class="hljs-variable">c</span> <span class="hljs-operator">=</span> a + b; <br>System.out.println(c); <br> } <br>}<br></code></pre></td></tr></table></figure><h4 id="3-2-2-1-常量池载入运行时常量池"><a href="#3-2-2-1-常量池载入运行时常量池" class="headerlink" title="3.2.2.1 常量池载入运行时常量池"></a>3.2.2.1 常量池载入运行时常量池</h4><p>常量池也属于方法区,只不过这里单独提出来了</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220808184345213.png" alt="image-20220808184345213"></p><h4 id="3-2-2-2-方法字节码载入方法区"><a href="#3-2-2-2-方法字节码载入方法区" class="headerlink" title="3.2.2.2 方法字节码载入方法区"></a>3.2.2.2 方法字节码载入方法区</h4><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220808184729286.png" alt="image-20220808184729286"></p><h4 id="3-2-2-3-main-线程开始运行,分配栈帧内存"><a href="#3-2-2-3-main-线程开始运行,分配栈帧内存" class="headerlink" title="3.2.2.3 main 线程开始运行,分配栈帧内存"></a>3.2.2.3 main 线程开始运行,分配栈帧内存</h4><p>(stack=2,locals=4) 对应操作数栈有2个空间(每个空间4个字节),局部变量表中有4个槽位</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220808184801651.png" alt="image-20220808184801651"></p><h4 id="3-2-2-4-执行引擎开始执行字节码"><a href="#3-2-2-4-执行引擎开始执行字节码" class="headerlink" title="3.2.2.4 执行引擎开始执行字节码"></a>3.2.2.4 执行引擎开始执行字节码</h4><h5 id="bipush-10"><a href="#bipush-10" class="headerlink" title="bipush 10"></a>bipush 10</h5><ul><li><p>将一个 byte 压入操作数栈</p><p>(其长度会补齐 4 个字节),类似的指令还有</p><ul><li>sipush 将一个 short 压入操作数栈(其长度会补齐 4 个字节)</li><li>ldc 将一个 int 压入操作数栈</li><li>ldc2_w 将一个 long 压入操作数栈(<strong>分两次压入</strong>,因为 long 是 8 个字节)</li><li>这里小的数字都是和字节码指令存在一起,<strong>超过 short 范围的数字存入了常量池(比如Short.MAX_VALUE + 1)</strong></li></ul></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809002236917.png" alt="image-20220809002236917"></p><h5 id="istore-1"><a href="#istore-1" class="headerlink" title="istore_1"></a>istore_1</h5><p>将操作数栈栈顶元素弹出,放入局部变量表的slot 1中</p><p>对应代码中的</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf"><span class="hljs-attribute">a</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span><br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809002349798.png" alt="image-20220809002349798"></p><h5 id="ldc-3"><a href="#ldc-3" class="headerlink" title="ldc #3"></a>ldc #3</h5><ul><li><p>读取运行时常量池中#3,即32768(超过short最大值范围的数会被放到运行时常量池中),将其加载到操作数栈中</p></li><li><p><strong>注意 Short.MAX_VALUE 是 32767</strong>,所以 32768 = Short.MAX_VALUE + 1 实际是在编译期间计算好的、</p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809002547059.png" alt="image-20220809002547059"></p><h5 id="istore-2"><a href="#istore-2" class="headerlink" title="istore_2"></a>istore_2</h5><p>将操作数栈中的元素弹出,放到局部变量表的2号位置</p><p>对应着代码中的</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf"><span class="hljs-attribute">b</span> <span class="hljs-operator">=</span> <span class="hljs-number">32768</span><br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809002645260.png" alt="image-20220809002645260"></p><h5 id="iload1、iload2"><a href="#iload1、iload2" class="headerlink" title="iload1、iload2"></a>iload1、iload2</h5><p>将局部变量表中1号位置和2号位置的元素放入操作数栈中</p><ul><li>因为只能在操作数栈中执行运算操作</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809002922739.png" alt="image-20220809002922739"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809002935532.png" alt="image-20220809002935532"></p><h5 id="iadd"><a href="#iadd" class="headerlink" title="iadd"></a>iadd</h5><p>将操作数栈中的两个元素<strong>弹出栈</strong>并相加,结果在压入操作数栈中</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809003051071.png" alt="image-20220809003051071"></p><h5 id="istore-3"><a href="#istore-3" class="headerlink" title="istore 3"></a>istore 3</h5><p>将操作数栈中的元素弹出,放入局部变量表的3号位置</p><p>对应代码中的</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">int</span> <span class="hljs-variable">c</span> <span class="hljs-operator">=</span> a + b;<br></code></pre></td></tr></table></figure><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809003148724.png" alt="image-20220809003148724"></p><h5 id="getstatic-4"><a href="#getstatic-4" class="headerlink" title="getstatic #4"></a>getstatic #4</h5><ul><li><p>在运行时常量池中找到#4,发现是一个对象</p></li><li><p>在堆内存中找到该对象,并将其<strong>引用</strong>放入操作数栈中</p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809003412248.png" alt="image-20220809003412248"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809003428927.png" alt="image-20220809003428927"></p><h5 id="iload-3"><a href="#iload-3" class="headerlink" title="iload 3"></a>iload 3</h5><p>将局部变量表中3号位置的元素压入操作数栈中</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809003627971.png" alt="image-20220809003627971"></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809003902007.png" alt="image-20220809003902007"></p><h5 id="invokevirtual-5"><a href="#invokevirtual-5" class="headerlink" title="invokevirtual 5"></a>invokevirtual 5</h5><ul><li>找到常量池 #5 项</li><li>定位到方法区 java/io/PrintStream.println:(I)V 方法</li><li>生成新的栈帧(分配 locals、stack等)</li><li>传递参数,执行新栈帧中的字节码</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809004000867.png" alt="image-20220809004000867"></p><ul><li>执行完毕,弹出栈帧</li><li>清除 main 操作数栈内容</li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220809004022928.png" alt="image-20220809004022928"></p><h5 id="return"><a href="#return" class="headerlink" title="return"></a>return</h5><ul><li>完成 main 方法调用</li><li>弹出 main 栈帧,程序结束</li></ul><hr><h3 id="3-2-3-练习-分析i"><a href="#3-2-3-练习-分析i" class="headerlink" title="3.2.3 练习-分析i++"></a>3.2.3 练习-分析i++</h3><p>源码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">* 从字节码角度分析 a++ 相关题目</span><br><span class="hljs-comment">*/</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;<br> <span class="hljs-type">int</span> <span class="hljs-variable">b</span> <span class="hljs-operator">=</span> a++ + ++a + a--;<br> System.out.println(a);<br> System.out.println(b);<br> }<br>}<br></code></pre></td></tr></table></figure><p>字节码code:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"> <span class="hljs-number">0</span>: bipush <span class="hljs-number">10</span><br> <span class="hljs-number">2</span>: istore_1<br> <span class="hljs-number">3</span>: iload_1<br> <span class="hljs-number">4</span>: iinc <span class="hljs-number">1</span>, <span class="hljs-number">1</span><br> <span class="hljs-number">7</span>: iinc <span class="hljs-number">1</span>, <span class="hljs-number">1</span><br><span class="hljs-number">10</span>: iload_1<br><span class="hljs-number">11</span>: iadd<br><span class="hljs-number">12</span>: iload_1<br><span class="hljs-number">13</span>: iinc <span class="hljs-number">1</span>, -<span class="hljs-number">1</span><br><span class="hljs-number">16</span>: iadd<br><span class="hljs-number">17</span>: istore_2<br></code></pre></td></tr></table></figure><p>分析:</p><ul><li>注意**iinc指令(自增指令)**是直接在局部变量slot上进行运算</li><li>a++ 和 ++a 的区别是先执行**iload(读取)**还是先执行iinc<ul><li>a++ 是先 iload 再 iinc</li><li>++a 是先 iinc 再 iload</li></ul></li></ul><p>结论:</p><p>图解过长,计算出后观看视频验证答案</p><p><a href="https://www.bilibili.com/video/BV1yE411Z7AP?p=112">https://www.bilibili.com/video/BV1yE411Z7AP?p=112</a></p><hr><h3 id="3-2-4-条件判断"><a href="#3-2-4-条件判断" class="headerlink" title="3.2.4 条件判断"></a>3.2.4 条件判断</h3><p>几点说明:</p><ul><li>byte,short,char 都会按 int 比较,因为操作数栈都是 4 字节</li><li>goto 用来进行跳转到指定行号的字节码</li></ul><p>源码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs Java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_3</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">if</span>(a == <span class="hljs-number">0</span>) {<br> a = <span class="hljs-number">10</span>;<br> } <span class="hljs-keyword">else</span> {<br> a = <span class="hljs-number">20</span>;<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><p>字节码code</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-number">0</span>: iconst_0 <span class="hljs-comment">//把 0 压入操作数栈 (比较小的数 -1到5用iconst)</span><br><span class="hljs-number">1</span>: istore_1 <span class="hljs-comment">//把这个 0 赋值给 a(局部变量1号位) 即 a = 0;</span><br><span class="hljs-number">2</span>: iload_1 <span class="hljs-comment">//把局部变量表1号位的值读取到操作数栈</span><br><span class="hljs-number">3</span>: ifne <span class="hljs-number">12</span> <span class="hljs-comment">// 判断 如果不成立(这里即a不等于0)则跳转到12行,如果等于则继续往下</span><br><span class="hljs-number">6</span>: bipush <span class="hljs-number">10</span> <span class="hljs-comment">//把 10 放入操作数栈</span><br><span class="hljs-number">8</span>: istore_1 <span class="hljs-comment">//把 10 赋值给 a</span><br><span class="hljs-number">9</span>: goto <span class="hljs-number">15</span> <span class="hljs-comment">// 跳转到15行</span><br><span class="hljs-number">12</span>: bipush <span class="hljs-number">20</span> <span class="hljs-comment">//把 20 放入操作数栈</span><br><span class="hljs-number">14</span>: istore_1 <span class="hljs-comment">//把 20 赋值给 a</span><br><span class="hljs-number">15</span>: <span class="hljs-keyword">return</span><br></code></pre></td></tr></table></figure><hr><h3 id="3-2-5-循环控制"><a href="#3-2-5-循环控制" class="headerlink" title="3.2.5 循环控制"></a>3.2.5 循环控制</h3><p>其实循环控制还是前面介绍的那些指令,例如 while 循环</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_4</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">while</span> (a < <span class="hljs-number">10</span>) {<br> a++;<br> }<br> }<br>}<br><br>code:<br><span class="hljs-number">0</span>: iconst_0<br><span class="hljs-number">1</span>: istore_1<br><span class="hljs-number">2</span>: iload_1<br><span class="hljs-number">3</span>: bipush <span class="hljs-number">10</span><br><span class="hljs-number">5</span>: if_icmpge <span class="hljs-number">14</span><br><span class="hljs-number">8</span>: iinc <span class="hljs-number">1</span>, <span class="hljs-number">1</span><br><span class="hljs-number">11</span>: goto <span class="hljs-number">2</span><br><span class="hljs-number">14</span>: <span class="hljs-keyword">return</span><br></code></pre></td></tr></table></figure><p>再比如 do while 循环:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_5</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">do</span> {<br> a++;<br> } <span class="hljs-keyword">while</span> (a < <span class="hljs-number">10</span>);<br> }<br>}<br><br>code:<br><span class="hljs-number">0</span>: iconst_0<br><span class="hljs-number">1</span>: istore_1<br><span class="hljs-number">2</span>: iinc <span class="hljs-number">1</span>, <span class="hljs-number">1</span><br><span class="hljs-number">5</span>: iload_1<br><span class="hljs-number">6</span>: bipush <span class="hljs-number">10</span><br><span class="hljs-number">8</span>: if_icmplt <span class="hljs-number">2</span><br><span class="hljs-number">11</span>: <span class="hljs-keyword">return</span><br></code></pre></td></tr></table></figure><p>最后再看看 for 循环:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_6</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++) {<br> <br> }<br> }<br>}<br><br><span class="hljs-number">0</span>: iconst_0<br><span class="hljs-number">1</span>: istore_1<br><span class="hljs-number">2</span>: iload_1<br><span class="hljs-number">3</span>: bipush <span class="hljs-number">10</span><br><span class="hljs-number">5</span>: if_icmpge <span class="hljs-number">14</span><br><span class="hljs-number">8</span>: iinc <span class="hljs-number">1</span>, <span class="hljs-number">1</span><br><span class="hljs-number">11</span>: goto <span class="hljs-number">2</span><br><span class="hljs-number">14</span>: <span class="hljs-keyword">return</span><br></code></pre></td></tr></table></figure><hr><h3 id="3-2-6-练习-分析x-x3D-0(经典)"><a href="#3-2-6-练习-分析x-x3D-0(经典)" class="headerlink" title="3.2.6 练习-分析x=0(经典)"></a>3.2.6 练习-分析x=0(经典)</h3><p>请从字节码角度分析,下列代码运行的结果:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo2</span> {<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><span class="hljs-type">int</span> i=<span class="hljs-number">0</span>;<br><span class="hljs-type">int</span> x=<span class="hljs-number">0</span>;<br><span class="hljs-keyword">while</span>(i<<span class="hljs-number">10</span>) {<br>x = x++;<br>i++;<br>}<br>System.out.println(x); <span class="hljs-comment">//接过为0</span><br>}<br>}<br></code></pre></td></tr></table></figure><p>为什么最终的x结果为0呢? 通过分析字节码指令即可知晓:</p><p>code:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-number">0</span>: iconst_0<span class="hljs-comment">//准备一个常数0</span><br><span class="hljs-number">1</span>: istore_1<span class="hljs-comment">//将常数0放入局部变量表的1号槽位 i=0</span><br><span class="hljs-number">2</span>: iconst_0<span class="hljs-comment">//准备一个常数0</span><br><span class="hljs-number">3</span>: istore_2<span class="hljs-comment">//将常数0放入局部变量的2号槽位 x=0</span><br><span class="hljs-number">4</span>: <br><span class="hljs-number">5</span>: bipush <span class="hljs-number">10</span><span class="hljs-comment">//将数字10放入操作数栈中,此时操作数栈中有2个数</span><br><span class="hljs-number">7</span>: if_icmpge <span class="hljs-number">21</span><span class="hljs-comment">//比较操作数栈中的两个数,如果下面的数大于上面的数,就跳转到21。这里的比较是将两个数做减法。因为涉及运算操作,所以会将两个数弹出操作数栈来进行运算。运算结束后操作数栈为空</span><br><span class="hljs-number">10</span>: iload_2<span class="hljs-comment">//将局部变量2号槽位的数放入操作数栈中,放入的值是0</span><br><span class="hljs-number">11</span>: iinc <span class="hljs-number">2</span>, <span class="hljs-number">1</span><span class="hljs-comment">//将局部变量2号槽位的数加1,自增后,槽位中的值为1</span><br><span class="hljs-number">14</span>: istore_2<span class="hljs-comment">//将操作数栈中的数放入到局部变量表的2号槽位,2号槽位的值又变为了0</span><br><span class="hljs-number">15</span>: iinc <span class="hljs-number">1</span>, <span class="hljs-number">1</span> <span class="hljs-comment">//1号槽位的值自增1</span><br><span class="hljs-number">18</span>: goto <span class="hljs-number">4</span> <span class="hljs-comment">//跳转到第4条指令</span><br><span class="hljs-number">21</span>: getstatic #<span class="hljs-number">2</span> <span class="hljs-comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span><br><span class="hljs-number">24</span>: iload_2<br><span class="hljs-number">25</span>: invokevirtual #<span class="hljs-number">3</span> <span class="hljs-comment">// Method java/io/PrintStream.println:(I)V</span><br><span class="hljs-number">28</span>: <span class="hljs-keyword">return</span><br></code></pre></td></tr></table></figure><p>简单图解分析(不包含对循环的判断,对循环的判断详情在上面的注释)</p><p>因为是循环嘛,就简单的解释下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java">x = <span class="hljs-number">0</span>;<br>x = x++;<br><br>code:<br><span class="hljs-number">0</span>: iconst_0<span class="hljs-comment">//准备一个常数0</span><br><span class="hljs-number">1</span>: istore_1<span class="hljs-comment">//将常数0放入局部变量表的1号槽位 x=0</span><br><span class="hljs-number">2</span>: iload_1<span class="hljs-comment">//将局部变量表1号槽位的数放入操作数栈中</span><br><span class="hljs-number">3</span>: iinc <span class="hljs-number">1</span>, <span class="hljs-number">1</span><span class="hljs-comment">//将局部变量1号槽位的数加1,自增后,槽位中的值为1</span><br><span class="hljs-number">4</span>: istore_1<span class="hljs-comment">//将操作数栈中的数放入到局部变量表的1号槽位,1号槽位的值又变为了0; 即赋值操作 x = ,赋值操作是赋值操作数栈里的,具体看3.2.2.4中</span><br></code></pre></td></tr></table></figure><p>图解:<a href="https://www.processon.com/view/link/62f14dd41efad4106699d25a">分析x = 0 | ProcessOn免费在线作图,在线流程图,在线思维导图 |</a></p><hr><h3 id="3-2-7-构造方法"><a href="#3-2-7-构造方法" class="headerlink" title="3.2.7 构造方法"></a>3.2.7 构造方法</h3><h4 id="3-2-7-1-cinit-V"><a href="#3-2-7-1-cinit-V" class="headerlink" title="3.2.7.1 cinit()V"></a>3.2.7.1 cinit()V</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3</span> {<br><span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;<br><br><span class="hljs-keyword">static</span> {<br>i = <span class="hljs-number">20</span>;<br>}<br><br><span class="hljs-keyword">static</span> {<br>i = <span class="hljs-number">30</span>;<br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br>System.out.println(i); <span class="hljs-comment">//结果为30</span><br>}<br>}Copy<br></code></pre></td></tr></table></figure><p>编译器会按<strong>从上至下</strong>的顺序,收集所有 static 静态代码块和静态成员赋值的代码,<strong>合并</strong>为一个特殊的方法 cinit()V :</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java">stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">0</span>, args_size=<span class="hljs-number">0</span><br> <span class="hljs-number">0</span>: bipush <span class="hljs-number">10</span><br> <span class="hljs-number">2</span>: putstatic #<span class="hljs-number">3</span> <span class="hljs-comment">// Field i:I 变量赋值</span><br> <span class="hljs-number">5</span>: bipush 20ja<br> <span class="hljs-number">7</span>: putstatic #<span class="hljs-number">3</span> <span class="hljs-comment">// Field i:I</span><br> <span class="hljs-number">10</span>: bipush <span class="hljs-number">30</span><br> <span class="hljs-number">12</span>: putstatic #<span class="hljs-number">3</span> <span class="hljs-comment">// Field i:I</span><br> <span class="hljs-number">15</span>: <span class="hljs-keyword">return</span><br></code></pre></td></tr></table></figure><hr><h4 id="3-2-7-2-init-V"><a href="#3-2-7-2-init-V" class="headerlink" title="3.2.7.2 init()V"></a>3.2.7.2 init()V</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo4</span> {<br><span class="hljs-keyword">private</span> <span class="hljs-type">String</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span> <span class="hljs-string">"s1"</span>;<br><br>{<br>b = <span class="hljs-number">20</span>;<br>}<br><br><span class="hljs-keyword">private</span> <span class="hljs-type">int</span> <span class="hljs-variable">b</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;<br><br>{<br>a = <span class="hljs-string">"s2"</span>;<br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-title function_">Demo4</span><span class="hljs-params">(String a, <span class="hljs-type">int</span> b)</span> {<br><span class="hljs-built_in">this</span>.a = a;<br><span class="hljs-built_in">this</span>.b = b;<br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><span class="hljs-type">Demo4</span> <span class="hljs-variable">d</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Demo4</span>(<span class="hljs-string">"s3"</span>, <span class="hljs-number">30</span>);<br>System.out.println(d.a);<br>System.out.println(d.b);<br>}<br>}<br></code></pre></td></tr></table></figure><ul><li>编译器会按<strong>从上至下</strong>的顺序,收集所有 {} 代码块和成员变量赋值的代码,<strong>形成新的构造方法</strong>,但<strong>原始构造方法</strong>内的代码<strong>总是在后</strong></li><li>如果有多个构造函数,则会一一对应生成多个<init></li><li>简单说:执行顺序:静态代码块 > 代码块 > 构造方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> cn.itcast.jvm.t3.bytecode.Demo3_8_2(java.lang.String, <span class="hljs-type">int</span>);<br> descriptor: (Ljava/lang/String;I)V<br> flags: ACC_PUBLIC<br> Code:<br> stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">3</span><br> <span class="hljs-number">0</span>: aload_0<br> <span class="hljs-number">1</span>: invokespecial #<span class="hljs-number">1</span> <span class="hljs-comment">// super.<init>()V</span><br> <span class="hljs-number">4</span>: aload_0<br> <span class="hljs-number">5</span>: ldc #<span class="hljs-number">2</span> <span class="hljs-comment">// <- "s1"</span><br> <span class="hljs-number">7</span>: putfield #<span class="hljs-number">3</span> <span class="hljs-comment">// -> this.a</span><br> <span class="hljs-number">10</span>: aload_0<br> <span class="hljs-number">11</span>: bipush <span class="hljs-number">20</span> <span class="hljs-comment">// <- 20</span><br> <span class="hljs-number">13</span>: putfield #<span class="hljs-number">4</span> <span class="hljs-comment">// -> this.b</span><br> <span class="hljs-number">16</span>: aload_0<br> <span class="hljs-number">17</span>: bipush <span class="hljs-number">10</span> <span class="hljs-comment">// <- 10</span><br> <span class="hljs-number">19</span>: putfield #<span class="hljs-number">4</span> <span class="hljs-comment">// -> this.b</span><br> <span class="hljs-number">22</span>: aload_0<br> <span class="hljs-number">23</span>: ldc #<span class="hljs-number">5</span> <span class="hljs-comment">// <- "s2"</span><br> <span class="hljs-number">25</span>: putfield #<span class="hljs-number">3</span> <span class="hljs-comment">// -> this.a</span><br> <span class="hljs-number">28</span>: aload_0 <span class="hljs-comment">// ------------------------------</span><br> <span class="hljs-number">29</span>: aload_1 <span class="hljs-comment">// <- slot 1(a) "s3" |</span><br> <span class="hljs-number">30</span>: putfield #<span class="hljs-number">3</span> <span class="hljs-comment">// -> this.a |</span><br> <span class="hljs-number">33</span>: aload_0 |<br> <span class="hljs-number">34</span>: iload_2 <span class="hljs-comment">// <- slot 2(b) 30 |</span><br> <span class="hljs-number">35</span>: putfield #<span class="hljs-number">4</span> <span class="hljs-comment">// -> this.b --------------------</span><br> <span class="hljs-number">38</span>: <span class="hljs-keyword">return</span><br> LineNumberTable: ...<br> LocalVariableTable:<br> Start Length Slot Name Signature<br> <span class="hljs-number">0</span> <span class="hljs-number">39</span> <span class="hljs-number">0</span> <span class="hljs-built_in">this</span> Lcn/itcast/jvm/t3/bytecode/Demo3_8_2;<br> <span class="hljs-number">0</span> <span class="hljs-number">39</span> <span class="hljs-number">1</span> a Ljava/lang/String;<br> <span class="hljs-number">0</span> <span class="hljs-number">39</span> <span class="hljs-number">2</span> b I<br> MethodParameters: ...<br><br></code></pre></td></tr></table></figure><hr><h3 id="3-2-8-方法调用"><a href="#3-2-8-方法调用" class="headerlink" title="3.2.8 方法调用"></a>3.2.8 方法调用</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo5</span> {<br><span class="hljs-keyword">public</span> <span class="hljs-title function_">Demo5</span><span class="hljs-params">()</span> {<br><br>}<br><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test1</span><span class="hljs-params">()</span> {<br><br>}<br><br><span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test2</span><span class="hljs-params">()</span> {<br><br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test3</span><span class="hljs-params">()</span> {<br><br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test4</span><span class="hljs-params">()</span> {<br><br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><span class="hljs-type">Demo5</span> <span class="hljs-variable">demo5</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Demo5</span>();<br>demo5.test1();<br>demo5.test2();<br>demo5.test3();<br>Demo5.test4();<br>}<br>}<br></code></pre></td></tr></table></figure><p>不同方法在调用时,对应的虚拟机指令有所区别</p><ul><li>私有、构造、被final修饰的方法,在调用时都使用<strong>invokespecial</strong>指令</li><li>普通成员方法在调用时,使用invokespecial指令。因为编译期间无法确定该方法的内容,只有在运行期间才能确定</li><li>静态方法在调用时使用invokestatic指令</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">Code:</span><br> <span class="hljs-string">stack=2,</span> <span class="hljs-string">locals=2,</span> <span class="hljs-string">args_size=1</span><br> <span class="hljs-attr">0:</span> <span class="hljs-string">new</span> <span class="hljs-comment">#2 // class com/Demo5 </span><br> <span class="hljs-attr">3:</span> <span class="hljs-string">dup</span><br> <span class="hljs-attr">4:</span> <span class="hljs-string">invokespecial</span> <span class="hljs-comment">#3 // Method "<init>":()V</span><br> <span class="hljs-attr">7:</span> <span class="hljs-string">astore_1</span><br> <span class="hljs-attr">8:</span> <span class="hljs-string">aload_1</span><br> <span class="hljs-attr">9:</span> <span class="hljs-string">invokespecial</span> <span class="hljs-comment">#4 // Method test1:()V</span><br> <span class="hljs-attr">12:</span> <span class="hljs-string">aload_1</span><br> <span class="hljs-attr">13:</span> <span class="hljs-string">invokespecial</span> <span class="hljs-comment">#5 // Method test2:()V</span><br> <span class="hljs-attr">16:</span> <span class="hljs-string">aload_1</span><br> <span class="hljs-attr">17:</span> <span class="hljs-string">invokevirtual</span> <span class="hljs-comment">#6 // Method test3:()V</span><br> <span class="hljs-attr">20:</span> <span class="hljs-string">invokestatic</span> <span class="hljs-comment">#7 // Method test4:()V</span><br> <span class="hljs-attr">23:</span> <span class="hljs-string">returnCopy</span><br></code></pre></td></tr></table></figure><ul><li>new 是创建【对象】,给对象分配堆内存,执行成功会将【<strong>对象引用</strong>】压入操作数栈</li><li>dup 是赋值操作数栈栈顶的内容,本例即为【<strong>对象引用</strong>】,为什么需要两份引用呢,一个是要配合 invokespecial 调用该对象的构造方法 “init”:()V (会消耗掉栈顶一个引用),另一个要 配合 astore_1 赋值给局部变量</li><li>终方法(final),私有方法(private),构造方法都是由 invokespecial 指令来调用,属于静态绑定</li><li>普通成员方法是由 invokevirtual 调用,属于<strong>动态绑定</strong>,即支持多态 成员方法与静态方法调用的另一个区别是,执行方法前是否需要【对象引用】</li></ul><hr><h3 id="3-2-9-多态的原理"><a href="#3-2-9-多态的原理" class="headerlink" title="3.2.9 多态的原理"></a>3.2.9 多态的原理</h3><blockquote><p>ps: 实在不理解,仅摘抄笔记 <a href="https://www.bilibili.com/video/BV1yE411Z7AP?p=122">https://www.bilibili.com/video/BV1yE411Z7AP?p=122</a></p></blockquote><p>因为普通成员方法需要在运行时才能确定具体的内容,所以虚拟机需要调用<strong>invokevirtual</strong>指令</p><p>在执行invokevirtual指令时,经历了以下几个步骤</p><ul><li>先通过栈帧中对象的引用找到对象</li><li><strong>分析对象头</strong>,找到对象实际的Class</li><li>Class结构中有<strong>vtable</strong>,它在类加载的链接阶段就已经根据方法的重写规则生成好了</li><li>查询vtable找到方法的具体地址</li><li>执行方法的字节码</li></ul><hr><h3 id="3-2-10-异常处理-面试问"><a href="#3-2-10-异常处理-面试问" class="headerlink" title="3.2.10 异常处理(面试问)"></a>3.2.10 异常处理(面试问)</h3><h4 id="3-2-10-1-try-catch"><a href="#3-2-10-1-try-catch" class="headerlink" title="3.2.10.1 try-catch"></a>3.2.10.1 try-catch</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> *代码</span><br><span class="hljs-comment">**/</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_11_1</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">try</span> {<br> i = <span class="hljs-number">10</span>;<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> i = <span class="hljs-number">20</span>;<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(java.lang.String[])</span>;<br> descriptor: ([Ljava/lang/String;)V<br> flags: ACC_PUBLIC, ACC_STATIC<br> Code:<br> stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">1</span><br> <span class="hljs-number">0</span>: iconst_0<br> <span class="hljs-number">1</span>: istore_1<br> <span class="hljs-number">2</span>: bipush <span class="hljs-number">10</span><br> <span class="hljs-number">4</span>: istore_1<br> <span class="hljs-number">5</span>: goto <span class="hljs-number">12</span><br> <span class="hljs-number">8</span>: astore_2<br> <span class="hljs-number">9</span>: bipush <span class="hljs-number">20</span><br> <span class="hljs-number">11</span>: istore_1<br> <span class="hljs-number">12</span>: <span class="hljs-keyword">return</span><br> Exception table: <span class="hljs-comment">//异常表,用来监测</span><br> from to target type<br> <span class="hljs-number">2</span> <span class="hljs-number">5</span> <span class="hljs-number">8</span> Class java/lang/Exception<br> LineNumberTable: ...<br> LocalVariableTable:<br> Start Length Slot Name Signature<br> <span class="hljs-number">9</span> <span class="hljs-number">3</span> <span class="hljs-number">2</span> e Ljava/lang/Exception;<br> <span class="hljs-number">0</span> <span class="hljs-number">13</span> <span class="hljs-number">0</span> args [Ljava/lang/String;<br> <span class="hljs-number">2</span> <span class="hljs-number">11</span> <span class="hljs-number">1</span> i I<br> StackMapTable: ...<br> MethodParameters: ...<br>}<br></code></pre></td></tr></table></figure><ul><li>可以看到多出来一个 Exception table 的结构,[from, to) 是<strong>前闭后开</strong>(也就是检测2~4行)的检测范围,一旦这个范围内的字节码执行出现异常,则通过 <strong>type 匹配</strong>异常类型,如果一致,进入 target 所指示行号</li><li>8行的字节码指令 astore_2 是将异常对象引用存入局部变量表的2号位置(为e)</li></ul><hr><h4 id="3-2-10-2-多个single-catch"><a href="#3-2-10-2-多个single-catch" class="headerlink" title="3.2.10.2 多个single-catch"></a>3.2.10.2 多个single-catch</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_11_2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">try</span> {<br> i = <span class="hljs-number">10</span>;<br> } <span class="hljs-keyword">catch</span> (ArithmeticException e) {<br> i = <span class="hljs-number">30</span>;<br> } <span class="hljs-keyword">catch</span> (NullPointerException e) {<br> i = <span class="hljs-number">40</span>;<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> i = <span class="hljs-number">50</span>;<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight nestedtext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs nestedtext"><span class="hljs-attribute">public static void main(java.lang.String[]);</span><br><span class="hljs-attribute"> descriptor</span><span class="hljs-punctuation">:</span> <span class="hljs-string">([Ljava/lang/String;)V</span><br> <span class="hljs-attribute">flags</span><span class="hljs-punctuation">:</span> <span class="hljs-string">ACC_PUBLIC, ACC_STATIC</span><br> <span class="hljs-attribute">Code</span><span class="hljs-punctuation">:</span><br> <span class="hljs-attribute">stack=1, locals=3, args_size=1</span><br><span class="hljs-attribute"> 0</span><span class="hljs-punctuation">:</span> <span class="hljs-string">iconst_0</span><br> <span class="hljs-attribute">1</span><span class="hljs-punctuation">:</span> <span class="hljs-string">istore_1</span><br> <span class="hljs-attribute">2</span><span class="hljs-punctuation">:</span> <span class="hljs-string">bipush 10</span><br> <span class="hljs-attribute">4</span><span class="hljs-punctuation">:</span> <span class="hljs-string">istore_1</span><br> <span class="hljs-attribute">5</span><span class="hljs-punctuation">:</span> <span class="hljs-string">goto 26</span><br> <span class="hljs-attribute">8</span><span class="hljs-punctuation">:</span> <span class="hljs-string">astore_2</span><br> <span class="hljs-attribute">9</span><span class="hljs-punctuation">:</span> <span class="hljs-string">bipush 30</span><br> <span class="hljs-attribute">11</span><span class="hljs-punctuation">:</span> <span class="hljs-string">istore_1</span><br> <span class="hljs-attribute">12</span><span class="hljs-punctuation">:</span> <span class="hljs-string">goto 26</span><br> <span class="hljs-attribute">15</span><span class="hljs-punctuation">:</span> <span class="hljs-string">astore_2</span><br> <span class="hljs-attribute">16</span><span class="hljs-punctuation">:</span> <span class="hljs-string">bipush 40</span><br> <span class="hljs-attribute">18</span><span class="hljs-punctuation">:</span> <span class="hljs-string">istore_1</span><br> <span class="hljs-attribute">19</span><span class="hljs-punctuation">:</span> <span class="hljs-string">goto 26</span><br> <span class="hljs-attribute">22</span><span class="hljs-punctuation">:</span> <span class="hljs-string">astore_2</span><br> <span class="hljs-attribute">23</span><span class="hljs-punctuation">:</span> <span class="hljs-string">bipush 50</span><br> <span class="hljs-attribute">25</span><span class="hljs-punctuation">:</span> <span class="hljs-string">istore_1</span><br> <span class="hljs-attribute">26</span><span class="hljs-punctuation">:</span> <span class="hljs-string">return</span><br> <span class="hljs-attribute">Exception table</span><span class="hljs-punctuation">:</span><br> <span class="hljs-attribute">from to target type</span><br><span class="hljs-attribute"> 2 5 8 Class java/lang/ArithmeticException</span><br><span class="hljs-attribute"> 2 5 15 Class java/lang/NullPointerException</span><br><span class="hljs-attribute"> 2 5 22 Class java/lang/Exception</span><br><span class="hljs-attribute"> LineNumberTable</span><span class="hljs-punctuation">:</span> <span class="hljs-string">...</span><br> <span class="hljs-attribute">LocalVariableTable</span><span class="hljs-punctuation">:</span><br> <span class="hljs-attribute">Start Length Slot Name Signature</span><br><span class="hljs-attribute"> 9 3 2 e Ljava/lang/ArithmeticException;</span><br><span class="hljs-attribute"> 16 3 2 e Ljava/lang/NullPointerException;</span><br><span class="hljs-attribute"> 23 3 2 e Ljava/lang/Exception;</span><br><span class="hljs-attribute"> 0 27 0 args [Ljava/lang/String;</span><br><span class="hljs-attribute"> 2 25 1 i I</span><br><span class="hljs-attribute"> StackMapTable</span><span class="hljs-punctuation">:</span> <span class="hljs-string">...</span><br> <span class="hljs-attribute">MethodParameters</span><span class="hljs-punctuation">:</span> <span class="hljs-string">...</span><br></code></pre></td></tr></table></figure><ul><li>因为异常出现时,<strong>只能进入</strong> Exception table 中<strong>一个分支</strong>,所以局部变量表 slot 2 位置<strong>被共用</strong></li></ul><hr><h4 id="3-2-10-3-multi-chtch的情况"><a href="#3-2-10-3-multi-chtch的情况" class="headerlink" title="3.2.10.3 multi-chtch的情况"></a>3.2.10.3 multi-chtch的情况</h4><ul><li>相当于多个single-catch的优化,把平级的异常写在一起</li><li>target都一样</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_11_3</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-type">Method</span> <span class="hljs-variable">test</span> <span class="hljs-operator">=</span> Demo3_11_3.class.getMethod(<span class="hljs-string">"test"</span>);<br> test.invoke(<span class="hljs-literal">null</span>);<br> } <span class="hljs-keyword">catch</span> (NoSuchMethodException | IllegalAccessException |<br> InvocationTargetException e) {<br> e.printStackTrace();<br> }<br> }<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> {<br> System.out.println(<span class="hljs-string">"ok"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><hr><h4 id="3-2-10-4-finally"><a href="#3-2-10-4-finally" class="headerlink" title="3.2.10.4 finally"></a>3.2.10.4 finally</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3_11_4</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">try</span> {<br> i = <span class="hljs-number">10</span>;<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> i = <span class="hljs-number">20</span>;<br> } <span class="hljs-keyword">finally</span> {<br> i = <span class="hljs-number">30</span>;<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(java.lang.String[])</span>;<br> descriptor: ([Ljava/lang/String;)V<br> flags: ACC_PUBLIC, ACC_STATIC<br> Code:<br> stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">4</span>, args_size=<span class="hljs-number">1</span><br> <span class="hljs-number">0</span>: iconst_0<br> <span class="hljs-number">1</span>: istore_1 <span class="hljs-comment">// 0 -> i</span><br> <span class="hljs-comment">/**try块**/</span><br> <span class="hljs-number">2</span>: bipush <span class="hljs-number">10</span> <span class="hljs-comment">// try --------------------------------------</span><br> <span class="hljs-number">4</span>: istore_1 <span class="hljs-comment">// 10 -> i |</span><br> <span class="hljs-comment">/**try块执行完后,会执行finally**/</span> |<br> <span class="hljs-number">5</span>: bipush <span class="hljs-number">30</span> <span class="hljs-comment">// finally |</span><br> <span class="hljs-number">7</span>: istore_1 <span class="hljs-comment">// 30 -> i |</span><br> <span class="hljs-number">8</span>: goto <span class="hljs-number">27</span> <span class="hljs-comment">// return -----------------------------------</span><br> <span class="hljs-comment">/**catch块**/</span> <br> <span class="hljs-number">11</span>: astore_2 <span class="hljs-comment">// catch Exceptin -> e 异常信息放入局部变量表的2号槽位</span><br> <span class="hljs-number">12</span>: bipush <span class="hljs-number">20</span> <span class="hljs-comment">// |</span><br> <span class="hljs-number">14</span>: istore_1 <span class="hljs-comment">// 20 -> i |</span><br> <span class="hljs-comment">/**catch块执行完后,会执行finally**/</span> |<br> <span class="hljs-number">15</span>: bipush <span class="hljs-number">30</span> <span class="hljs-comment">// finally |</span><br> <span class="hljs-number">17</span>: istore_1 <span class="hljs-comment">// 30 -> i |</span><br> <span class="hljs-number">18</span>: goto <span class="hljs-number">27</span> <span class="hljs-comment">// return -----------------------------------</span><br> <span class="hljs-comment">/**出现异常,但未被Exception捕获,会抛出其他异常,这时也需要执行finally块中的代码**/</span> <br> <span class="hljs-number">21</span>: astore_3 <span class="hljs-comment">// catch any -> slot 3 ----------------------</span><br> <span class="hljs-number">22</span>: bipush <span class="hljs-number">30</span> <span class="hljs-comment">// finally |</span><br> <span class="hljs-comment">/**同上**/</span><br> <span class="hljs-number">24</span>: istore_1 <span class="hljs-comment">// 30 -> i |</span><br> <span class="hljs-number">25</span>: aload_3 <span class="hljs-comment">// <- slot 3 |</span><br> <span class="hljs-number">26</span>: athrow <span class="hljs-comment">// throw 抛出异常-----------------------------</span><br> <span class="hljs-number">27</span>: <span class="hljs-keyword">return</span><br> Exception table:<br> from to target type<br> <span class="hljs-number">2</span> <span class="hljs-number">5</span> <span class="hljs-number">11</span> Class java/lang/Exception<br> <span class="hljs-number">2</span> <span class="hljs-number">5</span> <span class="hljs-number">21</span> any <span class="hljs-comment">// 剩余的异常类型,比如 Error</span><br> <span class="hljs-number">11</span> <span class="hljs-number">15</span> <span class="hljs-number">21</span> any <span class="hljs-comment">// 剩余的异常类型,比如 Error</span><br> LineNumberTable: ...<br> LocalVariableTable:<br> Start Length Slot Name Signature<br> <span class="hljs-number">12</span> <span class="hljs-number">3</span> <span class="hljs-number">2</span> e Ljava/lang/Exception;<br> <span class="hljs-number">0</span> <span class="hljs-number">28</span> <span class="hljs-number">0</span> args [Ljava/lang/String;<br> <span class="hljs-number">2</span> <span class="hljs-number">26</span> <span class="hljs-number">1</span> i I<br> StackMapTable: ...<br> MethodParameters: ...<br><br></code></pre></td></tr></table></figure><ul><li>可以看到 finally 中的代码被<strong>复制了 3 份</strong>,分别放入 try 流程,catch 流程以及 catch剩余的异常类型流程</li><li><strong>注意</strong>:虽然从字节码指令看来,每个块中都有finally块,但是finally块中的代码<strong>只会被执行一次</strong></li></ul><hr><h4 id="3-2-10-5-finally面试题"><a href="#3-2-10-5-finally面试题" class="headerlink" title="3.2.10.5 finally面试题"></a>3.2.10.5 finally面试题</h4><h5 id="题一:finally中含有return"><a href="#题一:finally中含有return" class="headerlink" title="题一:finally中含有return"></a>题一:finally中含有return</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3</span> {<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> Demo3.test();<br>System.out.println(i);<br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> {<br><span class="hljs-type">int</span> i;<br><span class="hljs-keyword">try</span> {<br>i = <span class="hljs-number">10</span>;<br><span class="hljs-keyword">return</span> i;<br>} <span class="hljs-keyword">finally</span> {<br>i = <span class="hljs-number">20</span>;<br><span class="hljs-keyword">return</span> i;<br>}<br>}<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span>;<br> descriptor: ()I<br> flags: ACC_PUBLIC, ACC_STATIC<br> Code:<br> stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">2</span>, args_size=<span class="hljs-number">0</span><br> <span class="hljs-number">0</span>: bipush <span class="hljs-number">10</span> <span class="hljs-comment">// <- 10 放入栈顶</span><br> <span class="hljs-number">2</span>: istore_0 <span class="hljs-comment">// 10 -> slot 0 (从栈顶移除了)</span><br> <span class="hljs-number">3</span>: bipush <span class="hljs-number">20</span> <span class="hljs-comment">// <- 20 放入栈顶</span><br> <span class="hljs-number">5</span>: ireturn <span class="hljs-comment">// 返回栈顶 int(20)</span><br> <span class="hljs-number">6</span>: astore_1 <span class="hljs-comment">// catch any -> slot 1</span><br> <span class="hljs-number">7</span>: bipush <span class="hljs-number">20</span> <span class="hljs-comment">// <- 20 放入栈顶</span><br> <span class="hljs-number">9</span>: ireturn <span class="hljs-comment">// 返回栈顶 int(20)</span><br> Exception table:<br> from to target type<br> <span class="hljs-number">0</span> <span class="hljs-number">3</span> <span class="hljs-number">6</span> any<br> LineNumberTable: ...<br> StackMapTable: ...<br></code></pre></td></tr></table></figure><p><strong>所以结果为20</strong></p><ul><li><p>由于 finally 中的 <strong>ireturn</strong> 被插入了所有可能的流程,因此返回结果肯定以finally的为准</p></li><li><p>至于字节码中第 2 行,似乎没啥用,且留个伏笔,看下个例子</p><ul><li><p>实际上呢是istore_0后面还有iload_0、和istore_1 用来暂存返回值;</p></li><li><p>这个时候临时变量表0号位和1号位都是 10,操作数栈为空</p></li><li><p>然后执行finally,bipush 20后面起始还有,istore_0,iload_0</p></li><li><p>这个时候呢临时变量表0号位是 10,局部<strong>变量表栈顶是20</strong></p></li><li><p>最后ireturn 20</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs Java">完整code <br> <span class="hljs-number">0</span>: bipush <span class="hljs-number">10</span><br> <span class="hljs-number">2</span>: istore_0<br> <span class="hljs-number">3</span>: iload_0<br> <span class="hljs-number">4</span>: istore_1 <span class="hljs-comment">//暂存返回值</span><br> <span class="hljs-number">5</span>: bipush <span class="hljs-number">20</span><br> <span class="hljs-number">7</span>: istore_0<br> <span class="hljs-number">8</span>: iload_0<br> <span class="hljs-number">9</span>: ireturn<span class="hljs-comment">//ireturn会返回操作数栈顶的整型值20</span><br><span class="hljs-comment">//如果出现异常,还是会执行finally块中的内容,没有抛出异常</span><br><span class="hljs-number">10</span>: astore_2<br><span class="hljs-number">11</span>: bipush <span class="hljs-number">20</span><br><span class="hljs-number">13</span>: istore_0<br><span class="hljs-number">14</span>: iload_0<br><span class="hljs-number">15</span>: ireturn<span class="hljs-comment">//这里没有athrow了,也就是如果在finally块中如果有返回操作的话,且try块中出现异常,会吞掉异常!</span><br></code></pre></td></tr></table></figure></li></ul></li><li><p>跟上例中的 finally 相比,发现<strong>没有 athrow 了</strong>,这告诉我们:如果在 finally 中出现了 return,会<strong>吞掉异常</strong></p></li><li><p>所以<strong>不要在finally中进行返回操作</strong></p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> Demo3.test();<br> <span class="hljs-comment">//最终结果为20</span><br> System.out.println(i);<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> {<br> <span class="hljs-type">int</span> i;<br> <span class="hljs-keyword">try</span> {<br> i = <span class="hljs-number">10</span>;<br> <span class="hljs-comment">//这里应该会抛出异常</span><br> i = i/<span class="hljs-number">0</span>;<br> <span class="hljs-keyword">return</span> i;<br> } <span class="hljs-keyword">finally</span> {<br> i = <span class="hljs-number">20</span>;<br> <span class="hljs-keyword">return</span> i;<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><p>会发现打印结果为20,并未抛出异常</p><hr><h5 id="题二:finally中不含有return"><a href="#题二:finally中不含有return" class="headerlink" title="题二:finally中不含有return"></a>题二:finally中不含有return</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo4</span> {<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> Demo4.test();<br>System.out.println(i);<br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> {<br><span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;<br><span class="hljs-keyword">try</span> {<br><span class="hljs-keyword">return</span> i;<br>} <span class="hljs-keyword">finally</span> {<br>i = <span class="hljs-number">20</span>;<br>}<br>}<br>}Copy<br></code></pre></td></tr></table></figure><p>对应字节码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java">Code:<br> stack=<span class="hljs-number">1</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">0</span><br> <span class="hljs-number">0</span>: bipush <span class="hljs-number">10</span><br> <span class="hljs-number">2</span>: istore_0 <span class="hljs-comment">//赋值给i 10</span><br> <span class="hljs-number">3</span>: iload_0<span class="hljs-comment">//加载到操作数栈顶</span><br> <span class="hljs-number">4</span>: istore_1 <span class="hljs-comment">//加载到局部变量表的1号位置</span><br> <span class="hljs-number">5</span>: bipush <span class="hljs-number">20</span><br> <span class="hljs-number">7</span>: istore_0 <span class="hljs-comment">//赋值给i 20</span><br> <span class="hljs-number">8</span>: iload_1 <span class="hljs-comment">//加载局部变量表1号位置的数10到操作数栈</span><br> <span class="hljs-number">9</span>: ireturn <span class="hljs-comment">//返回操作数栈顶元素 10</span><br> <span class="hljs-number">10</span>: astore_2<br> <span class="hljs-number">11</span>: bipush <span class="hljs-number">20</span><br> <span class="hljs-number">13</span>: istore_0<br> <span class="hljs-number">14</span>: aload_2 <span class="hljs-comment">//加载异常</span><br> <span class="hljs-number">15</span>: athrow <span class="hljs-comment">//抛出异常</span><br> Exception table:<br> from to target type<br> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">10</span> any<br></code></pre></td></tr></table></figure><p>即上道题中卖的关子,也解释了;第3,4行的意图就是在1号位上保存好这个return,因为后面还有finally的代码要执行</p><p>答案为10</p><hr><h3 id="3-2-11-synchronized"><a href="#3-2-11-synchronized" class="headerlink" title="3.2.11 synchronized"></a>3.2.11 synchronized</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo5</span> {<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;<br><span class="hljs-type">Lock</span> <span class="hljs-variable">lock</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Lock</span>();<br><span class="hljs-keyword">synchronized</span> (lock) {<br>System.out.println(i);<br>}<br>}<br>}<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Lock</span>{}<br></code></pre></td></tr></table></figure><p>对应字节码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs java">Code:<br> stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">5</span>, args_size=<span class="hljs-number">1</span><br> <span class="hljs-number">0</span>: bipush <span class="hljs-number">10</span><br> <span class="hljs-number">2</span>: istore_1<br> <span class="hljs-number">3</span>: <span class="hljs-keyword">new</span> #<span class="hljs-number">2</span> <span class="hljs-comment">// class com/nyima/JVM/day06/Lock</span><br> <span class="hljs-number">6</span>: dup <span class="hljs-comment">//复制一份,放到操作数栈顶,用于构造函数消耗</span><br> <span class="hljs-number">7</span>: invokespecial #<span class="hljs-number">3</span> <span class="hljs-comment">// Method com/nyima/JVM/day06/Lock."<init>":()V</span><br> <span class="hljs-number">10</span>: astore_2 <span class="hljs-comment">//剩下的一份放到局部变量表的2号位置</span><br> <span class="hljs-number">11</span>: aload_2 <span class="hljs-comment">//加载到操作数栈</span><br> <span class="hljs-number">12</span>: dup <span class="hljs-comment">//复制一份,放到操作数栈,用于加锁时消耗</span><br> <span class="hljs-number">13</span>: astore_3 <span class="hljs-comment">//将操作数栈顶元素弹出,暂存到局部变量表的三号槽位。这时操作数栈中有一份对象的引用</span><br> <span class="hljs-number">14</span>: monitorenter <span class="hljs-comment">//加锁</span><br> <span class="hljs-comment">//锁住后代码块中的操作 </span><br> <span class="hljs-number">15</span>: getstatic #<span class="hljs-number">4</span> <span class="hljs-comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span><br> <span class="hljs-number">18</span>: iload_1<br> <span class="hljs-number">19</span>: invokevirtual #<span class="hljs-number">5</span> <span class="hljs-comment">// Method java/io/PrintStream.println:(I)V</span><br> <span class="hljs-comment">//加载局部变量表中三号槽位对象的引用,用于解锁 </span><br> <span class="hljs-number">22</span>: aload_3 <br> <span class="hljs-number">23</span>: monitorexit <span class="hljs-comment">//解锁</span><br> <span class="hljs-number">24</span>: goto <span class="hljs-number">34</span><br> <span class="hljs-comment">//异常操作 </span><br> <span class="hljs-number">27</span>: astore <span class="hljs-number">4</span><br> <span class="hljs-number">29</span>: aload_3<br> <span class="hljs-number">30</span>: monitorexit <span class="hljs-comment">//解锁</span><br> <span class="hljs-number">31</span>: aload <span class="hljs-number">4</span><br> <span class="hljs-number">33</span>: athrow<br> <span class="hljs-number">34</span>: <span class="hljs-keyword">return</span><br> <span class="hljs-comment">//可以看出,无论何时出现异常,都会跳转到27行,将异常放入局部变量中,并进行解锁操作,然后加载异常并抛出异常。 </span><br> Exception table:<br> from to target type<br> <span class="hljs-number">15</span> <span class="hljs-number">24</span> <span class="hljs-number">27</span> any<br> <span class="hljs-number">27</span> <span class="hljs-number">31</span> <span class="hljs-number">27</span> any<br></code></pre></td></tr></table></figure><hr><h2 id="3-3-编译期处理"><a href="#3-3-编译期处理" class="headerlink" title="3.3 编译期处理"></a>3.3 编译期处理</h2><p>所谓的 <strong>语法糖</strong> ,其实就是指 java 编译器把 *.java 源码编译为 *.class 字节码的过程中,<strong>自动生成</strong>和<strong>转换</strong>的一些代码,主要是为了减轻程序员的负担,算是 java 编译器给我们的一个额外福利</p><p><strong>注意</strong>,以下代码的分析,借助了 javap 工具,idea 的反编译功能,idea 插件 jclasslib 等工具。另外, 编译器转换的<strong>结果直接就是 class 字节码</strong>,只是为了便于阅读,给出了 几乎等价 的 java 源码方式,并不是编译器还会转换出中间的 java 源码,切记。</p><blockquote><p>引用了大佬的笔记:</p><p><a href="https://nyimac.gitee.io/2020/07/03/JVM%E5%AD%A6%E4%B9%A0/">https://nyimac.gitee.io/2020/07/03/JVM%E5%AD%A6%E4%B9%A0/</a></p></blockquote><h3 id="3-3-1-默认构造函数"><a href="#3-3-1-默认构造函数" class="headerlink" title="3.3.1 默认构造函数"></a>3.3.1 默认构造函数</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Candy1</span> {<br><br>}<br></code></pre></td></tr></table></figure><p>经过编译期优化后</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Candy1</span> {<br> <span class="hljs-comment">//这个无参构造器是java编译器帮我们加上的</span><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">Candy1</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">//即调用父类 Object 的无参构造方法,即调用 java/lang/Object." <init>":()V</span><br> <span class="hljs-built_in">super</span>();<br> }<br>}<br></code></pre></td></tr></table></figure><hr><h3 id="3-3-2-自动拆装箱"><a href="#3-3-2-自动拆装箱" class="headerlink" title="3.3.2 自动拆装箱"></a>3.3.2 自动拆装箱</h3><p>基本类型和其包装类型的相互转换过程,称为拆装箱</p><p>在JDK 5以后,它们的转换可以在编译期自动完成</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">Integer</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br> <span class="hljs-type">int</span> <span class="hljs-variable">y</span> <span class="hljs-operator">=</span> x;<br> }<br>}<br></code></pre></td></tr></table></figure><p>转换过程如下</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">//基本类型赋值给包装类型,称为装箱</span><br> <span class="hljs-type">Integer</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> Integer.valueOf(<span class="hljs-number">1</span>);<br> <span class="hljs-comment">//包装类型赋值给基本类型,称谓拆箱</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">y</span> <span class="hljs-operator">=</span> x.intValue();<br> }<br>}<br></code></pre></td></tr></table></figure><hr><h3 id="3-3-3-泛型集合取值"><a href="#3-3-3-泛型集合取值" class="headerlink" title="3.3.3 泛型集合取值"></a>3.3.3 泛型集合取值</h3><p>泛型也是在 JDK 5 开始加入的特性,但 java 在<strong>编译泛型代码后</strong>会执行 <strong>泛型擦除</strong> 的动作,即泛型信息在编译为字节码之后就<strong>丢失</strong>了,实际的类型都当做了 <strong>Object</strong> 类型来处理:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo3</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> List<Integer> list = <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>();<br> list.add(<span class="hljs-number">10</span>);<br> <span class="hljs-type">Integer</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> list.get(<span class="hljs-number">0</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><p>对应字节码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java">Code:<br> stack=<span class="hljs-number">2</span>, locals=<span class="hljs-number">3</span>, args_size=<span class="hljs-number">1</span><br> <span class="hljs-number">0</span>: <span class="hljs-keyword">new</span> #<span class="hljs-number">2</span> <span class="hljs-comment">// class java/util/ArrayList</span><br> <span class="hljs-number">3</span>: dup<br> <span class="hljs-number">4</span>: invokespecial #<span class="hljs-number">3</span> <span class="hljs-comment">// Method java/util/ArrayList."<init>":()V</span><br> <span class="hljs-number">7</span>: astore_1<br> <span class="hljs-number">8</span>: aload_1<br> <span class="hljs-number">9</span>: bipush <span class="hljs-number">10</span><br> <span class="hljs-number">11</span>: invokestatic #<span class="hljs-number">4</span> <span class="hljs-comment">// Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;</span><br> <span class="hljs-comment">//这里进行了泛型擦除,实际调用的是add(Objcet o)</span><br> <span class="hljs-number">14</span>: invokeinterface #<span class="hljs-number">5</span>, <span class="hljs-number">2</span> <span class="hljs-comment">// InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z</span><br><br> <span class="hljs-number">19</span>: pop<br> <span class="hljs-number">20</span>: aload_1<br> <span class="hljs-number">21</span>: iconst_0<br> <span class="hljs-comment">//这里也进行了泛型擦除,实际调用的是get(Object o) </span><br> <span class="hljs-number">22</span>: invokeinterface #<span class="hljs-number">6</span>, <span class="hljs-number">2</span> <span class="hljs-comment">// InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;</span><br><span class="hljs-comment">//这里进行了类型转换,将Object转换成了Integer</span><br> <span class="hljs-number">27</span>: checkcast #<span class="hljs-number">7</span> <span class="hljs-comment">// class java/lang/Integer</span><br> <span class="hljs-number">30</span>: astore_2<br> <span class="hljs-number">31</span>: returnCopy<br></code></pre></td></tr></table></figure><p>所以调用get函数取值时,有一个类型转换的操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Integer</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> (Integer) list.get(<span class="hljs-number">0</span>);<br></code></pre></td></tr></table></figure><p>如果要将返回结果赋值给一个int类型的变量,则还有<strong>自动拆箱</strong>的操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">int</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> (Integer) list.get(<span class="hljs-number">0</span>).intValue();<br></code></pre></td></tr></table></figure><p>还好这些麻烦事都不用自己做。</p><hr><h3 id="3-3-4-可变参数"><a href="#3-3-4-可变参数" class="headerlink" title="3.3.4 可变参数"></a>3.3.4 可变参数</h3><p>可变参数也是 JDK 5 开始加入的新特性:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo4</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">foo</span><span class="hljs-params">(String... args)</span> {<br> <span class="hljs-comment">//将args赋值给arr,可以看出String...实际就是String[] </span><br> String[] arr = args;<br> System.out.println(arr.length);<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> foo(<span class="hljs-string">"hello"</span>, <span class="hljs-string">"world"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><p>可变参数 <strong>String…</strong> args 其实是一个 <strong>String[]</strong> args ,从代码中的赋值语句中就可以看出来。 同 样 java 编译器会在编译期间将上述代码变换为:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo4</span> {<br> <span class="hljs-keyword">public</span> Demo4 {}<br><br> <br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">foo</span><span class="hljs-params">(String[] args)</span> {<br> String[] arr = args;<br> System.out.println(arr.length);<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> foo(<span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>[]{<span class="hljs-string">"hello"</span>, <span class="hljs-string">"world"</span>});<br> }<br>}Copy<br></code></pre></td></tr></table></figure><p>注意,如果调用的是foo(),即未传递参数时,等价代码为foo(new String[]{}),<strong>创建了一个空数组</strong>,而不是直接传递的null</p><hr><h3 id="3-3-5-foreach"><a href="#3-3-5-foreach" class="headerlink" title="3.3.5 foreach"></a>3.3.5 foreach</h3><p>仍是 JDK 5 开始引入的语法糖,数组的循环:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo5</span> {<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">//数组赋初值的简化写法也是一种语法糖。</span><br><span class="hljs-type">int</span>[] arr = {<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>};<br><span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> x : arr) {<br>System.out.println(x);<br>}<br>}<br>}<br></code></pre></td></tr></table></figure><p>编译器会帮我们转换为</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo5</span> {<br> <span class="hljs-keyword">public</span> Demo5 {}<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><span class="hljs-type">int</span>[] arr = <span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[]{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>};<br><span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>; i<arr.length; ++i) {<br><span class="hljs-type">int</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> arr[i];<br>System.out.println(x);<br>}<br>}<br>}<br></code></pre></td></tr></table></figure><p><strong>如果是集合使用foreach</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo5</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> List<Integer> list = Arrays.asList(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>);<br> <span class="hljs-keyword">for</span> (Integer x : list) {<br> System.out.println(x);<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><p>集合要使用foreach,需要该集合类实现了<strong>Iterable接口</strong>,因为集合的遍历需要用到<strong>迭代器Iterator</strong>,实际被编译器转换为迭代器的调用</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo5</span> {<br> <span class="hljs-keyword">public</span> Demo5 {}<br> <br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> List<Integer> list = Arrays.asList(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>);<br> <span class="hljs-comment">//获得该集合的迭代器</span><br> Iterator<Integer> iterator = list.iterator();<br> <span class="hljs-keyword">while</span>(iterator.hasNext()) {<br> <span class="hljs-type">Integer</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> iterator.next();<br> System.out.println(x);<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>注意<br>foreach 循环写法,能够配合数组,以及所有实现了 Iterable 接口的集合类一起使用,</p><p>其中Iterable 用来获取集合的迭代器( Iterator )</p></blockquote><hr><h3 id="3-3-6-switch字符串"><a href="#3-3-6-switch字符串" class="headerlink" title="3.3.6 switch字符串"></a>3.3.6 switch字符串</h3><p>从 JDK 7 开始,switch 可以作用于字符串和枚举类,这个功能其实也是语法糖,例如:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo6</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">"hello"</span>;<br> <span class="hljs-keyword">switch</span> (str) {<br> <span class="hljs-keyword">case</span> <span class="hljs-string">"hello"</span> :<br> System.out.println(<span class="hljs-string">"h"</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> <span class="hljs-string">"world"</span> :<br> System.out.println(<span class="hljs-string">"w"</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">default</span>:<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>注意<br>switch 配合 String 和枚举使用时,变量不能为null,原因分析完语法糖转换后的代码应当自然清楚</p></blockquote><p>会被编译器转换为:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo6</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">Demo6</span><span class="hljs-params">()</span> {<br> <br> }<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">str</span> <span class="hljs-operator">=</span> <span class="hljs-string">"hello"</span>;<br> <span class="hljs-type">int</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> -<span class="hljs-number">1</span>;<br> <span class="hljs-comment">//通过字符串的hashCode+value来判断是否匹配</span><br> <span class="hljs-keyword">switch</span> (str.hashCode()) {<br> <span class="hljs-comment">//hello的hashCode</span><br> <span class="hljs-keyword">case</span> <span class="hljs-number">99162322</span> :<br> <span class="hljs-comment">//再次比较,因为字符串的hashCode有可能相等</span><br> <span class="hljs-keyword">if</span>(str.equals(<span class="hljs-string">"hello"</span>)) {<br> x = <span class="hljs-number">0</span>;<br> }<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-comment">//world的hashCode</span><br> <span class="hljs-keyword">case</span> <span class="hljs-number">11331880</span> :<br> <span class="hljs-keyword">if</span>(str.equals(<span class="hljs-string">"world"</span>)) {<br> x = <span class="hljs-number">1</span>;<br> }<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">default</span>:<br> <span class="hljs-keyword">break</span>;<br> }<br><br> <span class="hljs-comment">//用第二个switch在进行输出判断</span><br> <span class="hljs-keyword">switch</span> (x) {<br> <span class="hljs-keyword">case</span> <span class="hljs-number">0</span>:<br> System.out.println(<span class="hljs-string">"h"</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:<br> System.out.println(<span class="hljs-string">"w"</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">default</span>:<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><p>过程说明:</p><ul><li><p>在编译期间,单个的switch被分为了两个</p><ul><li><p>第一个用来匹配字符串,并给x赋值</p><ul><li><p>字符串的匹配用到了字符串的hashCode,还用到了equals方法</p></li><li><p>使用hashCode是为了提高比较效率,使用equals是防止有hashCode冲突(如BM和C.)</p></li><li><p>第二个用来根据x的值来决定输出语句</p></li><li><p>例如:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Candy6_2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">choose</span><span class="hljs-params">(String str)</span> {<br> <span class="hljs-keyword">switch</span> (str) {<br> <span class="hljs-keyword">case</span> <span class="hljs-string">"BM"</span>: {<br> System.out.println(<span class="hljs-string">"h"</span>);<br> <span class="hljs-keyword">break</span>;<br> }<br> <span class="hljs-keyword">case</span> <span class="hljs-string">"C."</span>: {<br> System.out.println(<span class="hljs-string">"w"</span>);<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br> }<br>}<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment">**会被转换为</span><br><span class="hljs-comment">**/</span><br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Candy6_2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">Candy6_2</span><span class="hljs-params">()</span> {<br> }<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">choose</span><span class="hljs-params">(String str)</span> {<br> <span class="hljs-type">byte</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> -<span class="hljs-number">1</span>;<br> <span class="hljs-keyword">switch</span>(str.hashCode()) {<br> <span class="hljs-keyword">case</span> <span class="hljs-number">2123</span>: <span class="hljs-comment">// hashCode 值可能相同,需要进一步用 equals 比较</span><br> <span class="hljs-keyword">if</span> (str.equals(<span class="hljs-string">"C."</span>)) {<br> x = <span class="hljs-number">1</span>;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (str.equals(<span class="hljs-string">"BM"</span>)) {<br> x = <span class="hljs-number">0</span>;<br> }<br> <span class="hljs-keyword">default</span>:<br> <span class="hljs-keyword">switch</span>(x) {<br> <span class="hljs-keyword">case</span> <span class="hljs-number">0</span>:<br> System.out.println(<span class="hljs-string">"h"</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:<br> System.out.println(<span class="hljs-string">"w"</span>);<br> }<br> }<br> }<br>}<br></code></pre></td></tr></table></figure></li></ul></li></ul></li></ul><hr><h3 id="3-3-7-switch枚举"><a href="#3-3-7-switch枚举" class="headerlink" title="3.3.7 switch枚举"></a>3.3.7 switch枚举</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo7</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">SEX</span> <span class="hljs-variable">sex</span> <span class="hljs-operator">=</span> SEX.MALE;<br> <span class="hljs-keyword">switch</span> (sex) {<br> <span class="hljs-keyword">case</span> MALE:<br> System.out.println(<span class="hljs-string">"man"</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> FEMALE:<br> System.out.println(<span class="hljs-string">"woman"</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">default</span>:<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br>}<br><br><span class="hljs-keyword">enum</span> <span class="hljs-title class_">SEX</span> {<br> MALE, FEMALE;<br>}<br></code></pre></td></tr></table></figure><p>编译器中执行的代码如下</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo7</span> {<br> <span class="hljs-comment">/** </span><br><span class="hljs-comment"> * 定义一个合成类(仅 jvm 使用,对我们不可见) </span><br><span class="hljs-comment"> * 用来映射枚举的 ordinal 与数组元素的关系 </span><br><span class="hljs-comment"> * 枚举的 ordinal 表示枚举对象的序号,从 0 开始 </span><br><span class="hljs-comment"> * 即 MALE 的 ordinal()=0,FEMALE 的 ordinal()=1 </span><br><span class="hljs-comment"> */</span> <br> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">$MAP</span> {<br> <span class="hljs-comment">//数组大小即为枚举元素个数,里面存放了case用于比较的数字</span><br> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span>[] map = <span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[<span class="hljs-number">2</span>];<br> <span class="hljs-keyword">static</span> {<br> <span class="hljs-comment">//ordinal即枚举元素对应所在的位置,MALE为0,FEMALE为1</span><br> map[SEX.MALE.ordinal()] = <span class="hljs-number">1</span>;<br> map[SEX.FEMALE.ordinal()] = <span class="hljs-number">2</span>;<br> }<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">SEX</span> <span class="hljs-variable">sex</span> <span class="hljs-operator">=</span> SEX.MALE;<br> <span class="hljs-comment">//将对应位置枚举元素的值赋给x,用于case操作</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> $MAP.map[sex.ordinal()];<br> <span class="hljs-keyword">switch</span> (x) {<br> <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:<br> System.out.println(<span class="hljs-string">"man"</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:<br> System.out.println(<span class="hljs-string">"woman"</span>);<br> <span class="hljs-keyword">break</span>;<br> <span class="hljs-keyword">default</span>:<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><hr><h3 id="3-3-8-枚举类"><a href="#3-3-8-枚举类" class="headerlink" title="3.3.8 枚举类"></a>3.3.8 枚举类</h3><p>JDK 7 新增了枚举类,以前面的性别枚举为例:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">SEX</span> {<br> MALE, FEMALE;<br>}<br></code></pre></td></tr></table></figure><p>转换后的代码</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs php"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Sex</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Enum</span><<span class="hljs-title">Sex</span>> </span>{ <br> <span class="hljs-comment">//对应枚举类中的元素</span><br> <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-keyword">final</span> Sex MALE; <br> <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> <span class="hljs-keyword">final</span> Sex FEMALE; <br> <span class="hljs-keyword">private</span> <span class="hljs-built_in">static</span> <span class="hljs-keyword">final</span> Sex[] <span class="hljs-variable">$VALUES</span>;<br> <br> <span class="hljs-built_in">static</span> { <br> <span class="hljs-comment">//调用构造函数,传入枚举元素的值及ordinal</span><br> MALE = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Sex</span>(<span class="hljs-string">"MALE"</span>, <span class="hljs-number">0</span>); <br> FEMALE = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Sex</span>(<span class="hljs-string">"FEMALE"</span>, <span class="hljs-number">1</span>); <br> <span class="hljs-variable">$VALUES</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Sex</span>[]{MALE, FEMALE}; <br> }<br> <br> <span class="hljs-comment">//调用父类中的方法</span><br> <span class="hljs-keyword">private</span> <span class="hljs-title function_ invoke__">Sex</span>(String name, <span class="hljs-keyword">int</span> ordinal) { <br> <span class="hljs-title function_ invoke__">super</span>(name, ordinal); <br> }<br> <br> <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> Sex[] <span class="hljs-title function_ invoke__">values</span>() { <br> <span class="hljs-keyword">return</span> <span class="hljs-variable">$VALUES</span>.<span class="hljs-keyword">clone</span>(); <br> }<br> <span class="hljs-keyword">public</span> <span class="hljs-built_in">static</span> Sex <span class="hljs-title function_ invoke__">valueOf</span>(String name) { <br> <span class="hljs-keyword">return</span> Enum.<span class="hljs-title function_ invoke__">valueOf</span>(Sex.<span class="hljs-keyword">class</span>, name); <br> } <br> <br>}Copy<br></code></pre></td></tr></table></figure><hr><h3 id="3-3-9-try-with-resources"><a href="#3-3-9-try-with-resources" class="headerlink" title="3.3.9 try-with-resources"></a>3.3.9 try-with-resources</h3><p><a href="https://www.bilibili.com/video/BV1yE411Z7AP?p=140">https://www.bilibili.com/video/BV1yE411Z7AP?p=140</a></p><hr><h3 id="3-3-10-方法重写时的桥接方法"><a href="#3-3-10-方法重写时的桥接方法" class="headerlink" title="3.3.10 方法重写时的桥接方法"></a>3.3.10 方法重写时的桥接方法</h3><p>我们都知道,方法重写时对返回值分两种情况:</p><ul><li>父子类的返回值完全一致</li><li>子类返回值可以是父类返回值的子类(比较绕口,见下面的例子)</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span> {<br> <span class="hljs-keyword">public</span> Number <span class="hljs-title function_">m</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;<br> }<br>}<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">B</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">A</span> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-comment">// 子类 m 方法的返回值是 Integer 是父类 m 方法返回值 Number 的子类</span><br> <span class="hljs-keyword">public</span> Integer <span class="hljs-title function_">m</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">2</span>;<br> }<br>}<br><br></code></pre></td></tr></table></figure><p>对于子类,java 编译器会做如下处理:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">class</span> <span class="hljs-title class_">B</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">A</span> {<br> <span class="hljs-keyword">public</span> Integer <span class="hljs-title function_">m</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-number">2</span>;<br> }<br> <span class="hljs-comment">// 此方法才是真正重写了父类 public Number m() 方法</span><br> <span class="hljs-keyword">public</span> synthetic bridge Number <span class="hljs-title function_">m</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">// 调用 public Integer m()</span><br> <span class="hljs-keyword">return</span> m();<br> }<br>}<br></code></pre></td></tr></table></figure><p>其中桥接方法比较特殊,仅对 java 虚拟机可见,并且与原来的 public Integer m() 没有命名冲突,可以<br>用下面反射代码来验证:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">for</span> (Method m : B.class.getDeclaredMethods()) {<br>System.out.println(m);<br>}<br><br><span class="hljs-comment">//会输出</span><br><span class="hljs-keyword">public</span> java.lang.Integer test.candy.B.m()<br><span class="hljs-keyword">public</span> java.lang.Number test.candy.B.m()<br></code></pre></td></tr></table></figure><hr><h3 id="3-3-11-匿名内部类"><a href="#3-3-11-匿名内部类" class="headerlink" title="3.3.11 匿名内部类"></a>3.3.11 匿名内部类</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo8</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">main</span>(<span class="hljs-params"><span class="hljs-built_in">String</span>[] args</span>) {<br> <span class="hljs-title class_">Runnable</span> runnable = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Runnable</span>() {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">run</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-title class_">System</span>.<span class="hljs-property">out</span>.<span class="hljs-title function_">println</span>(<span class="hljs-string">"running..."</span>);<br> }<br> };<br> }<br>}<span class="hljs-title class_">Copy</span><br></code></pre></td></tr></table></figure><p>转换后的代码</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo8</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">main</span>(<span class="hljs-params"><span class="hljs-built_in">String</span>[] args</span>) {<br> <span class="hljs-comment">//用额外创建的类来创建匿名内部类对象</span><br> <span class="hljs-title class_">Runnable</span> runnable = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Demo8</span><span class="hljs-title function_">$1</span>();<br> }<br>}<br><br><span class="hljs-comment">//创建了一个额外的类,实现了Runnable接口</span><br>final <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo8$1</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-title class_">Demo8</span><span class="hljs-title function_">$1</span>() {}<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">run</span>(<span class="hljs-params"></span>) {<br> <span class="hljs-title class_">System</span>.<span class="hljs-property">out</span>.<span class="hljs-title function_">println</span>(<span class="hljs-string">"running..."</span>);<br> }<br>}<span class="hljs-title class_">Copy</span><br></code></pre></td></tr></table></figure><p>如果匿名内部类中引用了<strong>局部变量</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo8</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br> <span class="hljs-type">Runnable</span> <span class="hljs-variable">runnable</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Runnable</span>() {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> System.out.println(x);<br> }<br> };<br> }<br>}<br></code></pre></td></tr></table></figure><p>转化后代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo8</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;<br> <span class="hljs-type">Runnable</span> <span class="hljs-variable">runnable</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Runnable</span>() {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> System.out.println(x);<br> }<br> };<br> }<br>}<br><br><span class="hljs-keyword">final</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo8$1</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> {<br> <span class="hljs-comment">//多创建了一个变量</span><br> <span class="hljs-type">int</span> val$x;<br> <span class="hljs-comment">//变为了有参构造器</span><br> <span class="hljs-keyword">public</span> Demo8$<span class="hljs-number">1</span>(<span class="hljs-type">int</span> x) {<br> <span class="hljs-built_in">this</span>.val$x = x;<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> System.out.println(val$x);<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="3-4-类加载阶段-重要"><a href="#3-4-类加载阶段-重要" class="headerlink" title="3.4 类加载阶段(重要)"></a>3.4 类加载阶段(重要)</h2><h3 id="3-4-1-加载"><a href="#3-4-1-加载" class="headerlink" title="3.4.1 加载"></a>3.4.1 加载</h3><ul><li><p>将类的字节码载入</p><p>方法区</p><p>(1.8后为元空间,在本地内存中)中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 field 有:</p><ul><li>_java_mirror 即 java 的类镜像,例如对 String 来说,它的镜像类就是 String.class,作用是把 klass 暴露给 java 使用</li><li>_super 即父类</li><li>_fields 即成员变量</li><li>_methods 即方法</li><li>_constants 即常量池</li><li>_class_loader 即类加载器</li><li>_vtable 虚方法表</li><li>_itable 接口方法</li></ul></li><li><p>如果这个类还有父类没有加载,<strong>先加载父类</strong></p></li><li><p>加载和链接可能是<strong>交替运行</strong>的</p></li></ul><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220810223332427.png" alt="image-20220810223332427"></p><blockquote><ul><li>instanceKlass 这样的【元数据】是存储在方法区(1.8 后的元空间内,而元空间又位于本地内存中),但 _java_mirror<br>是存储在堆中<ul><li>InstanceKlass和*.class(JAVA镜像类 _java_mirror)互相保存了对方的地址</li><li>类的对象在对象头中保存了*.class的地址。让对象可以通过其找到方法区中的instanceKlass,从而获取类的各种信息</li></ul></li></ul></blockquote><hr><h3 id="3-4-2-链接"><a href="#3-4-2-链接" class="headerlink" title="3.4.2 链接"></a>3.4.2 链接</h3><h4 id="3-4-2-1-验证"><a href="#3-4-2-1-验证" class="headerlink" title="3.4.2.1 验证"></a>3.4.2.1 验证</h4><p>验证类是否符合 JVM规范,安全性检查</p><p>例如:</p><p>用 UE 等支持二进制的编辑器修改 HelloWorld.class 的魔数(3.1.1),在控制台运行</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220810230057883.png" alt="image-20220810230057883"></p><hr><h4 id="3-4-2-2-准备"><a href="#3-4-2-2-准备" class="headerlink" title="3.4.2.2 准备"></a>3.4.2.2 准备</h4><p>为 static 变量分配空间,设置默认值</p><ul><li><p>static变量在JDK 7以前是存储与instanceKlass末尾。但在JDK 7以后就存储在_java_mirror末尾了(即堆中)</p></li><li><p>static变量在分配空间和赋值是在两个阶段完成的。分配空间在准备阶段完成,赋值在初始化阶段完成</p><ul><li><p>如果 static 变量是 final 的<strong>基本类型</strong>,以及<strong>字符串常量</strong>,那么<strong>编译</strong>阶段值就确定了,<strong>赋值在准备阶段完成</strong></p></li><li><p>如果 static 变量是 final 的,但属于<strong>引用类型</strong>(比如 new Object()),那么赋值也会在<strong>初始化阶段完成</strong></p></li></ul></li></ul><hr><h4 id="3-4-2-3-解析"><a href="#3-4-2-3-解析" class="headerlink" title="3.4.2.3 解析"></a>3.4.2.3 解析</h4><ul><li>将常量池中的<strong>符号引用解析为直接引用</strong><ul><li>符号引用:仅仅是个符号,不知道这个类或者方法、属性具体在内存的哪个位置</li><li>直接引用:知道这个类或者方法、属性具体在内存的哪个位置</li></ul></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> cn.itcast.jvm.t3.load;<br><span class="hljs-comment">/**</span><br><span class="hljs-comment">* 解析的含义</span><br><span class="hljs-comment">*/</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Load2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> ClassNotFoundException,IOException {<br> <span class="hljs-type">ClassLoader</span> <span class="hljs-variable">classloader</span> <span class="hljs-operator">=</span> Load2.class.getClassLoader();<br> <span class="hljs-comment">// loadClass 方法不会导致类的解析和初始化</span><br> Class<?> c = classloader.loadClass(<span class="hljs-string">"cn.itcast.jvm.t3.load.C"</span>);<br> <span class="hljs-comment">// new C(); new会导致类的解析和初始化</span><br> System.in.read();<br> }<br>}<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">C</span> {<br><span class="hljs-type">D</span> <span class="hljs-variable">d</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">D</span>();<br>}<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">D</span> {<br> <br>}<br><br></code></pre></td></tr></table></figure><hr><h3 id="3-4-3-初始化"><a href="#3-4-3-初始化" class="headerlink" title="3.4.3 初始化"></a>3.4.3 初始化</h3><p>初始化即调用 <clinit>()V ,虚拟机会保证这个类的『构造方法』的线程安全</p><ul><li>clinit()方法是由编译器自动收集类中的所有类变量的<strong>赋值动作和静态语句块</strong>(static{}块)中的语句合并产生的<ul><li>所以<strong>验证类是否被初始化,可以看该类的静态代码块是否被执行</strong></li></ul></li></ul><h4 id="3-4-3-1-发生时机"><a href="#3-4-3-1-发生时机" class="headerlink" title="3.4.3.1 发生时机"></a>3.4.3.1 发生时机</h4><p><strong>类的初始化的懒惰的</strong>,以下情况会初始化</p><ul><li>main 方法所在的类,总会被首先初始化</li><li>首次访问这个类的静态变量或静态方法时</li><li>子类初始化,如果父类还没初始化,会引发</li><li>子类访问父类的静态变量,只会触发父类的初始化</li><li>Class.forName</li><li>new 会导致初始化</li></ul><p>以下情况不会初始化</p><ul><li>访问类的 static final 静态常量(基本类型和字符串)</li><li>类对象.class 不会触发初始化</li><li>创建该类对象的数组</li><li>类加载器的.loadClass方法</li><li>Class.forNamed的参数2为false时</li></ul><p><strong>如下代码验证</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> cn.itcast.jvm.t3.load;<br><br><span class="hljs-keyword">import</span> java.io.IOException;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Load3</span> {<br> <span class="hljs-keyword">static</span> {<br> System.out.println(<span class="hljs-string">"main init"</span>);<br> }<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> ClassNotFoundException, IOException {<br><span class="hljs-comment">// // 1. 静态常量不会触发初始化</span><br><span class="hljs-comment">// System.out.println(B.b);</span><br><span class="hljs-comment">// // 2. 类对象.class 不会触发初始化</span><br><span class="hljs-comment">// System.out.println(B.class);</span><br><span class="hljs-comment">// // 3. 创建该类的数组不会触发初始化</span><br><span class="hljs-comment">// System.out.println(new B[0]);</span><br> <span class="hljs-comment">// 4. 不会初始化类 B,但会加载 B、A</span><br> <span class="hljs-type">ClassLoader</span> <span class="hljs-variable">cl</span> <span class="hljs-operator">=</span> Thread.currentThread().getContextClassLoader();<br> cl.loadClass(<span class="hljs-string">"cn.itcast.jvm.t3.load.B"</span>);<br><span class="hljs-comment">// // 5. 不会初始化类 B,但会加载 B、A</span><br><span class="hljs-comment">// ClassLoader c2 = Thread.currentThread().getContextClassLoader();</span><br><span class="hljs-comment">// Class.forName("cn.itcast.jvm.t3.load.B", false, c2);</span><br> System.in.read();<br><br><br><span class="hljs-comment">// // 1. 首次访问这个类的静态变量或静态方法时</span><br><span class="hljs-comment">// System.out.println(A.a);</span><br><span class="hljs-comment">// // 2. 子类初始化,如果父类还没初始化,会引发</span><br><span class="hljs-comment">// System.out.println(B.c);</span><br><span class="hljs-comment">// // 3. 子类访问父类静态变量,只触发父类初始化</span><br><span class="hljs-comment">// System.out.println(B.a);</span><br><span class="hljs-comment">// // 4. 会初始化类 B,并先初始化类 A</span><br><span class="hljs-comment">// Class.forName("cn.itcast.jvm.t3.load.B");</span><br> }<br>}<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span> {<br> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">static</span> {<br> System.out.println(<span class="hljs-string">"a init"</span>);<br> }<br>}<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">B</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">A</span> {<br> <span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> <span class="hljs-type">double</span> <span class="hljs-variable">b</span> <span class="hljs-operator">=</span> <span class="hljs-number">5.0</span>;<br> <span class="hljs-keyword">static</span> <span class="hljs-type">boolean</span> <span class="hljs-variable">c</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;<br> <span class="hljs-keyword">static</span> {<br> System.out.println(<span class="hljs-string">"b init"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><hr><h3 id="3-4-4-练习"><a href="#3-4-4-练习" class="headerlink" title="3.4.4 练习"></a>3.4.4 练习</h3><h4 id="3-4-4-1-练习一"><a href="#3-4-4-1-练习一" class="headerlink" title="3.4.4.1 练习一"></a>3.4.4.1 练习一</h4><p>从字节码分析,使用 a,b,c 这三个常量是否会导致 E 初始化</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> cn.itcast.jvm.t3.load;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Load4</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> System.out.println(E.a);<br> System.out.println(E.b);<br> System.out.println(E.c);<br><br> }<br>}<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">E</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">int</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span> <span class="hljs-number">10</span>;<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">String</span> <span class="hljs-variable">b</span> <span class="hljs-operator">=</span> <span class="hljs-string">"hello"</span>;<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Integer</span> <span class="hljs-variable">c</span> <span class="hljs-operator">=</span> <span class="hljs-number">20</span>; <span class="hljs-comment">// Integer.valueOf(20)</span><br> <span class="hljs-keyword">static</span> {<br> System.out.println(<span class="hljs-string">"init E"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>答案:a,b不会,c会<br>因为a和b都属于类的 static final 静态常量(基本类型和字符串)<br>c的Integer是包装类型 会用语法糖执行Integer.valueOf(20) 把 20 这个基本类型转换成Integer</p></blockquote><hr><h4 id="3-4-4-2-练习二"><a href="#3-4-4-2-练习二" class="headerlink" title="3.4.4.2 练习二"></a>3.4.4.2 练习二</h4><p>完成懒惰初始化单例模式</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> cn.itcast.jvm.t3.load;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Load9</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><span class="hljs-comment">// Singleton.test();</span><br> Singleton.getInstance();<br> }<br><br>}<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Singleton</span> {<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">test</span><span class="hljs-params">()</span> {<br> System.out.println(<span class="hljs-string">"test"</span>);<br> }<br> <br> <span class="hljs-comment">//构造方法私有</span><br> <span class="hljs-keyword">private</span> <span class="hljs-title function_">Singleton</span><span class="hljs-params">()</span> {}<br><br> <span class="hljs-comment">//静态内部类中保存单例,静态内部类的好处就是可以访问外部类的资源,比如构造方法和方法(私有的也可访问)</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LazyHolder</span>{<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">Singleton</span> <span class="hljs-variable">SINGLETON</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Singleton</span>();<br> <span class="hljs-keyword">static</span> {<br> System.out.println(<span class="hljs-string">"lazy holder init"</span>);<span class="hljs-comment">//用于验证是否初始化</span><br> }<br> }<br> <br><span class="hljs-comment">// 第一次调用 getInstance 方法,才会导致内部类加载和初始化其静态成员</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Singleton <span class="hljs-title function_">getInstance</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> LazyHolder.SINGLETON;<br> }<br>}<br><br></code></pre></td></tr></table></figure><p><strong>静态内部类中保存单例,静态内部类的好处就是可以访问外部类的资源,比如构造方法和方法(私有的也可访问)</strong></p><p>以上的实现特点是:</p><ul><li>懒惰实例化</li><li>初始化时的线程安全是有保障的</li><li>完美(哈哈哈哈哈)</li></ul><hr><h2 id="3-5-类加载器"><a href="#3-5-类加载器" class="headerlink" title="3.5 类加载器"></a>3.5 类加载器</h2><h3 id="3-5-1-类与类加载器"><a href="#3-5-1-类与类加载器" class="headerlink" title="3.5.1 类与类加载器"></a>3.5.1 类与类加载器</h3><p>类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远超类加载阶段</p><p>对于任意一个类,都必须由加载它的<strong>类加载器</strong>和这个<strong>类本身</strong>一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:<strong>比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义</strong>,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等</p><p>以JDK 8为例</p><table><thead><tr><th>名称</th><th>加载的类</th><th>说明</th></tr></thead><tbody><tr><td>Bootstrap ClassLoader(启动类加载器)</td><td>JAVA_HOME/jre/lib</td><td>无法直接访问</td></tr><tr><td>Extension ClassLoader(拓展类加载器)</td><td>JAVA_HOME/jre/lib/ext</td><td>上级为Bootstrap,<strong>显示为null</strong></td></tr><tr><td>Application ClassLoader(应用程序类加载器)</td><td>classpath</td><td>上级为Extension</td></tr><tr><td>自定义类加载器</td><td>自定义</td><td>上级为Application</td></tr></tbody></table><ul><li><p>各司其职,每个加载器只加载自己负责目录下的所有的类</p></li><li><p>层级关系:</p><ul><li><p>自底向上询问有没有加载过,例如String类</p><ul><li>自定义类加载器 问 应用程序类加载器有没有加载String,如果没有,继续往上,到达启动类加载器中已经加载过了,则String不用再加载</li></ul></li><li><p>如果都没有加载过则由最顶级开始往下,查找自己负责的目录下能不能加载;例如自定义的Student类</p><ul><li>先往上询问,肯定都没有加载过,然后再一步步下来到应用程序加载器</li></ul></li></ul></li></ul><hr><h3 id="3-5-2-启动类加载器"><a href="#3-5-2-启动类加载器" class="headerlink" title="3.5.2 启动类加载器"></a>3.5.2 启动类加载器</h3><p>可通过在控制台输入指令,使得自定义类被启动类加器加载</p><p>在正确的路径下执行:java -Xbootclasspath/a:.cn.itcast.jvm.t3.load.Load5</p><h3 id="3-5-3-扩展类加载类"><a href="#3-5-3-扩展类加载类" class="headerlink" title="3.5.3 扩展类加载类"></a>3.5.3 扩展类加载类</h3><p>如果<strong>classpath</strong>和 <strong>JAVA_HOME/jre/lib/ext</strong> 下有同名类,加载时会使用<strong>拓展类加载器</strong>加载。当应用程序类加载器发现拓展类加载器已将该同名类加载过了,则不会再次加载</p><h3 id="3-5-4-双亲委派模式"><a href="#3-5-4-双亲委派模式" class="headerlink" title="3.5.4 双亲委派模式"></a>3.5.4 双亲委派模式</h3><p>所谓的双亲委派,就是指调用类加载器的 loadClass 方法时,查找类的规则</p><blockquote><p><strong>注意</strong><br>这里的双亲,翻译为上级似乎更为合适,因为它们并没有继承关系</p></blockquote><p>loadClass源码</p><p>递归查找</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">protected</span> Class<?> loadClass(String name, <span class="hljs-type">boolean</span> resolve)<br> <span class="hljs-keyword">throws</span> ClassNotFoundException<br>{<br> <span class="hljs-keyword">synchronized</span> (getClassLoadingLock(name)) {<br> <span class="hljs-comment">// 首先查找该类是否已经被该类加载器加载过了</span><br> Class<?> c = findLoadedClass(name);<br> <span class="hljs-comment">//如果没有被加载过</span><br> <span class="hljs-keyword">if</span> (c == <span class="hljs-literal">null</span>) {<br> <span class="hljs-type">long</span> <span class="hljs-variable">t0</span> <span class="hljs-operator">=</span> System.nanoTime();<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-comment">//看是否被它的上级加载器加载过了 Extension的上级是Bootstarp,但它显示为null</span><br> <span class="hljs-keyword">if</span> (parent != <span class="hljs-literal">null</span>) {<br> <span class="hljs-comment">//有上级,就委派上级 这里是递归</span><br> c = parent.loadClass(name, <span class="hljs-literal">false</span>);<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">//如果没有上级了(ExtClassLoader),则委派BootstrapClassLoader 看是否被启动类加载器加载过</span><br> c = findBootstrapClassOrNull(name);<br> }<br> } <span class="hljs-keyword">catch</span> (ClassNotFoundException e) {<br> <span class="hljs-comment">// ClassNotFoundException thrown if class not found</span><br> <span class="hljs-comment">// from the non-null parent class loader</span><br> <span class="hljs-comment">//捕获异常,但不做任何处理</span><br> }<br><br> <span class="hljs-keyword">if</span> (c == <span class="hljs-literal">null</span>) {<br> <span class="hljs-comment">//如果还是没有找到,先让拓展类加载器调用findClass方法去找到该类,如果还是没找到,就抛出异常</span><br> <span class="hljs-type">long</span> <span class="hljs-variable">t1</span> <span class="hljs-operator">=</span> System.nanoTime();<br> c = findClass(name);<br><br> <span class="hljs-comment">// 记录时间</span><br> sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);<br> sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);<br> sun.misc.PerfCounter.getFindClasses().increment();<br> }<br> }<br> <span class="hljs-keyword">if</span> (resolve) {<br> resolveClass(c);<br> }<br> <span class="hljs-keyword">return</span> c;<br> }<br>}<br></code></pre></td></tr></table></figure><p>为了防止内存中出现多个相同的字节码;因为如果没有双亲委派的话,用户就可以自己定义一个java.lang.String类,那么就无法保证类的唯一性。</p><hr><h3 id="3-5-5-自定义加载器"><a href="#3-5-5-自定义加载器" class="headerlink" title="3.5.5 自定义加载器"></a>3.5.5 自定义加载器</h3><h4 id="3-5-5-1-使用场景"><a href="#3-5-5-1-使用场景" class="headerlink" title="3.5.5.1 使用场景"></a>3.5.5.1 使用场景</h4><ul><li>想加载非 classpath 随意路径中的类文件</li><li>通过接口来使用实现,希望解耦时,常用在框架设计</li><li>这些类希望予以隔离,不同应用的同名类都可以加载,不冲突,常见于 tomcat 容器</li></ul><h4 id="3-5-5-2-步骤"><a href="#3-5-5-2-步骤" class="headerlink" title="3.5.5.2 步骤"></a>3.5.5.2 步骤</h4><ul><li>继承ClassLoader父类</li><li>要遵从双亲委派机制,重写 findClass 方法<ul><li>不是重写loadClass方法,否则不会走双亲委派机制</li></ul></li><li>读取类文件的字节码</li><li>调用父类的 defineClass 方法来加载类</li><li>使用者调用该类加载器的 loadClass 方法</li></ul><hr><h2 id="3-6-破坏双亲委派"><a href="#3-6-破坏双亲委派" class="headerlink" title="3.6 破坏双亲委派"></a>3.6 破坏双亲委派</h2><p><strong>那怎么打破双亲委派模型</strong>?</p><p>自定义类加载器,继承ClassLoader类,重写loadClass方法和findClass方法。</p><p><strong>列举一些你知道的打破双亲委派机制的例子,为什么要打破?</strong></p><ul><li><p>JNDI 通过引入线程上下文类加载器,可以在 Thread.setContextClassLoader 方法设置,默认是应用程序类加载器,来加载 SPI 的代码。有了线程上下文类加载器,就可以完成父类加载器请求子类加载器完成类加载的行为。打破的原因,是为了 JNDI 服务的类加载器是启动器类加载,为了完成高级类加载器请求子类加载器(即上文中的线程上下文加载器)加载类。</p></li><li><p>Tomcat,应用的类加载器优先自行加载应用目录下的 class,并不是先委派给父加载器,加载不了才委派给父加载器。</p><p>tomcat之所以造了一堆自己的classloader,大致是出于下面三类目的:</p><ul><li>对于各个 <code>webapp</code>中的 <code>class</code>和 <code>lib</code>,需要相互隔离,不能出现一个应用中加载的类库会影响另一个应用的情况,而对于许多应用,需要有共享的lib以便不浪费资源。</li><li>与 <code>jvm</code>一样的安全性问题。使用单独的 <code>classloader</code>去装载 <code>tomcat</code>自身的类库,以免其他恶意或无意的破坏;</li><li>热部署。</li></ul><p>tomcat类加载器如下图:</p><p><img src="http://blog-img.coolsen.cn/img/image-20210329231930719.png" alt="img"></p></li><li><p>OSGi,实现模块化热部署,为每个模块都自定义了类加载器,需要更换模块时,模块与类加载器一起更换。其类加载的过程中,有平级的类加载器加载行为。打破的原因是为了实现模块热替换。</p></li><li><p>JDK 9,Extension ClassLoader 被 Platform ClassLoader 取代,当平台及应用程序类加载器收到类加载请求,在委派给父加载器加载前,要先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载。打破的原因,是为了添加模块化的特性。</p></li></ul><hr><h2 id="3-7-运行期优化"><a href="#3-7-运行期优化" class="headerlink" title="3.7 运行期优化"></a>3.7 运行期优化</h2><h3 id="3-7-1-即时编译"><a href="#3-7-1-即时编译" class="headerlink" title="3.7.1 即时编译"></a>3.7.1 即时编译</h3><h4 id="3-7-1-1分层编译"><a href="#3-7-1-1分层编译" class="headerlink" title="3.7.1.1分层编译"></a>3.7.1.1分层编译</h4><p>JVM 将执行状态分成了 5 个层次:</p><ul><li>0层:解释执行,用解释器将字节码翻译为机器码</li><li>1层:使用 <strong>C1 即时编译器</strong>编译执行(不带 profiling)</li><li>2层:使用 C1 即时编译器编译执行(带基本的profiling)</li><li>3层:使用 C1 即时编译器编译执行(带完全的profiling)</li><li>4层:使用 <strong>C2 即时编译器</strong>编译执行</li></ul><blockquote><p>profiling 是指在运行过程中收集一些程序执行状态的数据,例如【方法的调用次数】,【循环的 回边次数】等</p></blockquote><h5 id="即时编译器(JIT)与解释器的区别"><a href="#即时编译器(JIT)与解释器的区别" class="headerlink" title="即时编译器(JIT)与解释器的区别"></a>即时编译器(JIT)与解释器的区别</h5><ul><li>解释器<ul><li>将字节码<strong>解释</strong>为机器码,下次即使遇到相同的字节码,仍会执行重复的解释</li><li>是将字节码解释为针对所有平台都通用的机器码</li></ul></li><li>即时编译器<ul><li>将一些字节码<strong>编译</strong>为机器码,<strong>并存入 Code Cache</strong>,下次遇到相同的代码,直接执行,无需再编译</li><li>根据平台类型,生成平台特定的机器码</li></ul></li></ul><p>对于大部分的不常用的代码,我们无需耗费时间将其<strong>编译</strong>成机器码,而是采取解释执行的方式运行;另一方面,对于仅占据小部分的热点代码,我们则可以将其编译成机器码,以达到理想的运行速度。 执行效率上简单比较一下 Interpreter < C1 < C2,总的目标是发现热点代码(hotspot名称的由 来),并优化这些热点代码(例如循环1000次 new Obkect对象,一定次数之后,就会把new Object()编译成机器码,提高效率)</p><h5 id="逃逸分析"><a href="#逃逸分析" class="headerlink" title="逃逸分析"></a>逃逸分析</h5><p>逃逸分析(Escape Analysis)简单来讲就是,Java Hotspot 虚拟机可以分析新创建对象的使用范围,并决定是否在 Java 堆上分配内存的一项技术(例如上述,1000个Object对象循环创建,但是从来没用过,就会在一段时间后发生逃逸,修改字节码,后续使它实际上没有被创建)</p><p>逃逸分析的 JVM 参数如下:</p><ul><li>开启逃逸分析:-XX:+DoEscapeAnalysis</li><li>关闭逃逸分析:-XX:-DoEscapeAnalysis</li><li>显示分析结果:-XX:+PrintEscapeAnalysis</li></ul><p>逃逸分析技术在 Java SE 6u23+ 开始支持,并默认设置为启用状态,可以不用额外加这个参数</p><hr><h3 id="3-7-2-方法内联"><a href="#3-7-2-方法内联" class="headerlink" title="3.7.2 方法内联"></a>3.7.2 方法内联</h3><h4 id="3-7-2-1-内联函数"><a href="#3-7-2-1-内联函数" class="headerlink" title="3.7.2.1 内联函数"></a>3.7.2.1 内联函数</h4><p>内联函数就是在程序编译时,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体来直接进行替换</p><p>C++是否为内联函数由自己决定,<strong>Java由编译器决定</strong>。Java不支持直接声明为内联函数的,如果想让他内联,你只能够向编译器提出请求: 关键字<strong>final修饰</strong> 用来指明那个函数是希望被JVM内联的,如</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">doSomething</span><span class="hljs-params">()</span> { <br> <span class="hljs-comment">// to do something </span><br>}<br></code></pre></td></tr></table></figure><p>总的来说,一般的函数都不会被当做内联函数,只有声明了final后,编译器才会考虑是不是要把你的函数变成内联函数</p><p>JVM内建有许多运行时优化。首先<strong>短方法</strong>更利于JVM推断。流程更明显,作用域更短,副作用也更明显。如果是长方法JVM可能直接就跪了。</p><hr><h4 id="3-7-2-1-举例"><a href="#3-7-2-1-举例" class="headerlink" title="3.7.2.1 举例"></a>3.7.2.1 举例</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">square</span><span class="hljs-params">(<span class="hljs-keyword">final</span> <span class="hljs-type">int</span> i)</span> {<br><span class="hljs-keyword">return</span> i * i;<br>}<br><br>System.out.println(square(<span class="hljs-number">9</span>));<br></code></pre></td></tr></table></figure><p>如果发现 square 是热点方法,并且长度不太长时,会进行内联,所谓的内联就是把方法内代码拷贝、<br>粘贴到调用者的位置:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">System.out.println(<span class="hljs-number">9</span> * <span class="hljs-number">9</span>);<br></code></pre></td></tr></table></figure><p>还能够进行常量折叠(constant folding)的优化(因为计算结果始终都是81,就当成一个常量看)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">System.out.println(<span class="hljs-number">81</span>);<br></code></pre></td></tr></table></figure><hr><h1 id="4-内存模型"><a href="#4-内存模型" class="headerlink" title="4. 内存模型"></a>4. 内存模型</h1><p>听说这部分结合juc并发学习更好,学成后更新^_^</p>]]></content>
<categories>
<category>Java</category>
<category>JVM</category>
</categories>
<tags>
<tag>Java</tag>
<tag>JVM</tag>
</tags>
</entry>
<entry>
<title>插入排序</title>
<link href="/2022/07/25/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F/"/>
<url>/2022/07/25/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="1-插入排序思路"><a href="#1-插入排序思路" class="headerlink" title="1. 插入排序思路"></a>1. 插入排序思路</h1><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812183249450.png" alt="image-20220812183249450"></p><hr><h1 id="2-代码实现"><a href="#2-代码实现" class="headerlink" title="2. 代码实现"></a>2. 代码实现</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">cha_ru_pai_xu</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] a = {<span class="hljs-number">9</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>};<br> <span class="hljs-type">int</span>[] b = {<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">3</span>};<br> insert(a);<br> }<br><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">insert</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">n</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i < a.length; i++) {<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> a[i]; <span class="hljs-comment">// 记录当前要插入的值</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">j</span> <span class="hljs-operator">=</span> i - <span class="hljs-number">1</span>;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 这是从后往前比较插入</span><br><span class="hljs-comment"> * 这样会有一个空位直接移位</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">while</span> (j >= <span class="hljs-number">0</span>){<br> <span class="hljs-comment">// 记住 temp 不能用 a[i]代替 因为下面的对换可能会改变a[i]的值</span><br> <span class="hljs-keyword">if</span> (a[j] > temp) {<br> a[j+<span class="hljs-number">1</span>] = a[j];<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">break</span>;<br> j--;<br> }<br> a[j + <span class="hljs-number">1</span>] = temp;<br> System.out.println(Arrays.toString(a));<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java">[<span class="hljs-number">3</span>, <span class="hljs-number">9</span>, <span class="hljs-number">7</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>]<br>[<span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">9</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">9</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">9</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">4</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br></code></pre></td></tr></table></figure><hr><h1 id="3-简单图解"><a href="#3-简单图解" class="headerlink" title="3. 简单图解"></a>3. 简单图解</h1><p>[2,3,1]为例</p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812184750309.png" alt="image-20220812184750309"></p><hr><h1 id="4-与选择排序比较"><a href="#4-与选择排序比较" class="headerlink" title="4. 与选择排序比较"></a>4. 与选择排序比较</h1><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812184838772.png" alt="image-20220812184838772"></p><hr><h1 id="5-插入、选择面试题例"><a href="#5-插入、选择面试题例" class="headerlink" title="5. 插入、选择面试题例"></a>5. 插入、选择面试题例</h1><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812184917509.png" alt="image-20220812184917509"></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">9</span>、<span class="hljs-number">18</span>、<span class="hljs-number">19</span>、<span class="hljs-number">23</span>、<span class="hljs-number">23</span>、<span class="hljs-number">15</span><br></code></pre></td></tr></table></figure><hr><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812184951572.png" alt="image-20220812184951572"></p><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">9</span>、<span class="hljs-number">15</span>、<span class="hljs-number">18</span>、<span class="hljs-number">19</span>、<span class="hljs-number">23</span>、<span class="hljs-number">23</span><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>算法与数据结构</category>
<category>排序/查找</category>
</categories>
<tags>
<tag>排序</tag>
</tags>
</entry>
<entry>
<title>二分查找</title>
<link href="/2022/07/24/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
<url>/2022/07/24/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="1-算法实现流程"><a href="#1-算法实现流程" class="headerlink" title="1. 算法实现流程"></a>1. 算法实现流程</h1><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812175040199.png" alt="image-20220812175040199"></p><hr><h1 id="2-实现代码"><a href="#2-实现代码" class="headerlink" title="2. 实现代码"></a>2. 实现代码</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">er_fen_cha_zhao</span> { <br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] array = {<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">7</span>,<span class="hljs-number">8</span>,<span class="hljs-number">9</span>,<span class="hljs-number">10</span>};<br> <span class="hljs-type">int</span> <span class="hljs-variable">target</span> <span class="hljs-operator">=</span> <span class="hljs-number">100</span>;<br> System.out.println(<span class="hljs-string">"二分查找"</span>);<br> <span class="hljs-type">int</span> <span class="hljs-variable">idx</span> <span class="hljs-operator">=</span> binarySearch(array,target);<br> <span class="hljs-keyword">if</span> (idx != -<span class="hljs-number">1</span>){<br> System.out.println(<span class="hljs-string">"目标存在,位置为array["</span> + idx +<span class="hljs-string">"] ==> "</span> + array[idx]);<br> }<span class="hljs-keyword">else</span> System.out.println(<span class="hljs-string">"目标不存在"</span>);<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">binarySearch</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> t)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">left</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-type">int</span> <span class="hljs-variable">right</span> <span class="hljs-operator">=</span> a.length - <span class="hljs-number">1</span>;<br> <span class="hljs-type">int</span> middle;<br> <span class="hljs-keyword">while</span> (left <= right){<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 二分查找 求中点 用加号</span><br><span class="hljs-comment"> */</span><br> middle = (right + left) / <span class="hljs-number">2</span>;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 当 left 和 right 很大的时候 相加再除以2就会产生整型溢出</span><br><span class="hljs-comment"> * 变换成</span><br><span class="hljs-comment"> * (right + left) / 2 ==> </span><br><span class="hljs-comment"> * left/2 + right/2 ==> </span><br><span class="hljs-comment"> * left + (-left/2 + right/2 ) ==> </span><br><span class="hljs-comment"> * left + (right - left)/2</span><br><span class="hljs-comment"> */</span><br> middle = left + (right - left)/<span class="hljs-number">2</span>;<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 方法二 右移运算 效率更高</span><br><span class="hljs-comment"> * (left + right) >>> 1</span><br><span class="hljs-comment"> */</span><br> middle = (left + right) >>> <span class="hljs-number">1</span>;<br> System.out.println(<span class="hljs-string">"middle=>"</span> + middle);<br> <span class="hljs-keyword">if</span> (a[middle] > t) right = middle - <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">if</span> (a[middle] < t) left = middle + <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">if</span> (a[middle] == t) <span class="hljs-keyword">return</span> middle;<br> }<br> <span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>;<br> }<br>}<br></code></pre></td></tr></table></figure><hr><h1 id="3-面试题举例"><a href="#3-面试题举例" class="headerlink" title="3. 面试题举例"></a>3. 面试题举例</h1><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812175306185.png" alt="image-20220812175306185"></p>]]></content>
<categories>
<category>算法与数据结构</category>
<category>排序/查找</category>
</categories>
<tags>
<tag>二分查找</tag>
</tags>
</entry>
<entry>
<title>冒泡排序及其优化</title>
<link href="/2022/07/24/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%E5%8F%8A%E5%85%B6%E4%BC%98%E5%8C%96/"/>
<url>/2022/07/24/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F%E5%8F%8A%E5%85%B6%E4%BC%98%E5%8C%96/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="1-冒泡排序思路"><a href="#1-冒泡排序思路" class="headerlink" title="1. 冒泡排序思路"></a>1. 冒泡排序思路</h1><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812175502704.png" alt="image-20220812175502704"></p><blockquote><p><strong>ps只是相邻元素比较,别和选择排序搞混了</strong></p></blockquote><hr><h1 id="2-代码实现"><a href="#2-代码实现" class="headerlink" title="2. 代码实现"></a>2. 代码实现</h1><h2 id="2-1-基础做法"><a href="#2-1-基础做法" class="headerlink" title="2.1 基础做法"></a>2.1 基础做法</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">mao_pao_pai_xu</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] array = {<span class="hljs-number">5</span>, <span class="hljs-number">2</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>};<br> bubble(array);<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 基础做法</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">bubble</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a)</span>{<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">j</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; j < a.length - <span class="hljs-number">1</span>; j++) {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < a.length - <span class="hljs-number">1</span>; i++) {<br> <span class="hljs-keyword">if</span> (a[i] > a[i+<span class="hljs-number">1</span>]) {<br> swap(a, i, i+<span class="hljs-number">1</span>);<br> }<br> }<br> System.out.println(Arrays.toString(a));<br> }<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 置换函数</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">swap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> i, <span class="hljs-type">int</span> j)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> a[i];<br> a[i] = a[j];<br> a[j] = temp;<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//最终每一次大循环输出,需要循环a.length次</span><br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br></code></pre></td></tr></table></figure><hr><h2 id="2-2-优化一"><a href="#2-2-优化一" class="headerlink" title="2.2 优化一"></a>2.2 优化一</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">mao_pao_pai_xu</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] array = {<span class="hljs-number">5</span>, <span class="hljs-number">2</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>};<br> bubble(array);<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 优化一:</span><br><span class="hljs-comment"> * 用一个标识符 swapped 来判断这次小循环是否发生了交换</span><br><span class="hljs-comment"> * 若没有 则排序完毕 不用继续大循环</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">bubble</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a)</span>{<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">j</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; j < a.length - <span class="hljs-number">1</span>; j++) {<br> <span class="hljs-type">boolean</span> <span class="hljs-variable">swapped</span> <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;<br> <span class="hljs-comment">//这个 -j 是每次循环后 都会把最大的放到后面,就可以少比较一次</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < a.length - <span class="hljs-number">1</span> - j; i++) {<br> <span class="hljs-keyword">if</span> (a[i] > a[i+<span class="hljs-number">1</span>]) {<br> swap(a, i, i+<span class="hljs-number">1</span>);<br> swapped = <span class="hljs-literal">true</span>;<br> }<br> }<br> <span class="hljs-keyword">if</span> (!swapped) <span class="hljs-keyword">break</span>;<br> System.out.println(Arrays.toString(a));<br> }<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 置换函数</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">swap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> i, <span class="hljs-type">int</span> j)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> a[i];<br> a[i] = a[j];<br> a[j] = temp;<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//最终只需要四次大循环</span><br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br></code></pre></td></tr></table></figure><hr><h2 id="2-3-优化二"><a href="#2-3-优化二" class="headerlink" title="2.3 优化二"></a>2.3 优化二</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">mao_pao_pai_xu</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] array = {<span class="hljs-number">5</span>, <span class="hljs-number">2</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>};<br> bubble(array);<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 优化二:</span><br><span class="hljs-comment"> * 每一次循环时都记录下 最后一次 交换的位置下标,因为如果后面都没有交换,就说明后面的已经排序好了</span><br><span class="hljs-comment"> * 减少了小循环的次数,达到优化目的</span><br><span class="hljs-comment"> * 下一次则不需要到下标之后的位置上去</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">bubble2</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">n</span> <span class="hljs-operator">=</span> a.length - <span class="hljs-number">1</span>;<br> <span class="hljs-keyword">while</span> (n != <span class="hljs-number">0</span>) {<br> <span class="hljs-type">int</span> <span class="hljs-variable">last</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<span class="hljs-comment">//这里必须要等于0 最后一次循环时才会使n = 0</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < n; i++) {<br> <span class="hljs-keyword">if</span> (a[i] > a[i+<span class="hljs-number">1</span>]) {<br> swap(a, i, i+<span class="hljs-number">1</span>);<br> last = i;<br> }<br> }<br> n = last;<br> System.out.println(Arrays.toString(a));<br> }<br> }<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 置换函数</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">swap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> i, <span class="hljs-type">int</span> j)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> a[i];<br> a[i] = a[j];<br> a[j] = temp;<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//和优化一一样只要四次大循环,但是我们观察小循环的次数</span><br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//二</span><br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">7</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">7</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>大[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>大[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>大[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>大[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br><br><span class="hljs-comment">//一</span><br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">7</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">7</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>大[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>大[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>大[<span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>大[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>算法与数据结构</category>
<category>排序/查找</category>
</categories>
<tags>
<tag>排序</tag>
</tags>
</entry>
<entry>
<title>快速排序</title>
<link href="/2022/07/24/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F/"/>
<url>/2022/07/24/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><p><a href="https://www.bilibili.com/video/BV15b4y117RJ?p=30">复习视频链接</a></p><h1 id="1-单边快排"><a href="#1-单边快排" class="headerlink" title="1. 单边快排"></a>1. 单边快排</h1><h2 id="1-1-单边快排思路"><a href="#1-1-单边快排思路" class="headerlink" title="1.1 单边快排思路"></a>1.1 单边快排思路</h2><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812231212029.png" alt="image-20220812231212029"></p><hr><h2 id="1-2-代码实现"><a href="#1-2-代码实现" class="headerlink" title="1.2 代码实现"></a>1.2 代码实现</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">kuai_su_pai_xu</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] a = {<span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">2</span>, <span class="hljs-number">9</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>};<br> quick(a,<span class="hljs-number">0</span>,a.length - <span class="hljs-number">1</span>);<br> System.out.println(Arrays.toString(a));<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 递归</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">quick</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> low, <span class="hljs-type">int</span> high)</span>{<br> <span class="hljs-keyword">if</span> (low >= high) <span class="hljs-keyword">return</span>;<br> <span class="hljs-type">int</span> <span class="hljs-variable">p</span> <span class="hljs-operator">=</span> partition1(a, low, high);<br> quick(a, low, p - <span class="hljs-number">1</span>);<span class="hljs-comment">//左边分区</span><br> quick(a, p + <span class="hljs-number">1</span>, high);<span class="hljs-comment">//右边分区</span><br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 用于分区 单边循环</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> a 目标数组</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> low 左指针</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> high 右指针</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> 返回基准点元素所在的正确索引,用它确定下一轮分区的边界</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">partition1</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> low, <span class="hljs-type">int</span> high)</span>{<br> <span class="hljs-comment">//1.</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">pv</span> <span class="hljs-operator">=</span> a[high];<br> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> low;<br> <span class="hljs-type">int</span> <span class="hljs-variable">j</span> <span class="hljs-operator">=</span> low;<br> <span class="hljs-keyword">while</span> (j < high){<br> <span class="hljs-comment">//2.</span><br> <span class="hljs-keyword">if</span> (a[j] < pv){<br> swap(a, i, j);<br> <span class="hljs-comment">//3.</span><br> i++;<span class="hljs-comment">//维护小于基准点的边界</span><br> }<br> j++;<br> }<br> <span class="hljs-comment">//4.</span><br> swap(a, i ,j);<br> System.out.println(Arrays.toString(a) + <span class="hljs-string">" i ==>"</span> + i);<br> <span class="hljs-comment">//返回值表示了基准点元素所在的正确索引,用它确定下一轮分区的边界</span><br> <span class="hljs-keyword">return</span> i;<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 置换函数</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">swap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> i, <span class="hljs-type">int</span> j)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> a[i];<br> a[i] = a[j];<br> a[j] = temp;<br> }<br>}<br></code></pre></td></tr></table></figure><h3 id="简单图解"><a href="#简单图解" class="headerlink" title="简单图解"></a>简单图解</h3><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/kuaisupaixu.jpg" alt="图解单边快排"></p><h3 id="运行结果"><a href="#运行结果" class="headerlink" title="运行结果"></a>运行结果</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java">[<span class="hljs-number">3</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">9</span>, <span class="hljs-number">8</span>, <span class="hljs-number">7</span>, <span class="hljs-number">5</span>] i ==><span class="hljs-number">3</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">9</span>, <span class="hljs-number">8</span>, <span class="hljs-number">7</span>, <span class="hljs-number">5</span>] i ==><span class="hljs-number">0</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">9</span>, <span class="hljs-number">8</span>, <span class="hljs-number">7</span>, <span class="hljs-number">5</span>] i ==><span class="hljs-number">2</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">7</span>, <span class="hljs-number">9</span>] i ==><span class="hljs-number">4</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">7</span>, <span class="hljs-number">9</span>] i ==><span class="hljs-number">7</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>] i ==><span class="hljs-number">5</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br></code></pre></td></tr></table></figure><h1 id="2-双边循环快排"><a href="#2-双边循环快排" class="headerlink" title="2. 双边循环快排"></a>2. 双边循环快排</h1><h2 id="2-1-双边循环快排思路"><a href="#2-1-双边循环快排思路" class="headerlink" title="2.1 双边循环快排思路"></a>2.1 双边循环快排思路</h2><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220813004415780.png" alt="image-20220813004415780"></p><hr><h2 id="2-2-代码实现"><a href="#2-2-代码实现" class="headerlink" title="2.2 代码实现"></a>2.2 代码实现</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">kuai_su_pai_xu</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] a = {<span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">2</span>, <span class="hljs-number">9</span>, <span class="hljs-number">8</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>};<br> quick(a,<span class="hljs-number">0</span>,a.length - <span class="hljs-number">1</span>);<br> System.out.println(Arrays.toString(a));<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 递归</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">quick</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> low, <span class="hljs-type">int</span> high)</span>{<br> <span class="hljs-keyword">if</span> (low >= high) <span class="hljs-keyword">return</span>;<br> <span class="hljs-type">int</span> <span class="hljs-variable">p</span> <span class="hljs-operator">=</span> partition2(a, low, high);<br> quick(a, low, p - <span class="hljs-number">1</span>);<span class="hljs-comment">//左边分区</span><br> quick(a, p + <span class="hljs-number">1</span>, high);<span class="hljs-comment">//右边分区</span><br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> *双边</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">partition2</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> low, <span class="hljs-type">int</span> high)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">pv</span> <span class="hljs-operator">=</span> a[low];<br> <span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> low;<br> <span class="hljs-type">int</span> <span class="hljs-variable">j</span> <span class="hljs-operator">=</span> high;<br> <span class="hljs-keyword">while</span> (i<j){<br> <span class="hljs-comment">//j 从右往左找小的</span><br> <span class="hljs-comment">//1. 必须先从 j 开始 从右往左</span><br> <span class="hljs-comment">// 这是因为 如果先从 i 找 i会停止在比基准点大的元素上</span><br> <span class="hljs-comment">// 同时 j 也会停留在这上面</span><br> <span class="hljs-comment">// 最后一步就会将 这个比基准点大的元素 移动到基准点上</span><br> <span class="hljs-keyword">while</span> (i < j && a[j] > pv){<br> j--;<br> }<br> <span class="hljs-comment">//i 从左往右找大的</span><br> <span class="hljs-comment">//1. 这里加上等于号 是因为 a[i] 的初始值就是 pv 即左侧基准点;不加的则i始终不动</span><br> <span class="hljs-comment">//2. 加上 i < j 是为了防止后续的序列中已经排好序了 造成i越过了j ;上同</span><br> <span class="hljs-keyword">while</span> (i < j && a[i] <= pv){<br> i++;<br> }<br><br> swap(a, i , j);<br> }<br> swap(a, low, i);<br> System.out.println(Arrays.toString(a) + <span class="hljs-string">" i ==>"</span> + i);<br> <span class="hljs-keyword">return</span> i;<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 置换函数</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">swap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> i, <span class="hljs-type">int</span> j)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> a[i];<br> a[i] = a[j];<br> a[j] = temp;<br> }<br>}<br></code></pre></td></tr></table></figure><p><strong>运行结果:</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">[<span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">7</span>] i ==><span class="hljs-number">4</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">2</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">7</span>] i ==><span class="hljs-number">0</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">7</span>] i ==><span class="hljs-number">2</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>] i ==><span class="hljs-number">6</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br></code></pre></td></tr></table></figure><p><strong>简单图解</strong></p><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/kuaisupaixu2.jpg" alt="kuaisupaixu2"></p><hr><h2 id="2-3-双边循环的细节要点"><a href="#2-3-双边循环的细节要点" class="headerlink" title="2.3 双边循环的细节要点"></a>2.3 双边循环的细节要点</h2><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220814003355160.png" alt="image-20220814003355160"></p><hr><h1 id="3-快速排序的特点"><a href="#3-快速排序的特点" class="headerlink" title="3. 快速排序的特点"></a>3. 快速排序的特点</h1><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220814003947544.png" alt="image-20220814003947544"></p>]]></content>
<categories>
<category>算法与数据结构</category>
<category>排序/查找</category>
</categories>
<tags>
<tag>排序</tag>
</tags>
</entry>
<entry>
<title>选择排序</title>
<link href="/2022/07/24/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F/"/>
<url>/2022/07/24/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F/</url>
<content type="html"><![CDATA[<meta name="referrer" content="no-referrer" /><h1 id="1-选择排序思路及其与冒泡排序的对比"><a href="#1-选择排序思路及其与冒泡排序的对比" class="headerlink" title="1. 选择排序思路及其与冒泡排序的对比"></a>1. 选择排序思路及其与冒泡排序的对比</h1><p><img src="https://gitee.com/Gu-taicheng/image/raw/master/img/image-20220812182220424.png" alt="image-20220812182220424"></p><blockquote><p><strong>不稳定排序可能会把数值大小相同的元素位置进行交换</strong></p></blockquote><hr><h1 id="2-代码实现"><a href="#2-代码实现" class="headerlink" title="2. 代码实现"></a>2. 代码实现</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">kuai_su_pai_xu</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] array = {<span class="hljs-number">5</span>, <span class="hljs-number">2</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>};<br> selection(array);<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">selection</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a)</span>{<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < a.length; i++) {<br> <span class="hljs-comment">//i 代表每轮选择最小元素 要 交换 到 的目标索引</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span> i;<span class="hljs-comment">//代表最小元素的索引</span><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">j</span> <span class="hljs-operator">=</span> s + <span class="hljs-number">1</span>; j < a.length; j++){<br> <span class="hljs-keyword">if</span> (a[s] > a[j]){<br> s = j;<br> }<br> }<br> <span class="hljs-comment">//把最小的换到 i 的位置上</span><br> <span class="hljs-comment">//每次只需要在这里交换一次 因为在上面记录了该轮最小的索引</span><br> <span class="hljs-comment">//也算个小优化</span><br> swap(a,s,i);<br> System.out.println(Arrays.toString(a));<br> }<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 置换函数</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">swap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] a, <span class="hljs-type">int</span> i, <span class="hljs-type">int</span> j)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> a[i];<br> a[i] = a[j];<br> a[j] = temp;<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//可见每次都是把当前循环最小的元素放到对应的索引上</span><br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br>[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>]<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>算法与数据结构</category>
<category>排序/查找</category>
</categories>
<tags>
<tag>排序</tag>
</tags>
</entry>
</search>