-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathatom.xml
More file actions
446 lines (261 loc) · 498 KB
/
Copy pathatom.xml
File metadata and controls
446 lines (261 loc) · 498 KB
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
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>HQP的个人博客</title>
<link href="/atom.xml" rel="self"/>
<link href="ayjcsgm.github.io/"/>
<updated>2020-02-01T11:55:04.093Z</updated>
<id>ayjcsgm.github.io/</id>
<author>
<name>生活,生活?</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>一款免费并且好用的VPN</title>
<link href="ayjcsgm.github.io/2020/02/01/%E4%B8%80%E6%AC%BE%E5%85%8D%E8%B4%B9%E5%B9%B6%E4%B8%94%E5%A5%BD%E7%94%A8%E7%9A%84VPN/"/>
<id>ayjcsgm.github.io/2020/02/01/%E4%B8%80%E6%AC%BE%E5%85%8D%E8%B4%B9%E5%B9%B6%E4%B8%94%E5%A5%BD%E7%94%A8%E7%9A%84VPN/</id>
<published>2020-02-01T11:43:53.000Z</published>
<updated>2020-02-01T11:55:04.093Z</updated>
<content type="html"><![CDATA[<p>安卓,苹果,Mac,Windows 好用的加速器 刷INS、访推特,完美支持高清1080P视频,无任何流量限制,真正免费的加速器 <a href="https://my.laihuluwa.com/aff/TpWz" target="_blank" rel="noopener">https://my.laihuluwa.com/aff/TpWz</a></p><a id="more"></a><p>记得填写下邀请码<strong>TpWz</strong>哦。</p><p>下载安装即可使用,简单无脑。</p><p><img src="https://img-blog.csdnimg.cn/20200201194825940.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p><img src="https://img-blog.csdnimg.cn/20200201195154567.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p><img src="https://img-blog.csdnimg.cn/2020020119541343.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>]]></content>
<summary type="html">
<p>安卓,苹果,Mac,Windows 好用的加速器 刷INS、访推特,完美支持高清1080P视频,无任何流量限制,真正免费的加速器 <a href="https://my.laihuluwa.com/aff/TpWz" target="_blank" rel="noopener">https://my.laihuluwa.com/aff/TpWz</a></p>
</summary>
</entry>
<entry>
<title>破解IDEA最新版教程</title>
<link href="ayjcsgm.github.io/2020/01/10/%E7%A0%B4%E8%A7%A3IDEA%E6%9C%80%E6%96%B0%E7%89%88%E6%95%99%E7%A8%8B/"/>
<id>ayjcsgm.github.io/2020/01/10/%E7%A0%B4%E8%A7%A3IDEA%E6%9C%80%E6%96%B0%E7%89%88%E6%95%99%E7%A8%8B/</id>
<published>2020-01-10T03:03:58.000Z</published>
<updated>2020-01-10T03:59:54.236Z</updated>
<content type="html"><![CDATA[<p>1、把这个jar文件放到你idea中的bin目录下(在我<a href="https://github.com/AYJCSGM/-" target="_blank" rel="noopener">github</a>上可下载)<br><img src="https://img-blog.csdnimg.cn/2020011010441384.png" alt="在这里插入图片描述"><br>2、然后打开idea,点击红线处</p><a id="more"></a><p><img src="https://img-blog.csdnimg.cn/20200110115039851.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p>3、下一步 ,添加这个jar包的绝对路径<br><img src="https://img-blog.csdnimg.cn/20200110115301978.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p><strong>其他的不用改<br>重点!:到这一步 一定要重启一下idea 然后再打开执行下一步</strong></p><p>4、然后打开激活框<br><img src="https://img-blog.csdnimg.cn/20200110115514562.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p>5、等待出现Server address,点击Test Connection后会出现成功的提示,最后点击Discover Server<br><img src="https://img-blog.csdnimg.cn/20200110115607521.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>6、点击中间那个Activation code,如果是已经激活过,点击Remove License<br><img src="https://img-blog.csdnimg.cn/20200110105006122.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>7、最后一步填写激活码点击Activate</p><blockquote><p>A82DEE284F-eyJsaWNlbnNlSWQiOiJBODJERUUyODRGIiwibGljZW5zZWVOYW1lIjoiaHR0cHM6Ly96aGlsZS5pbyIsImFzc2lnbmVlTmFtZSI6IiIsImFzc2lnbmVlRW1haWwiOiIiLCJsaWNlbnNlUmVzdHJpY3Rpb24iOiJVbmxpbWl0ZWQgbGljZW5zZSB0aWxsIGVuZCBvZiB0aGUgY2VudHVyeS4iLCJjaGVja0NvbmN1cnJlbnRVc2UiOmZhbHNlLCJwcm9kdWN0cyI6W3siY29kZSI6IklJIiwicGFpZFVwVG8iOiIyMDg5LTA3LTA3In0seyJjb2RlIjoiUlMwIiwicGFpZFVwVG8iOiIyMDg5LTA3LTA3In0seyJjb2RlIjoiV1MiLCJwYWlkVXBUbyI6IjIwODktMDctMDcifSx7ImNvZGUiOiJSRCIsInBhaWRVcFRvIjoiMjA4OS0wNy0wNyJ9LHsiY29kZSI6IlJDIiwicGFpZFVwVG8iOiIyMDg5LTA3LTA3In0seyJjb2RlIjoiREMiLCJwYWlkVXBUbyI6IjIwODktMDctMDcifSx7ImNvZGUiOiJEQiIsInBhaWRVcFRvIjoiMjA4OS0wNy0wNyJ9LHsiY29kZSI6IlJNIiwicGFpZFVwVG8iOiIyMDg5LTA3LTA3In0seyJjb2RlIjoiRE0iLCJwYWlkVXBUbyI6IjIwODktMDctMDcifSx7ImNvZGUiOiJBQyIsInBhaWRVcFRvIjoiMjA4OS0wNy0wNyJ9LHsiY29kZSI6IkRQTiIsInBhaWRVcFRvIjoiMjA4OS0wNy0wNyJ9LHsiY29kZSI6IkdPIiwicGFpZFVwVG8iOiIyMDg5LTA3LTA3In0seyJjb2RlIjoiUFMiLCJwYWlkVXBUbyI6IjIwODktMDctMDcifSx7ImNvZGUiOiJDTCIsInBhaWRVcFRvIjoiMjA4OS0wNy0wNyJ9LHsiY29kZSI6IlBDIiwicGFpZFVwVG8iOiIyMDg5LTA3LTA3In0seyJjb2RlIjoiUlNVIiwicGFpZFVwVG8iOiIyMDg5LTA3LTA3In1dLCJoYXNoIjoiODkwNzA3MC8wIiwiZ3JhY2VQZXJpb2REYXlzIjowLCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-5epo90Xs7KIIBb8ckoxnB/AZQ8Ev7rFrNqwFhBAsQYsQyhvqf1FcYdmlecFWJBHSWZU9b41kvsN4bwAHT5PiznOTmfvGv1MuOzMO0VOXZlc+edepemgpt+t3GUHvfGtzWFYeKeyCk+CLA9BqUzHRTgl2uBoIMNqh5izlDmejIwUHLl39QOyzHiTYNehnVN7GW5+QUeimTr/koVUgK8xofu59Tv8rcdiwIXwTo71LcU2z2P+T3R81fwKkt34evy7kRch4NIQUQUno//Pl3V0rInm3B2oFq9YBygPUdBUbdH/KHROyohZRD8SaZJO6kUT0BNvtDPKF4mCT1saWM38jkw==-MIIElTCCAn2gAwIBAgIBCTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTE4MTEwMTEyMjk0NloXDTIwMTEwMjEyMjk0NlowaDELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBU51c2xlMQ8wDQYDVQQHDAZQcmFndWUxGTAXBgNVBAoMEEpldEJyYWlucyBzLnIuby4xHTAbBgNVBAMMFHByb2QzeS1mcm9tLTIwMTgxMTAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5ndaik1GD0nyTdqkZgURQZGW+RGxCdBITPXIwpjhhaD0SXGa4XSZBEBoiPdY6XV6pOfUJeyfi9dXsY4MmT0D+sKoST3rSw96xaf9FXPvOjn4prMTdj3Ji3CyQrGWeQU2nzYqFrp1QYNLAbaViHRKuJrYHI6GCvqCbJe0LQ8qqUiVMA9wG/PQwScpNmTF9Kp2Iej+Z5OUxF33zzm+vg/nYV31HLF7fJUAplI/1nM+ZG8K+AXWgYKChtknl3sW9PCQa3a3imPL9GVToUNxc0wcuTil8mqveWcSQCHYxsIaUajWLpFzoO2AhK4mfYBSStAqEjoXRTuj17mo8Q6M2SHOcwIDAQABo4GZMIGWMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGEpG9oZGcfLMGNBkY7SgHiMGgTcMEgGA1UdIwRBMD+AFKOetkhnQhI2Qb1t4Lm0oFKLl/GzoRykGjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBggkA0myxg7KDeeEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQBonMu8oa3vmNAa4RQP8gPGlX3SQaA3WCRUAj6Zrlk8AesKV1YSkh5D2l+yUk6njysgzfr1bIR5xF8eup5xXc4/G7NtVYRSMvrd6rfQcHOyK5UFJLm+8utmyMIDrZOzLQuTsT8NxFpbCVCfV5wNRu4rChrCuArYVGaKbmp9ymkw1PU6+HoO5i2wU3ikTmRv8IRjrlSStyNzXpnPTwt7bja19ousk56r40SmlmC04GdDHErr0ei2UbjUua5kw71Qn9g02tL9fERI2sSRjQrvPbn9INwRWl5+k05mlKekbtbu2ev2woJFZK4WEXAd/GaAdeZZdumv8T2idDFL7cAirJwcrbfpawPeXr52oKTPnXfi0l5+g9Gnt/wfiXCrPElX6ycTR6iL3GC2VR4jTz6YatT4Ntz59/THOT7NJQhr6AyLkhhJCdkzE2cob/KouVp4ivV7Q3Fc6HX7eepHAAF/DpxwgOrg9smX6coXLgfp0b1RU2u/tUNID04rpNxTMueTtrT8WSskqvaJd3RH8r7cnRj6Y2hltkja82HlpDURDxDTRvv+krbwMr26SB/40BjpMUrDRCeKuiBahC0DCoU/4+ze1l94wVUhdkCfL0GpJrMSCDEK+XEurU18Hb7WT+ThXbkdl6VpFdHsRvqAnhR2g4b+Qzgidmuky5NUZVfEaZqV/g==</p></blockquote><p>8、点击Help里面的about查看期限,大功告成<br><img src="https://img-blog.csdnimg.cn/20200110105428721.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>]]></content>
<summary type="html">
<p>1、把这个jar文件放到你idea中的bin目录下(在我<a href="https://github.com/AYJCSGM/-" target="_blank" rel="noopener">github</a>上可下载)<br><img src="https://img-blog.csdnimg.cn/2020011010441384.png" alt="在这里插入图片描述"><br>2、然后打开idea,点击红线处</p>
</summary>
</entry>
<entry>
<title>分享一些好用的网站</title>
<link href="ayjcsgm.github.io/2019/12/25/%E5%88%86%E4%BA%AB%E4%B8%80%E4%BA%9B%E5%A5%BD%E7%94%A8%E7%9A%84%E7%BD%91%E7%AB%99/"/>
<id>ayjcsgm.github.io/2019/12/25/%E5%88%86%E4%BA%AB%E4%B8%80%E4%BA%9B%E5%A5%BD%E7%94%A8%E7%9A%84%E7%BD%91%E7%AB%99/</id>
<published>2019-12-25T06:41:47.000Z</published>
<updated>2019-12-25T06:43:15.984Z</updated>
<content type="html"><![CDATA[<p><a id="more"></a></p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>记录了我之前常用的一些网站,现在过去半年,收藏夹也多了一些网站,特地记录下来,希望能对你有所帮助。</p><p>由于这些网站都比较杂,我也懒得分类了。</p><a id="more"></a><h2 id="太长不看版"><a href="#太长不看版" class="headerlink" title="太长不看版"></a>太长不看版</h2><ol><li>临时邮箱 <a href="https://temp-mail.org/zh" target="_blank" rel="noopener">https://temp-mail.org/zh</a></li><li>域名注册商比较 <a href="https://www.nazhumi.com/domain/.com" target="_blank" rel="noopener">https://www.nazhumi.com/domain/.com</a></li><li>Tables Generator <a href="http://www.tablesgenerator.com/html_tables#" target="_blank" rel="noopener">http://www.tablesgenerator.com/html_tables#</a></li><li>图片放大 <a href="http://waifu2x.udp.jp/index.zh-CN.html" target="_blank" rel="noopener">http://waifu2x.udp.jp/index.zh-CN.html</a></li><li>物联网搜索引擎 <a href="https://www.shodan.io/" target="_blank" rel="noopener">https://www.shodan.io</a></li><li>AST Explorer <a href="https://astexplorer.net/" target="_blank" rel="noopener">https://astexplorer.net</a></li><li>Linux 命令搜索 <a href="http://git.io/linux" target="_blank" rel="noopener">http://git.io/linux</a></li><li>Vue render <a href="https://vuejs-tips.github.io/compiler/#inline-template" target="_blank" rel="noopener">https://vuejs-tips.github.io/compiler/#inline-template</a></li><li>图片转矢量图 <a href="https://www.vectorizer.io/" target="_blank" rel="noopener">https://www.vectorizer.io</a></li><li>在线免费作图 <a href="https://www.processon.com/" target="_blank" rel="noopener">https://www.processon.com</a></li><li>拾色器 <a href="https://color.hailpixel.com/" target="_blank" rel="noopener">https://color.hailpixel.com</a></li><li>在线幻灯片 <a href="https://slides.com/" target="_blank" rel="noopener">https://slides.com</a></li></ol><h2 id="1、TempMail(临时邮箱)"><a href="#1、TempMail(临时邮箱)" class="headerlink" title="1、TempMail(临时邮箱)"></a>1、TempMail(临时邮箱)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105152931.png" alt></p><p>有时候注册某些论坛必须提供邮箱,但你又不想使用你常用的邮箱时,临时邮箱应运而生,在注册这些不重要的服务时使用临时邮箱作为替身是个很好的选择。临时邮箱具有匿名性,能有效减少垃圾邮件;同时具有时效性,邮箱在短时间内会自毁。</p><p>建议:</p><ul><li>使用完后手动删除邮件</li></ul><blockquote><p>网站链接:<a href="https://temp-mail.org/zh" target="_blank" rel="noopener">https://temp-mail.org/zh</a></p></blockquote><h2 id="2、哪煮米(域名注册商比价)"><a href="#2、哪煮米(域名注册商比价)" class="headerlink" title="2、哪煮米(域名注册商比价)"></a>2、哪煮米(域名注册商比价)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105153609.png" alt></p><p>当你想购买一个域名而不知道选哪个注册商时,这个网站可以帮助你对一些价格进行评分。</p><p>该站目前已经收录了58家域名注册商,可注册域名后缀将近有2000个,相关信息还在不断收集中。</p><blockquote><p>网站链接:<a href="https://www.nazhumi.com" target="_blank" rel="noopener">https://www.nazhumi.com</a></p></blockquote><h2 id="3、Table-Generator(表格生成)"><a href="#3、Table-Generator(表格生成)" class="headerlink" title="3、Table Generator(表格生成)"></a>3、Table Generator(表格生成)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105154026.png" alt></p><p>还记得我当时做一个项目,需要展示十几个不规则的报表,当时弄了一下午愣是没画出一个,终于让我发现这个网站,简直就是神器,只要像在 Excel 一样拽拉合并就可以直接生成 HTML 代码,顿时提高了百倍工作效率。</p><p>另外除了 HTML,它还支持:LaTeX、Text、Markdown等</p><blockquote><p>网站链接:<a href="http://www.tablesgenerator.com/html_tables" target="_blank" rel="noopener">http://www.tablesgenerator.com/html_tables</a></p></blockquote><h2 id="4、waifu2x(图片无损放大降噪)"><a href="#4、waifu2x(图片无损放大降噪)" class="headerlink" title="4、waifu2x(图片无损放大降噪)"></a>4、waifu2x(图片无损放大降噪)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105154646.png" alt></p><p>这个网站厉害了,可以将图片无损放大数倍,似乎是通过卷积神经网络等高科技(咱也不懂),反正好用就是了,如果以后刚好有这个需求,不妨用起来,亲测好用!</p><p>下面放一个对比图</p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/下载.jpeg" alt></p><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/下载_waifu2x_art_noise1_scale_tta_1.png" alt></p><h2 id="5、shodan(物联网搜索引擎)"><a href="#5、shodan(物联网搜索引擎)" class="headerlink" title="5、shodan(物联网搜索引擎)"></a>5、shodan(物联网搜索引擎)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105160718.png" alt></p><p>首先,Shodan 是一个搜索引擎,但它与 Google 这种搜索网址的搜索引擎不同,Shodan 是用来搜索网络空间中在线设备的,你可以通过 Shodan 搜索指定的设备,或者搜索特定类型的设备。</p><p>好了,感兴趣的同学可以自行查阅资料,这里不好叙述过多。。</p><p>不过请不要做坏事哦。</p><blockquote><p>网站链接:<a href="https://www.shodan.io" target="_blank" rel="noopener">https://www.shodan.io</a></p></blockquote><h2 id="6、AST-Explorer(解析器生成AST)"><a href="#6、AST-Explorer(解析器生成AST)" class="headerlink" title="6、AST Explorer(解析器生成AST)"></a>6、AST Explorer(解析器生成AST)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105162317.png" alt></p><p>如果你在学习 AST 抽象语法树,那么这个网站很对你很有帮助,它可以展示将你输入的代码经过 parser 处理为AST 的过程。</p><blockquote><p>网站链接:<a href="https://astexplorer.net" target="_blank" rel="noopener">https://astexplorer.net</a></p></blockquote><h2 id="7、Linux-命令搜索"><a href="#7、Linux-命令搜索" class="headerlink" title="7、Linux 命令搜索"></a>7、Linux 命令搜索</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105163308.png" alt></p><p>有时候需要用到 Linux 命令但一时想不出去的时候,可以在上面搜一下,支持命令、描述搜索。</p><p>目前已收集 568 个命令,无聊时也可以上去看看,增加一下印象。</p><blockquote><p>网站链接:<a href="http://git.io/linux" target="_blank" rel="noopener">http://git.io/linux</a></p></blockquote><h2 id="8、Vue-render"><a href="#8、Vue-render" class="headerlink" title="8、Vue render"></a>8、Vue render</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105164607.png" alt></p><p>一个将 Vue HTML 编译为 JavaScript 的工具(有助于 Debug 和理解 Vue),深入学习 Vue 是如何把 HTML 编译成 JavaScript 有助于让你了解原理。</p><p>另外网站已经内置很多案例,可以快速查看一些内置指令在 render 函数中是如何编写的。</p><blockquote><p>网站链接:<a href="https://vuejs-tips.github.io/compiler/#inline-template" target="_blank" rel="noopener">https://vuejs-tips.github.io/compiler/#inline-template</a></p></blockquote><h2 id="9、Vectorizer(图片转矢量图)"><a href="#9、Vectorizer(图片转矢量图)" class="headerlink" title="9、Vectorizer(图片转矢量图)"></a>9、Vectorizer(图片转矢量图)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105165152.png" alt></p><p>该网站可以将位图转成矢量图(SVG),不仅支持PNG、BMP、JPEG格式,而且完全免费,亲测效果不错。</p><blockquote><p>网站链接:<a href="https://www.vectorizer.io" target="_blank" rel="noopener">https://www.vectorizer.io</a></p></blockquote><h2 id="10、processOn(免费在线作图)"><a href="#10、processOn(免费在线作图)" class="headerlink" title="10、processOn(免费在线作图)"></a>10、processOn(免费在线作图)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105165623.png" alt></p><p>ProcessOn 是一个在线协作绘图平台,为用户提供最强大、易用的作图工具!支持在线创作流程图、思维导图、组织结构图、网络拓扑图、BPMN、UML图、UI界面原型 …</p><p>之前做技术分享时用过一次,觉得挺不错的,免费版不支持直接导出高清图,解决方法就是先导入 SVG 再用别的工具转成 png。</p><p>但这个网站免费版只能在线存储9张图(包括回收站),目前的做法是先导入备份,有需要再导入。</p><p>这里推荐另外一个类似的网站 <a href="https://www.draw.io/" target="_blank" rel="noopener">draw.io</a>,不同之处在于:</p><ul><li>英文、可在线存储(利用 Google)、完全免费、不限数量</li></ul><p>所以如果是常用的话,建议还是使用 draw.io</p><blockquote><p>网站链接:<a href="https://processon.com" target="_blank" rel="noopener">https://processon.com</a></p></blockquote><h2 id="11、Colordot(拾色器)"><a href="#11、Colordot(拾色器)" class="headerlink" title="11、Colordot(拾色器)"></a>11、Colordot(拾色器)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105171134.png" alt></p><p>这个网站的特点就是通过摇动滑鼠轻松找出配色灵感,对于我这种不是很懂配色的人,以后就可以在上面挑几个比较顺眼的啦。</p><blockquote><p>网站链接:<a href="https://color.hailpixel.com" target="_blank" rel="noopener">https://color.hailpixel.com</a></p></blockquote><h2 id="12、Sliders(在线幻灯片)"><a href="#12、Sliders(在线幻灯片)" class="headerlink" title="12、Sliders(在线幻灯片)"></a>12、Sliders(在线幻灯片)</h2><p><img src="https://gd4ark-1258805822.cos.ap-guangzhou.myqcloud.com/images/20191105171436.png" alt></p><p>可以在浏览器中创建在线幻灯片,轻松做出简洁实用的幻灯片,对于开发者需要制作技术分享文稿时是个不错的选择。</p><p>但 Sliders 的一些高级功能是要收费的,仅提供一些基本的使用,如果愿意折腾不妨使用这个 <a href="https://github.com/hakimel/reveal.js" target="_blank" rel="noopener">Reveal.js</a>。</p><blockquote><p>网站链接:<a href="https://slides.com" target="_blank" rel="noopener">https://slides.com</a></p></blockquote>]]></content>
<summary type="html">
<p><a id="more"></a></p>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>记录了我之前常用的一些网站,现在过去半年,收藏夹也多了一些网站,特地记录下来,希望能对你有所帮助。</p>
<p>由于这些网站都比较杂,我也懒得分类了。</p>
</summary>
</entry>
<entry>
<title>面向对象七种设计原则</title>
<link href="ayjcsgm.github.io/2019/12/19/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E4%B8%83%E7%A7%8D%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/"/>
<id>ayjcsgm.github.io/2019/12/19/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E4%B8%83%E7%A7%8D%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/</id>
<published>2019-12-19T08:11:15.000Z</published>
<updated>2019-12-19T08:20:46.092Z</updated>
<content type="html"><![CDATA[<h3 id="1、面向对象设计原则概述"><a href="#1、面向对象设计原则概述" class="headerlink" title="1、面向对象设计原则概述"></a>1、面向对象设计原则概述</h3><p> 对于面向对象软件系统的设计而言,在支持可维护性的同时,提高系统的可复用性是一个至关重要的问题,<strong>如何同时提高一个软件系统的可维护性和可复用性是面向对象设计需要解决的核心问题之一</strong>。在面向对象设计中,可维护性的复用是以设计原则为基础的。每一个原则都蕴含一些面向对象设计的思想,可以从不同的角度提升一个软件结构的设计水平。</p><a id="more"></a><p><strong> 面向对象设计原则为支持可维护性复用而诞生,这些原则蕴含在很多设计模式中,它们是从许多设计方案中总结出的指导性原则</strong>。面向对象设计原则也是我们用于评价一个设计模式的使用效果的重要指标之一,在设计模式的学习中,大家经常会看到诸如“<span style="font-family:'Times New Roman';">XXX</span>模式符合<span style="font-family:'Times New Roman';">XXX</span>原则”、“<span style="font-family:'Times New Roman';">XXX</span>模式违反了<span style="font-family:'Times New Roman';">XXX</span>原则”这样的语句。</p><p><span style="font-size:16px;"><span style="font-size:18px;"> 最常见的7种面向对象设计原则如下表所示:</span></span></p><p align="center"><strong>表<span style="font-family:'Times New Roman';">1 7</span>种常用的面向对象设计原则</strong></p><div class="table-box"><table style="width:648px;" border="1" cellspacing="0" cellpadding="0"><tbody><tr><td style="background:rgb(243,243,243);" valign="top"><p align="center"><strong><span style="font-size:16px;">设计原则名称</span></strong></p></td><td style="background:rgb(243,243,243);" valign="top"><p align="center"><span style="font-size:16px;"><strong>定<span style="font-family:'Times New Roman';"> </span>义</strong></span></p></td><td style="background:rgb(243,243,243);" valign="top"><p align="center"><strong><span style="font-size:16px;">使用频率</span></strong></p></td></tr><tr><td valign="top"><p><span style="font-size:16px;">单一职责原则</span></p><p><span style="font-family:'Times New Roman';font-size:16px;">(Single Responsibility Principle, SRP)</span></p></td><td valign="top"><p><span style="font-size:16px;">一个类只负责一个功能领域中的相应职责</span></p></td><td valign="top"><p><span style="font-size:16px;">★★★★☆</span></p></td></tr><tr><td valign="top"><p><span style="font-size:16px;">开闭原则</span></p><p><span style="font-family:'Times New Roman';font-size:16px;">(Open-Closed Principle, OCP)</span></p></td><td valign="top"><p><span style="font-size:16px;">软件实体应对扩展开放,而对修改关闭</span></p></td><td valign="top"><p><span style="font-size:16px;">★★★★★</span></p></td></tr><tr><td valign="top"><p><span style="font-size:16px;">里氏代换原则</span></p><p><span style="font-family:'Times New Roman';font-size:16px;">(Liskov Substitution Principle, LSP)</span></p></td><td valign="top"><p><span style="font-size:16px;">所有引用基类对象的地方能够透明地使用其子类的对象</span></p><p><span style="font-family:'Times New Roman';font-size:16px;"> </span></p></td><td valign="top"><p><span style="font-size:16px;">★★★★★</span></p></td></tr><tr><td valign="top"><p><span style="font-size:16px;">依赖倒转原则</span></p><p><span style="font-family:'Times New Roman';font-size:16px;">(Dependence Inversion Principle, DIP)</span></p></td><td valign="top"><p align="left"><span style="font-size:16px;">抽象不应该依赖于细节,细节应该依赖于抽象</span></p></td><td valign="top"><p><span style="font-size:16px;">★★★★★</span></p></td></tr><tr><td valign="top"><p><span style="font-size:16px;">接口隔离原则</span></p><p><span style="font-family:'Times New Roman';font-size:16px;">(Interface Segregation Principle, ISP)</span></p></td><td valign="top"><p><span style="font-size:16px;">使用多个专门的接口,而不使用单一的总接口</span></p></td><td valign="top"><p><span style="font-size:16px;">★★☆☆☆</span></p></td></tr><tr><td valign="top"><p><span style="font-size:16px;">合成复用原则</span></p><p><span style="font-family:'Times New Roman';font-size:16px;">(Composite Reuse Principle, CRP)</span></p></td><td valign="top"><p align="left"><span style="font-size:16px;">尽量使用对象组合,而不是继承来达到复用的目的</span></p><p><span style="font-family:'Times New Roman';font-size:16px;"> </span></p></td><td valign="top"><p><span style="font-size:16px;">★★★★☆</span></p></td></tr><tr><td valign="top"><p><span style="font-size:16px;">迪米特法则</span></p><p><span style="font-family:'Times New Roman';font-size:16px;">(Law of Demeter, LoD)</span></p></td><td valign="top"><p><span style="font-size:16px;">一个软件实体应当尽可能少地与其他实体发生相互作用</span></p></td><td valign="top"><p><span style="font-size:16px;">★★★☆☆</span></p></td></tr></tbody></table></div><p align="center"></p><h3 id="2、单一职责原则"><a href="#2、单一职责原则" class="headerlink" title="2、单一职责原则"></a>2、单一职责原则</h3><div class="htmledit_views" id="content_views"><p align="left"><span style="font-size:16px;"> 单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小。单一职责原则定义如下:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(231,239,249);"><tbody><tr><td valign="top"><p><span style="font-size:16px;"><strong>单一职责原则<span style="font-family:'Times New Roman';">(Single Responsibility Principle, SRP)</span>:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。</strong></span></p></td></tr></tbody></table></div><p align="left"><span style="font-size:16px;"> 单一职责原则告诉我们:一个类不能太“累”!在软件系统中,一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。</span></p><p align="left"><span style="font-size:16px;"> <span style="color:#ff0000;">单一职责原则是实现<strong>高内聚、低耦合</strong>的指导方针,它是最简单但又最难运用的原则</span>,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。</span></p><p align="left"><span style="font-size:16px;"> 下面通过一个简单实例来进一步分析单一职责原则:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(242,242,242);"><tbody><tr><td valign="top"><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';">Sunny</span>软件公司开发人员针对某<span style="font-family:'Times New Roman';">CRM</span>(<span style="font-family:'Times New Roman';">Customer Relationship Management</span>,客户关系管理)系统中客户信息图形统计模块提出了如图<span style="font-family:'Times New Roman';">1</span>所示初始设计方案:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/05/1336147233_3529.jpg" width="294" height="163" style="width:234px;"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">1 </span>初始设计方案结构图</span></strong></p><p><span style="font-size:16px;">在图<span style="font-family:'Times New Roman';">1</span>中,<span style="font-family:'Times New Roman';">CustomerDataChart</span>类中的方法说明如下:<span style="font-family:'Times New Roman';">getConnection()</span>方法用于连接数据库,<span style="font-family:'Times New Roman';">findCustomers()</span>用于查询所有的客户信息,<span style="font-family:'Times New Roman';">createChart()</span>用于创建图表,<span style="font-family:'Times New Roman';">displayChart()</span>用于显示图表。</span></p><p><span style="font-size:16px;">现使用单一职责原则对其进行重构。</span></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 在图<span style="font-family:'Times New Roman';">1</span>中,<span style="font-family:'Times New Roman';">CustomerDataChart</span>类承担了太多的职责,既包含与数据库相关的方法,又包含与图表生成和显示相关的方法。如果在其他类中也需要连接数据库或者使用<span style="font-family:'Times New Roman';">findCustomers()</span>方法查询客户信息,则难以实现代码的重用。无论是修改数据库连接方式还是修改图表显示方式都需要修改该类,它不止一个引起它变化的原因,违背了单一职责原则。因此需要对该类进行拆分,使其满足单一职责原则,类<span style="font-family:'Times New Roman';">CustomerDataChart</span>可拆分为如下三个类:</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (1) DBUtil</span>:负责连接数据库,包含数据库连接方法<span style="font-family:'Times New Roman';">getConnection()</span>;</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (2) CustomerDAO</span>:负责操作数据库中的<span style="font-family:'Times New Roman';">Customer</span>表,包含对<span style="font-family:'Times New Roman';">Customer</span>表的增删改查等方法,如<span style="font-family:'Times New Roman';">findCustomers()</span>;</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (3) CustomerDataChart</span>:负责图表的生成和显示,包含方法<span style="font-family:'Times New Roman';">createChart()</span>和<span style="font-family:'Times New Roman';">displayChart()</span>。</span></p><p><span style="font-size:16px;"> 使用单一职责原则重构后的结构如图<span style="font-family:'Times New Roman';">2</span>所示:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/05/1336147240_4896.jpg" width="592" height="275" style="width:564px;"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">2 </span>重构后的结构图</span></strong></p><h3 id="3、开闭原则"><a href="#3、开闭原则" class="headerlink" title="3、开闭原则"></a>3、开闭原则</h3><p><span style="font-size:16px;"> 开闭原则是面向对象的可复用设计的第一块基石,它是最重要的面向对象设计原则。开闭原则由<span style="font-family:'Times New Roman';color:#ff0000;"><strong>Bertrand Meyer</strong></span>于<span style="font-family:'Times New Roman';">1988</span>年提出,其定义如下:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(231,239,249);"><tbody><tr><td valign="top"><p align="left"><span style="font-size:16px;"><strong>开闭原则<span style="font-family:'Times New Roman';">(Open-Closed Principle, OCP)</span>:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。</strong></span></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 在开闭原则的定义中,<strong>软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类</strong>。</span></p><p><span style="font-size:16px;"> 任何软件都需要面临一个很重要的问题,即它们的需求会随时间的推移而发生变化。当软件系统需要面对新的需求时,我们应该尽量保证系统的设计框架是稳定的。如果一个软件设计符合开闭原则,那么可以非常方便地对系统进行扩展,而且在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。随着软件规模越来越大,软件寿命越来越长,软件维护成本越来越高,设计满足开闭原则的软件系统也变得越来越重要。</span></p><p><span style="font-size:16px;"> 为了满足开闭原则,需要对系统进行抽象化设计,<strong>抽象化是开闭原则的关键</strong>。在<span style="font-family:'Times New Roman';">Java</span>、<span style="font-family:'Times New Roman';">C#</span>等编程语言中,可以为系统定义一个相对稳定的抽象层,而将不同的实现行为移至具体的实现层中完成。在很多面向对象编程语言中都提供了接口、抽象类等机制,可以通过它们定义系统的抽象层,再通过具体类来进行扩展。如果需要修改系统的行为,无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(247,247,247);"><tbody><tr><td valign="top"><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> Sunny</span>软件公司开发的CRM系统可以显示各种类型的图表,如饼状图和柱状图等,为了支持多种图表显示方式,原始设计方案如图1所示:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/05/1336201566_1496.jpg" width="429" height="190"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">1 </span>初始设计方案结构图</span></strong></p><p><span style="font-size:16px;"> 在ChartDisplay类的display()</span>方法中存在如下代码片段:<figure class="highlight plain"><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><span class="line">......</span><br><span class="line">if (type.equals("pie")) {</span><br><span class="line">PieChart chart = new PieChart();</span><br><span class="line">chart.display();</span><br><span class="line">}</span><br><span class="line">else if (type.equals("bar")) {</span><br><span class="line">BarChart chart = new BarChart();</span><br><span class="line">chart.display();</span><br><span class="line">}</span><br><span class="line">......</span><br></pre></td></tr></table></figure></p><p><span style="font-size:16px;"> 在该代码中,如果需要增加一个新的图表类,如折线图<span style="font-family:'Times New Roman';">LineChart</span>,则需要修改<span style="font-family:'Times New Roman';">ChartDisplay</span>类的<span style="font-family:'Times New Roman';">display()</span>方法的源代码,增加新的判断逻辑,违反了开闭原则。</span></p><p><span style="font-size:16px;"> 现对该系统进行重构,使之符合开闭原则。</span></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 在本实例中,由于在<span style="font-family:'Times New Roman';">ChartDisplay</span>类的<span style="font-family:'Times New Roman';">display()</span>方法中针对每一个图表类编程,因此增加新的图表类不得不修改源代码。可以通过抽象化的方式对系统进行重构,使之增加新的图表类时无须修改源代码,满足开闭原则。具体做法如下:</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (1) </span>增加一个抽象图表类<span style="font-family:'Times New Roman';">AbstractChart</span>,将各种具体图表类作为其子类;</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (2) ChartDisplay</span>类针对抽象图表类进行编程,由客户端来决定使用哪种具体图表。</span></p><p><span style="font-size:16px;"> 重构后结构如图<span style="font-family:'Times New Roman';">2</span>所示:</span></p><p align="center"><img alt src="https://img-my.csdn.net/uploads/201205/05/1336201573_6059.jpg" width="499" height="238" style="width:566px;"></p><p align="center"><span style="font-size:16px;"><strong>图2 重构后的结构图</strong></span></p><span style="font-size:16px;"></span><p><span style="font-size:16px;"> 在图<span style="font-family:'Times New Roman';">2</span>中,我们引入了<span style="color:#ff0000;">抽象图表类<span style="font-family:'Times New Roman';">AbstractChart</span></span>,且<span style="font-family:'Times New Roman';">ChartDisplay</span>针对抽象图表类进行编程,并通过<span style="font-family:'Times New Roman';">setChart()</span>方法由客户端来设置实例化的具体图表对象,在<span style="font-family:'Times New Roman';">ChartDisplay</span>的<span style="font-family:'Times New Roman';">display()</span>方法中调用<span style="font-family:'Times New Roman';">chart</span>对象的<span style="font-family:'Times New Roman';">display()</span>方法显示图表。如果需要增加一种新的图表,如折线图<span style="font-family:'Times New Roman';">LineChart</span>,只需要将<span style="font-family:'Times New Roman';">LineChart</span>也作为<span style="font-family:'Times New Roman';">AbstractChart</span>的子类,在客户端向<span style="font-family:'Times New Roman';">ChartDisplay</span>中注入一个<span style="font-family:'Times New Roman';">LineChart</span>对象即可,无须修改现有类库的源代码。</span><span style="font-size:16px;"> </span></p><p><span style="font-size:16px;"> 注意:因为<span style="font-family:'Times New Roman';">xml</span>和<span style="font-family:'Times New Roman';">properties</span>等格式的配置文件是纯文本文件,可以直接通过<span style="font-family:'Times New Roman';">VI</span>编辑器或记事本进行编辑,且无须编译,因此在软件开发中,一般不把对配置文件的修改认为是对系统源代码的修改。如果一个系统在扩展时只涉及到修改配置文件,而原有的<span style="font-family:'Times New Roman';">Java</span>代码或<span style="font-family:'Times New Roman';">C#</span>代码没有做任何修改,该系统即可认为是一个符合开闭原则的系统。</span></p><h3 id="4、里氏代换原则"><a href="#4、里氏代换原则" class="headerlink" title="4、里氏代换原则"></a>4、里氏代换原则</h3> <div class="htmledit_views" id="content_views"> <p><span style="font-size:16px;"> 里氏代换原则由<span style="font-family:'Times New Roman';">2008</span>年图灵奖得主、美国第一位计算机科学女博士<span style="font-family:'Times New Roman';color:#ff0000;"><strong>Barbara Liskov</strong></span>教授和卡内基·梅隆大学<span style="font-family:'Times New Roman';">Jeannette Wing</span>教授于<span style="font-family:'Times New Roman';">1994</span>年提出。其严格表述如下:如果对每一个类型为<span style="font-family:'Times New Roman';">S</span>的对象<span style="font-family:'Times New Roman';">o1</span>,都有类型为<span style="font-family:'Times New Roman';">T</span>的对象<span style="font-family:'Times New Roman';">o2</span>,使得以<span style="font-family:'Times New Roman';">T</span>定义的所有程序<span style="font-family:'Times New Roman';">P</span>在所有的对象<span style="font-family:'Times New Roman';">o1</span>代换<span style="font-family:'Times New Roman';">o2</span>时,程序<span style="font-family:'Times New Roman';">P</span>的行为没有变化,那么类型<span style="font-family:'Times New Roman';">S</span>是类型<span style="font-family:'Times New Roman';">T</span>的子类型。这个定义比较拗口且难以理解,因此我们一般使用它的另一个通俗版定义:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(231,239,249);"><tbody><tr><td valign="top"><p><span style="font-size:16px;"><strong>里氏代换原则<span style="font-family:'Times New Roman';">(Liskov Substitution Principle, LSP)</span>:所有引用基类(父类)的地方必须能透明地使用其子类的对象。</strong></span></p></td></tr></tbody></table></div><p align="left"><span style="font-size:16px;"> 里氏代换原则告诉我们,<span style="color:#ff0000;"><strong>在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。</strong></span>例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。</span></p><p align="left"><span style="font-size:16px;"> 例如有两个类,一个类为<span style="font-family:'Times New Roman';">BaseClass</span>,另一个是<span style="font-family:'Times New Roman';">SubClass</span>类,并且<span style="font-family:'Times New Roman';">SubClass</span>类是<span style="font-family:'Times New Roman';">BaseClass</span>类的子类,那么一个方法如果可以接受一个<span style="font-family:'Times New Roman';">BaseClass</span>类型的基类对象<span style="font-family:'Times New Roman';">base</span>的话,如:<span style="font-family:'Times New Roman';">method1(base)</span>,那么它必然可以接受一个<span style="font-family:'Times New Roman';">BaseClass</span>类型的子类对象<span style="font-family:'Times New Roman';">sub</span>,<span style="font-family:'Times New Roman';">method1(sub)</span>能够正常运行。反过来的代换不成立,如一个方法<span style="font-family:'Times New Roman';">method2</span>接受<span style="font-family:'Times New Roman';">BaseClass</span>类型的子类对象<span style="font-family:'Times New Roman';">sub</span>为参数:<span style="font-family:'Times New Roman';">method2(sub)</span>,那么一般而言不可以有<span style="font-family:'Times New Roman';">method2(base)</span>,除非是重载方法。</span></p><p align="left"><span style="font-size:16px;"> 里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此<strong><span style="color:#ff0000;">在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象</span></strong>。</span></p><p><span style="font-size:16px;"> </span><span style="font-size:16px;">在使用里氏代换原则时需要注意如下几个问题:</span></p><p align="left"><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (1)</span>子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。根据里氏代换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (2) </span>我们在运用里氏代换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。里氏代换原则是开闭原则的具体实现手段之一。</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (3) Java</span>语言中,在编译阶段,<span style="font-family:'Times New Roman';">Java</span>编译器会检查一个程序是否符合里氏代换原则,这是一个与实现无关的、纯语法意义上的检查,但<span style="font-family:'Times New Roman';">Java</span>编译器的检查是有局限的。</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(247,247,247);"><tbody><tr><td valign="top"><span style="font-family:'Times New Roman';"></span><p><span style="font-size:16px;"> 在Sunny软件公司开发的<span style="font-family:'Times New Roman';">CRM</span>系统中,客户<span style="font-family:'Times New Roman';">(Customer)</span>可以分为<span style="font-family:'Times New Roman';">VIP</span>客户<span style="font-family:'Times New Roman';">(VIPCustomer)</span>和普通客户<span style="font-family:'Times New Roman';">(CommonCustomer)</span>两类,系统需要提供一个发送<span style="font-family:'Times New Roman';">Email</span>的功能,原始设计方案如图<span style="font-family:'Times New Roman';">1</span>所示:<strong></strong></span></p><p align="center"><strong><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/06/1336312710_1412.jpg" width="507" height="330" style="width:412px;"></span></strong></p><p align="center"><strong><span style="font-size:12px;">图<span style="font-family:'Times New Roman';">1</span>原始结构图</span></strong></p><p><span style="font-size:16px;"> 在对系统进行进一步分析后发现,无论是普通客户还是<span style="font-family:'Times New Roman';">VIP</span>客户,发送邮件的过程都是相同的,也就是说两个<span style="font-family:'Times New Roman';">send()</span>方法中的代码重复,而且在本系统中还将增加新类型的客户。为了让系统具有更好的扩展性,同时减少代码重复,使用里氏代换原则对其进行重构。</span></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 在本实例中,可以考虑增加一个新的抽象客户类<span style="font-family:'Times New Roman';">Customer</span>,而将<span style="font-family:'Times New Roman';">CommonCustomer</span>和<span style="font-family:'Times New Roman';">VIPCustomer</span>类作为其子类,邮件发送类<span style="font-family:'Times New Roman';">EmailSender</span>类针对抽象客户类<span style="font-family:'Times New Roman';">Customer</span>编程,根据里氏代换原则,能够接受基类对象的地方必然能够接受子类对象,因此将<span style="font-family:'Times New Roman';">EmailSender</span>中的<span style="font-family:'Times New Roman';">send()</span>方法的参数类型改为<span style="font-family:'Times New Roman';">Customer</span>,如果需要增加新类型的客户,只需将其作为<span style="font-family:'Times New Roman';">Customer</span>类的子类即可。重构后的结构如图<span style="font-family:'Times New Roman';">2</span>所示:</span></p><p align="center"><img alt src="https://img-my.csdn.net/uploads/201205/06/1336312720_2300.jpg" width="199" height="439" style="width:319px;"></p><p align="center"><strong>图2<span style="font-family:'Times New Roman';"> </span>重构后的结构图</strong></p><p><span style="font-size:16px;"> <span style="color:#ff0000;"><strong>里氏代换原则是实现开闭原则的重要方式之一。</strong></span>在本实例中,在传递参数时使用基类对象,除此以外,在定义成员变量、定义局部变量、确定方法返回类型时都可使用里氏代换原则。针对基类编程,在程序运行时再确定具体子类。</span></p><h3 id="5、依赖倒转原则"><a href="#5、依赖倒转原则" class="headerlink" title="5、依赖倒转原则"></a>5、依赖倒转原则</h3><div class="htmledit_views" id="content_views"><p><span style="font-size:16px;"> <span style="font-size:16px;">如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要实现机制之一,它是系统抽象化的具体实现。依赖倒转原则是<span style="font-family:'Times New Roman';color:#ff0000;"><strong>Robert C. Martin</strong></span>在<span style="font-family:'Times New Roman';">1996</span>年为“<span style="font-family:'Times New Roman';">C++Reporter</span>”所写的专栏<span style="font-family:'Times New Roman';">Engineering Notebook</span>的第三篇,后来加入到他在<span style="font-family:'Times New Roman';">2002</span>年出版的经典著作“<span style="font-family:'Times New Roman';color:#ff0000;"><strong>Agile Software Development, Principles, Patterns, and Practices</strong></span>”一书中。依赖倒转原则定义如下:</span></span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(231,239,249);"><tbody><tr><td valign="top"><p><strong><span style="font-size:16px;">依赖倒转原则<span style="font-family:'Times New Roman';">(Dependency Inversion Principle, DIP)</span>:抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。</span></strong></p></td></tr></tbody></table></div><p align="left"><span style="font-size:16px;"> <span style="color:#ff0000;"> 依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。</span>为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。</span><span style="font-size:16px;"></span></p><p><span style="font-size:16px;"> 在引入抽象层后,系统将具有很好的灵活性,在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中,这样一来,如果系统行为发生变化,只需要对抽象层进行扩展,并修改配置文件,而无须修改原有系统的源代码,在不修改的情况下来扩展系统的功能,满足开闭原则的要求。</span></p><p><span style="font-size:16px;"> 在实现依赖倒转原则时,我们需要针对抽象层编程,而将具体类的对象通过<span style="color:#ff0000;"><strong>依赖注入<span style="font-family:'Times New Roman';">(DependencyInjection, DI)</span></strong></span>的方式注入到其他对象中,<strong><span style="color:#ff0000;">依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象</span></strong>。常用的注入方式有三种,分别是:<span style="color:#ff0000;"><strong>构造注入,设值注入(<span style="font-family:'Times New Roman';">Setter</span>注入)和接口注入</strong></span>。构造注入是指通过构造函数来传入具体类的对象,设值注入是指通过<span style="font-family:'Times New Roman';">Setter</span>方法来传入具体类的对象,而接口注入是指通过在接口中声明的业务方法来传入具体类的对象。这些方法在定义时使用的是抽象类型,在运行时再传入具体类型的对象,由子类对象来覆盖父类对象。</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top"><div class="table-box"><table border="0" cellspacing="0" cellpadding="0" width="631" style="width:631px;"><tbody><tr><td valign="top"><p align="center"><strong></strong> </p></td><td valign="top"><p><span style="font-size:16px;"><strong>扩展</strong></span></p><p><span style="font-size:16px;">软件工程大师<span style="font-family:'Times New Roman';">Martin Fowler</span>在其文章<em><span style="font-family:'Times New Roman';">Inversion of Control Containers and the Dependency Injection pattern</span></em>中对依赖注入进行了深入的分析,参考链接:</span></p><p><span style="font-family:'Times New Roman';font-size:16px;">http://martinfowler.com/articles/injection.html</span></p></td></tr></tbody></table></div><p></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 下面通过一个简单实例来加深对依赖倒转原则的理解:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(247,247,247);"><tbody><tr><td valign="top"><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> Sunny</span>软件公司开发人员在开发某<span style="font-family:'Times New Roman';">CRM</span>系统时发现:该系统经常需要将存储在<span style="font-family:'Times New Roman';">TXT</span>或<span style="font-family:'Times New Roman';">Excel</span>文件中的客户信息转存到数据库中,因此需要进行数据格式转换。在客户数据操作类中将调用数据格式转换类的方法实现格式转换和数据库插入操作,初始设计方案结构如图<span style="font-family:'Times New Roman';">1</span>所示:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/13/1336909329_9009.jpg" width="501" height="247"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">1 </span>初始设计方案结构图</span></strong></p><p><span style="font-size:16px;"> 在编码实现图<span style="font-family:'Times New Roman';">1</span>所示结构时,<span style="font-family:'Times New Roman';">Sunny</span>软件公司开发人员发现该设计方案存在一个非常严重的问题,由于每次转换数据时数据来源不一定相同,因此需要更换数据转换类,如有时候需要将<span style="font-family:'Times New Roman';">TXTDataConvertor</span>改为<span style="font-family:'Times New Roman';">ExcelDataConvertor</span>,此时,需要修改<span style="font-family:'Times New Roman';">CustomerDAO</span>的源代码,而且在引入并使用新的数据转换类时也不得不修改<span style="font-family:'Times New Roman';">CustomerDAO</span>的源代码,系统扩展性较差,违反了开闭原则,现需要对该方案进行重构。</span></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 在本实例中,<span style="color:#ff0000;">由于<span style="font-family:'Times New Roman';">CustomerDAO</span>针对具体数据转换类编程,因此在增加新的数据转换类或者更换数据转换类时都不得不修改<span style="font-family:'Times New Roman';">CustomerDAO</span>的源代码</span>。<span style="color:#ff0000;">我们可以通过引入抽象数据转换类解决该问题</span>,在引入抽象数据转换类<span style="font-family:'Times New Roman';">DataConvertor</span>之后,<span style="font-family:'Times New Roman';">CustomerDAO</span>针对抽象类<span style="font-family:'Times New Roman';">DataConvertor</span>编程,而将具体数据转换类名存储在配置文件中,符合依赖倒转原则。根据里氏代换原则,程序运行时,具体数据转换类对象将替换<span style="font-family:'Times New Roman';">DataConvertor</span>类型的对象,程序不会出现任何问题。更换具体数据转换类时无须修改源代码,只需要修改配置文件;如果需要增加新的具体数据转换类,只要将新增数据转换类作为<span style="font-family:'Times New Roman';">DataConvertor</span>的子类并修改配置文件即可,原有代码无须做任何修改,满足开闭原则。重构后的结构如图<span style="font-family:'Times New Roman';">2</span>所示:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/13/1336909334_4352.jpg" width="654" height="315"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">2</span>重构后的结构图</span></strong></p><p><span style="font-size:16px;"> </span></p><p><span style="font-size:16px;"> 在上述重构过程中,我们使用了开闭原则、里氏代换原则和依赖倒转原则,在大多数情况下,这三个设计原则会同时出现,<span style="color:#ff0000;"><strong>开闭原则是目标,里氏代换原则是基础,依赖倒转原则是手段</strong>,</span>它们相辅相成,相互补充,目标一致,只是分析问题时所站角度不同而已。</span></p><h3 id="6、接口隔离原则"><a href="#6、接口隔离原则" class="headerlink" title="6、接口隔离原则"></a>6、接口隔离原则</h3><div class="htmledit_views" id="content_views"><p><span style="font-size:16px;"> 接口隔离原则定义如下:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(231,239,249);"><tbody><tr><td valign="top"><p align="left"><strong><span style="font-size:16px;">接口隔离原则<span style="font-family:'Times New Roman';">(Interface Segregation Principle, ISP)</span>:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。</span></strong></p></td></tr></tbody></table></div><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> </span>根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。<strong><span style="color:#ff0000;">每一个接口应该承担一种相对独立的角色,不干不该干的事,该干的事都要干。</span></strong>这里的<span style="font-family:'Times New Roman';">“</span>接口<span style="font-family:'Times New Roman';">”</span>往往有两种不同的含义:一种是指一个类型所具有的方法特征的集合,仅仅是一种逻辑上的抽象;另外一种是指某种语言具体的<span style="font-family:'Times New Roman';">“</span>接口<span style="font-family:'Times New Roman';">”</span>定义,有严格的定义和结构,比如<span style="font-family:'Times New Roman';">Java</span>语言中的<span style="font-family:'Times New Roman';">interface</span>。对于这两种不同的含义,<span style="font-family:'Times New Roman';">ISP</span>的表达方式以及含义都有所不同:</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (1)</span>当把“接口”理解成一个类型所提供的所有方法特征的集合的时候,这就是一种逻辑上的概念,接口的划分将直接带来类型的划分。可以把接口理解成角色,一个接口只能代表一个角色,每个角色都有它特定的一个接口,此时,这个原则可以叫做<span style="color:#ff0000;">“<strong>角色隔离原则</strong>”</span>。</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (2)</span>如果把“接口”理解成狭义的特定语言的接口,那么<span style="font-family:'Times New Roman';">ISP</span>表达的意思是指<span style="color:#ff0000;"><strong>接口仅仅提供客户端需要的行为,客户端不需要的行为则隐藏起来,应当为客户端提供尽可能小的单独的接口,而不要提供大的总接口</strong>。</span>在面向对象编程语言中,实现一个接口就需要实现该接口中定义的所有方法,因此大的总接口使用起来不一定很方便,为了使接口的职责单一,需要将大接口中的方法根据其职责不同分别放在不同的小接口中,以确保每个接口使用起来都较为方便,并都承担某一单一角色。接口应该尽量细化,同时接口中的方法应该尽量少,每个接口中只包含一个客户端(如子模块或业务逻辑类)所需的方法即可,这种机制也称为<span style="color:#ff0000;">“<strong>定制服务</strong>”</span>,即为不同的客户端提供宽窄不同的接口。</span></p><p align="left"><span style="font-size:16px;"> 下面通过一个简单实例来加深对接口隔离原则的理解:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(242,242,242);"><tbody><tr><td valign="top"><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> Sunny</span>软件公司开发人员针对某<span style="font-family:'Times New Roman';">CRM</span>系统的客户数据显示模块设计了如图<span style="font-family:'Times New Roman';">1</span>所示接口,其中方法<span style="font-family:'Times New Roman';">dataRead()</span>用于从文件中读取数据,方法<span style="font-family:'Times New Roman';">transformToXML()</span>用于将数据转换成<span style="font-family:'Times New Roman';">XML</span>格式,方法<span style="font-family:'Times New Roman';">createChart()</span>用于创建图表,方法<span style="font-family:'Times New Roman';">displayChart()</span>用于显示图表,方法<span style="font-family:'Times New Roman';">createReport()</span>用于创建文字报表,方法<span style="font-family:'Times New Roman';">displayReport()</span>用于显示文字报表。</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/13/1336910243_3390.jpg" width="609" height="165" style="width:557px;"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">1 </span>初始设计方案结构图</span></strong></p><p><span style="font-size:16px;"> 在实际使用过程中发现该接口很不灵活,例如如果一个具体的数据显示类无须进行数据转换(源文件本身就是<span style="font-family:'Times New Roman';">XML</span>格式),但由于实现了该接口,将不得不实现其中声明的<span style="font-family:'Times New Roman';">transformToXML()</span>方法(至少需要提供一个空实现);如果需要创建和显示图表,除了需实现与图表相关的方法外,还需要实现创建和显示文字报表的方法,否则程序编译时将报错。</span></p><p><span style="font-size:16px;"> 现使用接口隔离原则对其进行重构。</span></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 在图<span style="font-family:'Times New Roman';">1</span>中,由于在接口<span style="font-family:'Times New Roman';">CustomerDataDisplay</span>中定义了太多方法,即该接口承担了太多职责,一方面导致该接口的实现类很庞大,在不同的实现类中都不得不实现接口中定义的所有方法,灵活性较差,如果出现大量的空方法,将导致系统中产生大量的无用代码,影响代码质量;另一方面由于客户端针对大接口编程,将在一定程序上破坏程序的封装性,客户端看到了不应该看到的方法,没有为客户端定制接口。因此需要将该接口按照接口隔离原则和单一职责原则进行重构,将其中的一些方法封装在不同的小接口中,确保每一个接口使用起来都较为方便,并都承担某一单一角色,每个接口中只包含一个客户端(如模块或类)所需的方法即可。</span></p><p><span style="font-size:16px;"> 通过使用接口隔离原则,本实例重构后的结构如图<span style="font-family:'Times New Roman';">2</span>所示:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/13/1336910247_6209.jpg" width="624" height="403"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">2</span>重构后的结构图</span></strong></p><p><span style="font-size:16px;"> <span style="color:#ff0000;"><strong> 在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。</strong></span>一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="width:572px;"><tbody><tr><td valign="top"><p><span style="font-size:16px;"><strong>扩展</strong></span></p><p><span style="font-size:16px;"><span style="font-family:'宋体';">在《敏捷软件开发——原则、模式与实践》一书中,</span>RobertC. Martin从解决“接口污染”的角度对接口隔离原则进行了详细的介绍,大家可以参阅该书第12章——<em>接口隔离原则(ISP)</em>进行深入的学习。</span></p></td></tr></tbody></table></div><h3 id="7、合成复用原则"><a href="#7、合成复用原则" class="headerlink" title="7、合成复用原则"></a>7、合成复用原则</h3><div class="htmledit_views" id="content_views"><p align="left"><span style="font-size:16px;"> 合成复用原则又称为组合<span style="font-family:'Times New Roman';">/</span>聚合复用原则<span style="font-family:'Times New Roman';">(Composition/Aggregate Reuse Principle, CARP)</span>,其定义如下:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(231,239,249);"><tbody><tr><td valign="top"><p align="left"><strong><span style="font-size:16px;">合成复用原则<span style="font-family:'Times New Roman';">(</span><span style="font-family:'Times New Roman';">Composite Reuse Principle, CRP)</span>:尽量使用对象组合,而不是继承来达到复用的目的。</span></strong></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分;新对象通过委派调用已有对象的方法达到复用功能的目的。简言之:<span style="color:#ff0000;"><strong>复用时要尽量使用组合<span style="font-family:'Times New Roman';">/</span>聚合关系(关联关系),少用继承</strong></span>。</span></p><p><span style="font-size:16px;"> 在面向对象设计中,可以通过两种方法在不同的环境中复用已有的设计和实现,即<span style="color:#ff0000;">通过组合<span style="font-family:'Times New Roman';">/</span>聚合关系或通过继承</span><span style="color:#ff0000;">,但首先应该考虑使用组合<span style="font-family:'Times New Roman';">/</span>聚合</span>,组合<span style="font-family:'Times New Roman';">/</span>聚合可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少;<span style="color:#ff0000;">其次才考虑继承</span>,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此<span style="color:#ff0000;">需要慎重使用继承复用</span>。</span></p><p><span style="font-size:16px;"> 通过继承来进行复用的主要问题在于继承复用会破坏系统的封装性,因为继承会将基类的实现细节暴露给子类,由于基类的内部细节通常对子类来说是可见的,所以这种复用又称<span style="font-family:'Times New Roman';">“</span>白箱<span style="font-family:'Times New Roman';">”</span>复用,如果基类发生改变,那么子类的实现也不得不发生改变;从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;而且继承只能在有限的环境中使用(如类没有声明为不能被继承)。</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top"><div class="table-box"><table border="0" cellspacing="0" cellpadding="0" width="564"><tbody><tr><td valign="top"><p align="center"><strong></strong> </p></td><td valign="top"><p><span style="font-size:16px;"><strong>扩展</strong></span></p><span style="font-size:16px;"></span><p><span style="font-size:16px;">对于继承的深入理解,大家可以参考《软件架构设计》一书作者温昱先生的文章——《见山只是山见水只是水——提升对继承的认识》。</span></p></td></tr></tbody></table></div><p></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 由于组合或聚合关系可以将已有的对象(也可称为成员对象)纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,这样做可以使得成员对象的内部实现细节对于新对象不可见,所以这种复用又称为<span style="font-family:'Times New Roman';">“</span>黑箱<span style="font-family:'Times New Roman';">”</span>复用,相对继承关系而言,其耦合度相对较低,成员对象的变化对新对象的影响不大,可以在新对象中根据实际需要有选择性地调用成员对象的操作;合成复用可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的其他对象。</span></p><p><span style="font-size:16px;"> 一般而言,<span style="color:#ff0000;">如果两个类之间是<span style="font-family:'Times New Roman';">“Has-A”</span>的关系应使用组合或聚合,如果是<span style="font-family:'Times New Roman';">“Is-A”</span>关系可使用继承</span>。<span style="font-family:'Times New Roman';">"Is-A"</span>是严格的分类学意义上的定义,意思是一个类是另一个类的<span style="font-family:'Times New Roman';">"</span>一种<span style="font-family:'Times New Roman';">"</span>;而<span style="font-family:'Times New Roman';">"Has-A"</span>则不同,它表示某一个角色具有某一项责任。</span></p><p align="left"><span style="font-size:16px;"> 下面通过一个简单实例来加深对合成复用原则的理解:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(242,242,242);"><tbody><tr><td valign="top"><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> Sunny</span>软件公司开发人员在初期的<span style="font-family:'Times New Roman';">CRM</span>系统设计中,考虑到客户数量不多,系统采用<span style="font-family:'Times New Roman';">MySQL</span>作为数据库,与数据库操作有关的类如<span style="font-family:'Times New Roman';">CustomerDAO</span>类等都需要连接数据库,连接数据库的方法<span style="font-family:'Times New Roman';">getConnection()</span>封装在<span style="font-family:'Times New Roman';">DBUtil</span>类中,由于需要重用<span style="font-family:'Times New Roman';">DBUtil</span>类的<span style="font-family:'Times New Roman';">getConnection()</span>方法,设计人员将<span style="font-family:'Times New Roman';">CustomerDAO</span>作为<span style="font-family:'Times New Roman';">DBUtil</span>类的子类,初始设计方案结构如图<span style="font-family:'Times New Roman';">1</span>所示:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/14/1336930023_1487.jpg" width="346" height="280" style="width:342px;"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">1 </span>初始设计方案结构图</span></strong></p><p><span style="font-size:16px;"> 随着客户数量的增加,系统决定升级为<span style="font-family:'Times New Roman';">Oracle</span>数据库,因此需要增加一个新的<span style="font-family:'Times New Roman';">OracleDBUtil</span>类来连接<span style="font-family:'Times New Roman';">Oracle</span>数据库,由于在初始设计方案中<span style="font-family:'Times New Roman';">CustomerDAO</span>和<span style="font-family:'Times New Roman';">DBUtil</span>之间是继承关系,因此在更换数据库连接方式时需要修改<span style="font-family:'Times New Roman';">CustomerDAO</span>类的源代码,将<span style="font-family:'Times New Roman';">CustomerDAO</span>作为<span style="font-family:'Times New Roman';">OracleDBUtil</span>的子类,这将违反开闭原则。【当然也可以修改<span style="font-family:'Times New Roman';">DBUtil</span>类的源代码,同样会违反开闭原则。】</span></p><p><span style="font-size:16px;"> 现使用合成复用原则对其进行重构。</span></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 根据合成复用原则,我们<span style="color:#ff0000;">在实现复用时应该多用关联,少用继承</span>。因此在本实例中我们可以使用关联复用来取代继承复用,重构后的结构如图<span style="font-family:'Times New Roman';">2</span>所示:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/14/1336930028_3039.jpg" width="510" height="252" style="width:509px;"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">2 </span>重构后的结构图</span></strong></p><p><span style="font-size:16px;"> 在图<span style="font-family:'Times New Roman';">2</span>中,<span style="font-family:'Times New Roman';">CustomerDAO</span>和<span style="font-family:'Times New Roman';">DBUtil</span>之间的关系由继承关系变为关联关系,采用依赖注入的方式将<span style="font-family:'Times New Roman';">DBUtil</span>对象注入到<span style="font-family:'Times New Roman';">CustomerDAO</span>中,可以使用构造注入,也可以使用<span style="font-family:'Times New Roman';">Setter</span>注入。如果需要对<span style="font-family:'Times New Roman';">DBUtil</span>的功能进行扩展,可以通过其子类来实现,如通过子类<span style="font-family:'Times New Roman';">OracleDBUtil</span></span><span style="font-size:16px;">来连接<span style="font-family:'Times New Roman';">Oracle</span>数据库。由于<span style="font-family:'Times New Roman';">CustomerDAO</span>针对<span style="font-family:'Times New Roman';">DBUtil</span>编程,根据里氏代换原则,<span style="font-family:'Times New Roman';">DBUtil</span>子类的对象可以覆盖<span style="font-family:'Times New Roman';">DBUtil</span>对象,只需在<span style="font-family:'Times New Roman';">CustomerDAO</span>中注入子类对象即可使用子类所扩展的方法。例如在<span style="font-family:'Times New Roman';">CustomerDAO</span>中注入<span style="font-family:'Times New Roman';">OracleDBUtil</span>对象,即可实现<span style="font-family:'Times New Roman';">Oracle</span>数据库连接,原有代码无须进行修改,而且还可以很灵活地增加新的数据库连接方式。</span></p><h3 id="8、迪米特法则"><a href="#8、迪米特法则" class="headerlink" title="8、迪米特法则"></a>8、迪米特法则</h3><div class="htmledit_views" id="content_views"><p><span style="font-size:16px;"> 迪米特法则来自于<span style="font-family:'Times New Roman';">1987</span>年美国东北大学<span style="font-family:'Times New Roman';">(Northeastern University)</span>一个名为“<span style="font-family:'Times New Roman';">Demeter</span>”的研究项目。迪米特法则又称为<span style="color:#ff0000;">最少知识原则<span style="font-family:'Times New Roman';">(LeastKnowledge Principle, LKP)</span>,</span>其定义如下:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(231,239,249);"><tbody><tr><td valign="top"><p align="left"><strong><span style="font-size:16px;">迪米特法则<span style="font-family:'Times New Roman';">(Law of Demeter, LoD)</span>:一个软件实体应当尽可能少地与其他实体发生相互作用。</span></strong></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 如果一个系统符合迪米特法则,那么当其中某一个模块发生修改时,就会尽量少地影响其他模块,扩展会相对容易,这是对软件实体之间通信的限制,迪米特法则要求限制软件实体之间通信的宽度和深度。<strong><span style="color:#ff0000;">迪米特法则可降低系统的耦合度,使类与类之间保持松散的耦合关系。</span></strong></span></p><p><span style="font-size:16px;"> 迪米特法则还有几种定义形式,包括<strong>:</strong><span style="color:#ff0000;"><strong>不要和“陌生人”说话</strong>、<strong>只与你的直接朋友通信</strong></span>等,在迪米特法则中,对于一个对象,其朋友包括以下几类:</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (1)</span>当前对象本身<span style="font-family:'Times New Roman';">(this)</span>;</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (2)</span>以参数形式传入到当前对象方法中的对象;</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (3)</span>当前对象的成员对象;</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (4)</span>如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友;</span></p><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> (5)</span>当前对象所创建的对象。</span></p><p><span style="font-size:16px;"> 任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。在应用迪米特法则时,一个对象只能与直接朋友发生交互,不要与“陌生人”发生直接交互,这样做可以降低系统的耦合度,一个对象的改变不会给太多其他对象带来影响。</span></p><p><span style="font-size:16px;"> 迪米特法则要求我们在设计系统时,<strong><span style="color:#ff0000;">应该尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用</span></strong>。简言之,就是<span style="color:#ff0000;"><strong>通过引入一个合理的第三者来降低现有对象之间的耦合度</strong>。</span></span></p><p><span style="font-size:16px;"> 在将迪米特法则运用到系统设计中时,要注意下面的几点:<span style="color:#ff0000;"><strong>在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及</strong>;<strong>在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限</strong>;<strong>在类的设计上,只要有可能,一个类型应当设计成不变类</strong>;<strong>在对其他类的引用上,一个对象对其他对象的引用应当降到最低</strong>。</span></span></p><p align="left"><span style="font-size:16px;"> 下面通过一个简单实例来加深对迪米特法则的理解:</span></p><div class="table-box"><table border="1" cellspacing="0" cellpadding="0" style="background:rgb(242,242,242);"><tbody><tr><td valign="top"><p><span style="font-size:16px;"><span style="font-family:'Times New Roman';"> Sunny</span>软件公司所开发<span style="font-family:'Times New Roman';">CRM</span>系统包含很多业务操作窗口,在这些窗口中,某些界面控件之间存在复杂的交互关系,一个控件事件的触发将导致多个其他界面控件产生响应,例如,当一个按钮<span style="font-family:'Times New Roman';">(Button)</span>被单击时,对应的列表框<span style="font-family:'Times New Roman';">(List)</span>、组合框<span style="font-family:'Times New Roman';">(ComboBox)</span>、文本框<span style="font-family:'Times New Roman';">(TextBox)</span>、文本标签<span style="font-family:'Times New Roman';">(Label)</span>等都将发生改变,在初始设计方案中,界面控件之间的交互关系可简化为如图<span style="font-family:'Times New Roman';">1</span>所示结构:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/14/1336930654_2743.jpg"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">1 </span>初始设计方案结构图</span></strong></p><p><span style="font-size:16px;"> 在图<span style="font-family:'Times New Roman';">1</span>中,由于界面控件之间的交互关系复杂,导致在该窗口中增加新的界面控件时需要修改与之交互的其他控件的源代码,系统扩展性较差,也不便于增加和删除新控件。</span></p><p><span style="font-size:16px;"> 现使用迪米特对其进行重构。</span></p></td></tr></tbody></table></div><p><span style="font-size:16px;"> 在本实例中,<span style="color:#ff0000;">可以通过引入一个专门用于控制界面控件交互的中间类<span style="font-family:'Times New Roman';">(Mediator)</span>来降低界面控件之间的耦合度</span>。<span style="color:#ff0000;">引入中间类之后,界面控件之间不再发生直接引用,而是将请求先转发给中间类,再由中间类来完成对其他控件的调用。当需要增加或删除新的控件时,只需修改中间类即可,无须修改新增控件或已有控件的源代码</span>,重构后结构如图<span style="font-family:'Times New Roman';">2</span>所示:</span></p><p align="center"><span style="font-size:16px;"><img alt src="https://img-my.csdn.net/uploads/201205/14/1336930673_6550.jpg" width="465" height="392" style="width:463px;"></span></p><p align="center"><strong><span style="font-size:16px;">图<span style="font-family:'Times New Roman';">2 </span>重构后的结构图</span></strong></p></div></div></div></div></div></div>]]></content>
<summary type="html">
<h3 id="1、面向对象设计原则概述"><a href="#1、面向对象设计原则概述" class="headerlink" title="1、面向对象设计原则概述"></a>1、面向对象设计原则概述</h3><p>&nbsp;&nbsp;&nbsp;&nbsp; 对于面向对象软件系统的设计而言,在支持可维护性的同时,提高系统的可复用性是一个至关重要的问题,<strong>如何同时提高一个软件系统的可维护性和可复用性是面向对象设计需要解决的核心问题之一</strong>。在面向对象设计中,可维护性的复用是以设计原则为基础的。每一个原则都蕴含一些面向对象设计的思想,可以从不同的角度提升一个软件结构的设计水平。</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>美化Hexo 个人博客</title>
<link href="ayjcsgm.github.io/2019/12/14/%E7%BE%8E%E5%8C%96Hexo-%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/"/>
<id>ayjcsgm.github.io/2019/12/14/%E7%BE%8E%E5%8C%96Hexo-%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/</id>
<published>2019-12-14T12:32:27.000Z</published>
<updated>2019-12-14T12:45:34.979Z</updated>
<content type="html"><![CDATA[<p></p><div class="toc"><h3><a name="t0"></a><a name="t0"></a>文章目录</h3><ul><li><a href="#font_colorFF00001_font_20" rel="nofollow" target="_self"><font color="#FF000">【01】添加卡通人物(看板娘) </font></a></li><li><a href="#font_colorFF000002_font_56" rel="nofollow" target="_self"><font color="#FF0000">【02】添加鼠标点击爱心效果 </font></a></li><li><a href="#font_colorFF000003_font_74" rel="nofollow" target="_self"><font color="#FF0000">【03】添加鼠标点击显示字体效果 </font></a></li><li><a href="#font_colorFF000004_font_123" rel="nofollow" target="_self"><font color="#FF0000">【04】添加鼠标点击烟花爆炸效果 </font></a></li><li><a href="#font_colorFF000005_font_140" rel="nofollow" target="_self"><font color="#FF0000">【05】自定义鼠标指针样式 </font></a></li><a id="more"></a><li><a href="#font_colorFF000006_font_163" rel="nofollow" target="_self"><font color="#FF0000">【06】添加彩色滚动变换字体 </font></a></li><li><a href="#font_colorFF000007_font_213" rel="nofollow" target="_self"><font color="#FF0000">【07】添加背景音乐 </font></a></li><li><a href="#font_colorFF000008_font_227" rel="nofollow" target="_self"><font color="#FF0000">【08】浏览器网页标题恶搞 </font></a></li><li><a href="#font_colorFF000009_font_262" rel="nofollow" target="_self"><font color="#FF0000">【09】背景添加动态线条效果 </font></a></li><li><a href="#font_colorFF000010_font_282" rel="nofollow" target="_self"><font color="#FF0000">【10】添加人体时钟等有趣的挂件 </font></a></li><li><a href="#font_colorFF000011_font_303" rel="nofollow" target="_self"><font color="#FF0000">【11】添加网站雪花飘落效果 </font></a></li><li><a href="#font_colorFF000012_font_500" rel="nofollow" target="_self"><font color="#FF0000">【12】添加背景动态彩带效果 </font></a></li><li><a href="#font_colorFF000013_font_515" rel="nofollow" target="_self"><font color="#FF0000">【13】添加背景代码雨特效 </font></a></li><li><a href="#font_colorFF0000__font_600" rel="nofollow" target="_self"><font color="#FF0000"> 未完待续...... </font></a></li></ul></div><p></p><hr><h1><a name="t1"></a><a name="t1"></a><a id="font_colorFF00001_font_20"></a><font color="#FF000">【01】添加卡通人物(看板娘) </font></h1><p>我在逛别人博客的时候偶然发现右下角居然有一个萌萌的卡通人物,还能根据你鼠标位置摇头,瞬间被吸引到了,赶紧也给自己博客添加一个吧!<a href="https://github.com/EYHN/hexo-helper-live2d" rel="noopener" target="_blank">点击此处</a>进入该项目地址</p><p>输入如下命令获取 live2d :</p><pre class="prettyprint"><code class="prism language-shell has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;">$ <span class="token function">npm</span> <span class="token function">install</span> --save hexo-helper-live2d <p>输入以下命令,下载相应的模型,将 <font color="#FF0000">packagename</font> 更换成模型名称即可,更多模型选择请<a href="https://github.com/xiazeyu/live2d-widget-models" rel="noopener" target="_blank">点击此处</a>,各个模型的预览请<a href="https://huaji8.top/post/live2d-plugin-2.0/" rel="noopener" target="_blank">访问原作者的博客</a></p><pre class="prettyprint"><code class="prism language-shell has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;">$ <span class="token function">npm</span> <span class="token function">install</span> packagename<p>打开站点目录下的 <font color="#FF0000">_config.yml</font> 文件,添加如下代码:</p><pre class="prettyprint"><code class="prism language-yaml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token key atrule">live2d</span><span class="token punctuation">:</span> <span class="token key atrule">enable</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">scriptFrom</span><span class="token punctuation">:</span> local <span class="token key atrule">model</span><span class="token punctuation">:</span> <span class="token key atrule">use</span><span class="token punctuation">:</span> live2d<span class="token punctuation">-</span>widget<span class="token punctuation">-</span>model<span class="token punctuation">-</span>haruto <span class="token comment">#模型选择</span> <span class="token key atrule">display</span><span class="token punctuation">:</span> <span class="token key atrule">position</span><span class="token punctuation">:</span> right <span class="token comment">#模型位置</span> <span class="token key atrule">width</span><span class="token punctuation">:</span> <span class="token number">150 </span><span class="token comment">#模型宽度</span> <span class="token key atrule">height</span><span class="token punctuation">:</span> <span class="token number">300 </span><span class="token comment">#模型高度</span> <span class="token key atrule">mobile</span><span class="token punctuation">:</span> <span class="token key atrule">show</span><span class="token punctuation">:</span> <span class="token boolean important">false </span><span class="token comment">#是否在手机端显示</span><p><img src="https://img-blog.csdnimg.cn/20191214200242585.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p>设置好过后我们就拥有了一个卡通人物<br><img src></p><hr><h1><a name="t2"></a><a name="t2"></a><a id="font_colorFF000002_font_56"></a><font color="#FF0000">【02】添加鼠标点击爱心效果 </font></h1><p>在<font color="#FF0000"> \themes\hexo-theme-spfk\source\js</font> 下新建文件 <font color="#FF0000">love.js</font>,在 <font color="#FF0000">love.js</font> 文件中添加以下代码:</p><pre class="prettyprint"><code class="prism language-javascript has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token operator">!</span><span class="token keyword">function</span><span class="token punctuation">(</span>e<span class="token punctuation">,</span>t<span class="token punctuation">,</span>a<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">function</span> <span class="token function">n</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token function">c</span><span class="token punctuation">(</span><span class="token string">".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 500%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">o</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">r</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">r</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> e<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>e<span class="token operator"><</span>d<span class="token punctuation">.</span>length<span class="token punctuation">;</span>e<span class="token operator">++</span><span class="token punctuation">)</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>alpha<span class="token operator"><=</span><span class="token number">0</span><span class="token operator">?</span><span class="token punctuation">(</span>t<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">removeChild</span><span class="token punctuation">(</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>el<span class="token punctuation">)</span><span class="token punctuation">,</span>d<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span>e<span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token punctuation">(</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>y<span class="token operator">--</span><span class="token punctuation">,</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>scale<span class="token operator">+=</span><span class="token number">.004</span><span class="token punctuation">,</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>alpha<span class="token operator">-=</span><span class="token number">.013</span><span class="token punctuation">,</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>el<span class="token punctuation">.</span>style<span class="token punctuation">.</span>cssText<span class="token operator">=</span><span class="token string">"left:"</span><span class="token operator">+</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>x<span class="token operator">+</span><span class="token string">"px;top:"</span><span class="token operator">+</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>y<span class="token operator">+</span><span class="token string">"px;opacity:"</span><span class="token operator">+</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>alpha<span class="token operator">+</span><span class="token string">";transform:scale("</span><span class="token operator">+</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>scale<span class="token operator">+</span><span class="token string">","</span><span class="token operator">+</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>scale<span class="token operator">+</span><span class="token string">") rotate(45deg);background:"</span><span class="token operator">+</span>d<span class="token punctuation">[</span>e<span class="token punctuation">]</span><span class="token punctuation">.</span>color<span class="token operator">+</span><span class="token string">";z-index:99999"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">requestAnimationFrame</span><span class="token punctuation">(</span>r<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">o</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">var</span> t<span class="token operator">=</span><span class="token string">"function"</span><span class="token operator">==</span><span class="token keyword">typeof</span> e<span class="token punctuation">.</span>onclick<span class="token operator">&&</span>e<span class="token punctuation">.</span>onclick<span class="token punctuation">;</span>e<span class="token punctuation">.</span><span class="token function-variable function">onclick</span><span class="token operator">=</span><span class="token keyword">function</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">{</span>t<span class="token operator">&&</span><span class="token function">t</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">i</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">i</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">var</span> a<span class="token operator">=</span>t<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"div"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>a<span class="token punctuation">.</span>className<span class="token operator">=</span><span class="token string">"heart"</span><span class="token punctuation">,</span>d<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span>el<span class="token punctuation">:</span>a<span class="token punctuation">,</span>x<span class="token punctuation">:</span>e<span class="token punctuation">.</span>clientX<span class="token operator">-</span><span class="token number">5</span><span class="token punctuation">,</span>y<span class="token punctuation">:</span>e<span class="token punctuation">.</span>clientY<span class="token operator">-</span><span class="token number">5</span><span class="token punctuation">,</span>scale<span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">,</span>alpha<span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">,</span>color<span class="token punctuation">:</span><span class="token function">s</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>t<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">c</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">var</span> a<span class="token operator">=</span>t<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"style"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>a<span class="token punctuation">.</span>type<span class="token operator">=</span><span class="token string">"text/css"</span><span class="token punctuation">;</span><span class="token keyword">try</span><span class="token punctuation">{</span>a<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>t<span class="token punctuation">.</span><span class="token function">createTextNode</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">catch</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span><span class="token punctuation">{</span>a<span class="token punctuation">.</span>styleSheet<span class="token punctuation">.</span>cssText<span class="token operator">=</span>e<span class="token punctuation">}</span>t<span class="token punctuation">.</span><span class="token function">getElementsByTagName</span><span class="token punctuation">(</span><span class="token string">"head"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">s</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">return</span><span class="token string">"rgb("</span><span class="token operator">+</span><span class="token operator">~</span><span class="token operator">~</span><span class="token punctuation">(</span><span class="token number">255</span><span class="token operator">*</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">+</span><span class="token string">","</span><span class="token operator">+</span><span class="token operator">~</span><span class="token operator">~</span><span class="token punctuation">(</span><span class="token number">255</span><span class="token operator">*</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">+</span><span class="token string">","</span><span class="token operator">+</span><span class="token operator">~</span><span class="token operator">~</span><span class="token punctuation">(</span><span class="token number">255</span><span class="token operator">*</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">+</span><span class="token string">")"</span><span class="token punctuation">}</span><span class="token keyword">var</span> d<span class="token operator">=</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>e<span class="token punctuation">.</span><span class="token function-variable function">requestAnimationFrame</span><span class="token operator">=</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">return</span> e<span class="token punctuation">.</span>requestAnimationFrame<span class="token operator">||</span>e<span class="token punctuation">.</span>webkitRequestAnimationFrame<span class="token operator">||</span>e<span class="token punctuation">.</span>mozRequestAnimationFrame<span class="token operator">||</span>e<span class="token punctuation">.</span>oRequestAnimationFrame<span class="token operator">||</span>e<span class="token punctuation">.</span>msRequestAnimationFrame<span class="token operator">||</span><span class="token keyword">function</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token function">setTimeout</span><span class="token punctuation">(</span>e<span class="token punctuation">,</span><span class="token number">1e3</span><span class="token operator">/</span><span class="token number">60</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token function">n</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">(</span>window<span class="token punctuation">,</span>document<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>在 <font color="#FF0000">\themes\hexo-theme-spfk\layout\layout.ejs</font> 文件末尾添加以下代码:</p><pre class="prettyprint"><code class="prism language-html has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token comment"><!-- 页面点击小红心 --></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/js/love.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre><p>完成以上操作后,当我们点击鼠标的时候就可以看见爱心的特效了<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk0YjFlZDQwYS5wbmc" alt></p><hr><h1><a name="t3"></a><a name="t3"></a><a id="font_colorFF000003_font_74"></a><font color="#FF0000">【03】添加鼠标点击显示字体效果 </font></h1><p>在<font color="#FF0000"> /themes/hexo-theme-spfk/source/js</font> 下新建文件 <font color="#FF0000">click_show_text.js</font>,在 <font color="#FF0000">click_show_text.js</font> 文件中添加以下代码:</p><figure class="highlight plain"><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><span class="line">var a_idx = 0;</span><br><span class="line">jQuery(document).ready(function($) {</span><br><span class="line"> $("body").click(function(e) {</span><br><span class="line"> var a = new Array</span><br><span class="line"> ("富强", "民主", "文明", "和谐", "自由", "平等", "公正", "法治", "爱国", "敬业", "诚信", "友善");</span><br><span class="line"> var $i = $("<span/>").text(a[a_idx]);</span><br><span class="line"> a_idx = (a_idx + 1) % a.length;</span><br><span class="line"> var x = e.pageX,</span><br><span class="line"> y = e.pageY;</span><br><span class="line"> $i.css({</span><br><span class="line"> "z-index": 5,</span><br><span class="line"> "top": y - 20,</span><br><span class="line"> "left": x,</span><br><span class="line"> "position": "absolute",</span><br><span class="line"> "font-weight": "bold",</span><br><span class="line"> "color": "#FF0000"</span><br><span class="line"> });</span><br><span class="line"> $("body").append($i);</span><br><span class="line"> $i.animate({</span><br><span class="line"> "top": y - 180,</span><br><span class="line"> "opacity": 0</span><br><span class="line"> },</span><br><span class="line">3000,</span><br><span class="line">function() {</span><br><span class="line"> $i.remove();</span><br><span class="line">});</span><br><span class="line"> });</span><br><span class="line"> setTimeout('delay()', 2000);</span><br><span class="line">});</span><br><span class="line">function delay() {</span><br><span class="line">$(".buryit").removeAttr(“onclick”);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其中的社会主义核心价值观可以根据你自己的创意替换为其他文字,然后在 <font color="#FF0000">\themes\hexo-theme-spfk\layout\layout.ejs</font> 文件末尾添加以下代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><!--单击显示文字--></span><br><span class="line"><script type="text/javascript" src="/js/click_show_text.js"></script></span><br></pre></td></tr></table></figure><p>最终实现效果如下:<br><img src="https://img-blog.csdnimg.cn/20190807102038173.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><hr><h1><a name="t4"></a><a name="t4"></a><a id="font_colorFF000004_font_123"></a><font color="#FF0000">【04】添加鼠标点击烟花爆炸效果 </font></h1><p>在 <font color="#FF0000">\themes\material-x\source\js</font> 目录下新建一个 <font color="#FF0000">fireworks.js</font> 的文件,里面写入以下代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">"use strict";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color="#F00",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:"linear",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(".fireworks");if(canvasEl){var ctx=canvasEl.getContext("2d"),numberOfParticules=30,pointerX=0,pointerY=0,tap="mousedown",colors=["#FF1461","#18FF92","#5A87FF","#FBF38C"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+"px",canvasEl.style.height=window.innerHeight+"px",canvasEl.getContext("2d").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){"sidebar"!==e.target.id&&"toggle-sidebar"!==e.target.id&&"A"!==e.target.nodeName&&"IMG"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener("resize",setCanvasSize,!1)}"use strict";function updateCoords(e){pointerX=(e.clientX||e.touches[0].clientX)-canvasEl.getBoundingClientRect().left,pointerY=e.clientY||e.touches[0].clientY-canvasEl.getBoundingClientRect().top}function setParticuleDirection(e){var t=anime.random(0,360)*Math.PI/180,a=anime.random(50,180),n=[-1,1][anime.random(0,1)]*a;return{x:e.x+n*Math.cos(t),y:e.y+n*Math.sin(t)}}function createParticule(e,t){var a={};return a.x=e,a.y=t,a.color=colors[anime.random(0,colors.length-1)],a.radius=anime.random(16,32),a.endPos=setParticuleDirection(a),a.draw=function(){ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.fillStyle=a.color,ctx.fill()},a}function createCircle(e,t){var a={};return a.x=e,a.y=t,a.color="#F00",a.radius=0.1,a.alpha=0.5,a.lineWidth=6,a.draw=function(){ctx.globalAlpha=a.alpha,ctx.beginPath(),ctx.arc(a.x,a.y,a.radius,0,2*Math.PI,!0),ctx.lineWidth=a.lineWidth,ctx.strokeStyle=a.color,ctx.stroke(),ctx.globalAlpha=1},a}function renderParticule(e){for(var t=0;t<e.animatables.length;t++){e.animatables[t].target.draw()}}function animateParticules(e,t){for(var a=createCircle(e,t),n=[],i=0;i<numberOfParticules;i++){n.push(createParticule(e,t))}anime.timeline().add({targets:n,x:function(e){return e.endPos.x},y:function(e){return e.endPos.y},radius:0.1,duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule}).add({targets:a,radius:anime.random(80,160),lineWidth:0,alpha:{value:0,easing:"linear",duration:anime.random(600,800)},duration:anime.random(1200,1800),easing:"easeOutExpo",update:renderParticule,offset:0})}function debounce(e,t){var a;return function(){var n=this,i=arguments;clearTimeout(a),a=setTimeout(function(){e.apply(n,i)},t)}}var canvasEl=document.querySelector(".fireworks");if(canvasEl){var ctx=canvasEl.getContext("2d"),numberOfParticules=30,pointerX=0,pointerY=0,tap="mousedown",colors=["#FF1461","#18FF92","#5A87FF","#FBF38C"],setCanvasSize=debounce(function(){canvasEl.width=2*window.innerWidth,canvasEl.height=2*window.innerHeight,canvasEl.style.width=window.innerWidth+"px",canvasEl.style.height=window.innerHeight+"px",canvasEl.getContext("2d").scale(2,2)},500),render=anime({duration:1/0,update:function(){ctx.clearRect(0,0,canvasEl.width,canvasEl.height)}});document.addEventListener(tap,function(e){"sidebar"!==e.target.id&&"toggle-sidebar"!==e.target.id&&"A"!==e.target.nodeName&&"IMG"!==e.target.nodeName&&(render.play(),updateCoords(e),animateParticules(pointerX,pointerY))},!1),setCanvasSize(),window.addEventListener("resize",setCanvasSize,!1)};</span><br></pre></td></tr></table></figure><p>然后在 <font color="#FF0000">\themes\material-x\layout\layout.ejs</font> 文件中写入以下代码:</p><figure class="highlight plain"><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><span class="line"><canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> </span><br><span class="line"><script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> </span><br><span class="line"><script type="text/javascript" src="/js/fireworks.js"></script></span><br></pre></td></tr></table></figure><p>最终效果:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk1MjFlZDhlMy5naWY" alt></p><hr><h1><a name="t5"></a><a name="t5"></a><a id="font_colorFF000005_font_140"></a><font color="#FF0000">【05】自定义鼠标指针样式 </font></h1><p>在 <font color="#FF0000"> \themes\material-x\source\less\_base.less</font> 文件 body 样式里写入如下代码:</p><figure class="highlight plain"><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><span class="line">body {</span><br><span class="line"> cursor: url(https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.1.6/images/mouse.cur),auto;</span><br><span class="line"> background-color: @theme_background;</span><br><span class="line"> ......</span><br><span class="line"> ......</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>鼠标指针可以用 Axialis CursorWorkshop 这个软件自己制作,不同主题具体放的文件有所不同,确保在博客主体 body 的 CSS 文件中即可,其中的鼠标指针链接可替换成自己的,首先尝试加载<code>https://cdn.jsdelivr.net/gh/TRHX/CDN-for-itrhx.com@2.1.6/images/mouse.cur</code> ,如果该文件不存在或由于其他原因无效,那么 auto 会被使用,也就是自动默认效果,图片格式为.ico、.ani、.cur,建议使用.cur,如果使用.ani或者其他格式无效,原因是浏览器兼容问题,请阅读<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Basic_User_Interface/Using_URL_values_for_the_cursor_property" rel="noopener" target="_blank">参考文档</a>或者参考以下兼容表:</p><div class="table-box"><table><thead><tr><th align="center">浏览器</th><th align="center">最低版本</th><th align="center">格式</th></tr></thead><tbody><tr><td align="center">Internet Explorer</td><td align="center">6.0</td><td align="center">.cur / .ani</td></tr><tr><td align="center">Firefox (Gecko), Windows and Linux</td><td align="center">1.5 (1.8)</td><td align="center">.cur / .png / .gif / .jpg</td></tr><tr><td align="center">Firefox (Gecko)</td><td align="center">4.0 (2.0)</td><td align="center">.cur / .png / .gif / .jpg / .svg</td></tr><tr><td align="center">Opera</td><td align="center">—</td><td align="center">—</td></tr><tr><td align="center">Safari (Webkit)</td><td align="center">3.0 (522-523)</td><td align="center">.cur / .png / .gif / .jpg</td></tr></tbody></table></div><p>拓展阅读:<a href="https://blog.csdn.net/ixygj197875/article/details/79338360" rel="noopener" target="_blank">《CSS 鼠标样式 cursor属性》</a> (By:歪脖先生的博客)</p><hr><h1><a name="t6"></a><a name="t6"></a><a id="font_colorFF000006_font_163"></a><font color="#FF0000">【06】添加彩色滚动变换字体 </font></h1><p>在你想要添加彩色滚动变换字体的地方写入以下代码即可,其中文字可自行更改:</p><figure class="highlight plain"><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><span class="line"><div id="binft"></div></span><br><span class="line"> <script></span><br><span class="line"> var binft = function (r) {</span><br><span class="line"> function t() {</span><br><span class="line"> return b[Math.floor(Math.random() * b.length)]</span><br><span class="line"> } </span><br><span class="line"> function e() {</span><br><span class="line"> return String.fromCharCode(94 * Math.random() + 33)</span><br><span class="line"> }</span><br><span class="line"> function n(r) {</span><br><span class="line"> for (var n = document.createDocumentFragment(), i = 0; r > i; i++) {</span><br><span class="line"> var l = document.createElement("span");</span><br><span class="line"> l.textContent = e(), l.style.color = t(), n.appendChild(l)</span><br><span class="line"> }</span><br><span class="line"> return n</span><br><span class="line"> }</span><br><span class="line"> function i() {</span><br><span class="line"> var t = o[c.skillI];</span><br><span class="line"> c.step ? c.step-- : (c.step = g, c.prefixP < l.length ? (c.prefixP >= 0 && (c.text += l[c.prefixP]), c.prefixP++) : "forward" === c.direction ? c.skillP < t.length ? (c.text += t[c.skillP], c.skillP++) : c.delay ? c.delay-- : (c.direction = "backward", c.delay = a) : c.skillP > 0 ? (c.text = c.text.slice(0, -1), c.skillP--) : (c.skillI = (c.skillI + 1) % o.length, c.direction = "forward")), r.textContent = c.text, r.appendChild(n(c.prefixP < l.length ? Math.min(s, s + c.prefixP) : Math.min(s, t.length - c.skillP))), setTimeout(i, d)</span><br><span class="line"> }</span><br><span class="line"> var l = "",</span><br><span class="line"> o = ["青青陵上柏,磊磊涧中石。", "人生天地间,忽如远行客。","斗酒相娱乐,聊厚不为薄。", "驱车策驽马,游戏宛与洛。","洛中何郁郁,冠带自相索。","长衢罗夹巷,王侯多第宅。","两宫遥相望,双阙百余尺。","极宴娱心意,戚戚何所迫?"].map(function (r) {</span><br><span class="line"> return r + ""</span><br><span class="line"> }),</span><br><span class="line"> a = 2,</span><br><span class="line"> g = 1,</span><br><span class="line"> s = 5,</span><br><span class="line"> d = 75,</span><br><span class="line"> b = ["rgb(110,64,170)", "rgb(150,61,179)", "rgb(191,60,175)", "rgb(228,65,157)", "rgb(254,75,131)", "rgb(255,94,99)", "rgb(255,120,71)", "rgb(251,150,51)", "rgb(226,183,47)", "rgb(198,214,60)", "rgb(175,240,91)", "rgb(127,246,88)", "rgb(82,246,103)", "rgb(48,239,130)", "rgb(29,223,163)", "rgb(26,199,194)", "rgb(35,171,216)", "rgb(54,140,225)", "rgb(76,110,219)", "rgb(96,84,200)"],</span><br><span class="line"> c = {</span><br><span class="line"> text: "",</span><br><span class="line"> prefixP: -s,</span><br><span class="line"> skillI: 0,</span><br><span class="line"> skillP: 0,</span><br><span class="line"> direction: "forward",</span><br><span class="line"> delay: a,</span><br><span class="line"> step: g</span><br><span class="line"> };</span><br><span class="line"> i()</span><br><span class="line"> };</span><br><span class="line"> binft(document.getElementById('binft'));</span><br><span class="line"> </script></span><br></pre></td></tr></table></figure><p>最终效果:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk1N2RiZmE0MC5naWY" alt></p><hr><h1><a name="t7"></a><a name="t7"></a><a id="font_colorFF000007_font_213"></a><font color="#FF0000">【07】添加背景音乐 </font></h1><p>打开网页版<a href="https://music.163.com/" rel="noopener" target="_blank">网易云音乐</a>,选择你准备添加的背景音乐,点击生成外链播放器,前提是要有版权,不然是无法生成外链播放器的,复制底下的HTML代码<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk3MDkzY2I1NS5wbmc" alt><br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk3MzY2MmUzZS5wbmc" alt></p><p>然后将此代码放到你想要放的地方,比如放在博客的左侧,则打开 <font color="#FF0000">\themes\hexo-theme-spfk\layout\_partial\left-col.ejs</font> 文件,将复制的HTML代码粘贴进去,再进行适当的位置设置让播放器更美观,其中 <font color="#FF0000">auto=1</font> 表示打开网页自动播放音乐,<font color="#FF0000">auto=0</font> 表示关闭自动播放音乐<br><img src="https://img-blog.csdnimg.cn/20190807102132566.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p>最后效果如下:<br><p><img src="https://img-blog.csdnimg.cn/20191214201057369.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p>这种网易云音乐外链的方式有很多局限性,因此推荐使用<font color="#FF0000">aplayer</font>,GitHub地址为:<a href="https://github.com/MoePlayer/APlayer" target="_blank" rel="noopener">https://github.com/MoePlayer/APlayer</a> ,参考教程:<a href="https://blog.yleao.com/2018/0902/hexo%E4%B8%8A%E7%9A%84aplayer%E5%BA%94%E7%94%A8.html" rel="noopener" target="_blank">《hexo上的aplayer应用》</a></p><p></p><hr><h1><a name="t8"></a><a name="t8"></a><a id="font_colorFF000008_font_227"></a><font color="#FF0000">【08】浏览器网页标题恶搞 </font></h1><p>当用户访问你的博客时点击到了其他网页,我们可以恶搞一下网页标题,呼唤用户回来,首先在目录 <font color="#FF0000">\Hexo\themes\hexo-theme-spfk\source\js</font> 下新建一个 <font color="#FF0000">FunnyTitle.js</font> 文件,在里面填写如下代码:</p><pre class="prettyprint"><code class="prism language-javascript has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token operator"><</span><span class="token operator">!</span><span class="token operator">--</span>浏览器搞笑标题<span class="token operator">--</span><span class="token operator">></span> <span class="token keyword">var</span> OriginTitle <span class="token operator">=</span> document<span class="token punctuation">.</span>title<span class="token punctuation">;</span> <span class="token keyword">var</span> titleTime<span class="token punctuation">;</span> document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'visibilitychange'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>document<span class="token punctuation">.</span>hidden<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'[rel="icon"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'href'</span><span class="token punctuation">,</span> <span class="token string">"/img/trhx2.png"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> document<span class="token punctuation">.</span>title <span class="token operator">=</span> <span class="token string">'ヽ(●-`Д´-)ノ你丑你就走!'</span><span class="token punctuation">;</span> <span class="token function">clearTimeout</span><span class="token punctuation">(</span>titleTime<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'[rel="icon"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'href'</span><span class="token punctuation">,</span> <span class="token string">"/img/trhx2.png"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> document<span class="token punctuation">.</span>title <span class="token operator">=</span> <span class="token string">'ヾ(Ő∀Ő3)ノ你帅就回来!'</span> <span class="token operator">+</span> OriginTitle<span class="token punctuation">;</span> titleTime <span class="token operator">=</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> document<span class="token punctuation">.</span>title <span class="token operator">=</span> OriginTitle<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">2000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>然后在 <font color="#FF0000">\Hexo\themes\hexo-theme-spfk\layout\layout.ejs</font> 文件中添加如下代码:</p><pre class="prettyprint"><code class="prism language-xml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token comment"><!--浏览器搞笑标题--></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>\js\FunnyTitle.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre><p>再次部署博客后就可以看见标题搞笑的效果了:</p><p><img src="https://img-blog.csdnimg.cn/20190807102300267.png" alt="在这里插入图片描述"><br><img src="https://img-blog.csdnimg.cn/20190807102307306.png" alt="在这里插入图片描述"></p><hr><h1><a name="t9"></a><a name="t9"></a><a id="font_colorFF000009_font_262"></a><font color="#FF0000">【09】背景添加动态线条效果 </font></h1><p>在 <font color="#FF0000">\Hexo\themes\hexo-theme-spfk\layout\layout.ejs</font> 文件中添加如下代码:</p><pre class="prettyprint"><code class="prism language-xml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token comment"><!--动态线条背景--></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span><span class="token attr-name">color</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>220,220,220<span class="token punctuation">"</span></span> <span class="token attr-name">opacity</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">'</span>0.7<span class="token punctuation">'</span></span> <span class="token attr-name">zIndex</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>-2<span class="token punctuation">"</span></span> <span class="token attr-name">count</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>200<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>//cdn.bootcss.com/canvas-nest.js/1.0.0/canvas-nest.min.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre><p>其中:</p><ul><li>color:表示线条颜色,三个数字分别为(R,G,B),默认:(0,0,0)</li><li>opacity:表示线条透明度(0~1),默认:0.5</li><li>count:表示线条的总数量,默认:150</li><li>zIndex:表示背景的z-index属性,css属性用于控制所在层的位置,默认:-1</li></ul><hr><h1><a name="t10"></a><a name="t10"></a><a id="font_colorFF000010_font_282"></a><font color="#FF0000">【10】添加人体时钟等有趣的挂件 </font></h1><p>无意中发现了个有趣的人体时钟 HONE HONE CLOCK,作者是个日本人,<a href="http://chabudai.org/blog/" rel="noopener" target="_blank">点击此处</a>访问作者博客,<a href="http://chabudai.org/blog/?p=59" rel="noopener" target="_blank">点击此处</a>在作者原博客上查看动态样式,<a href="http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_tr.swf" rel="noopener" target="_blank">点击此处</a>查看动态大图,如果你的博客上有合适的地方,加上一个人体时钟会很有趣的<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk5YTg3NWVlNS5wbmc" alt></p><p>实现代码:</p><figure class="highlight plain"><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><span class="line"><!--人体时钟背景透明--></span><br><span class="line"><script charset="Shift_JIS" src="http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_tr.js"></script></span><br><span class="line"><!–人体时钟背景白–></span><br><span class="line"><script charset=“Shift_JIS” src=“http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_wh.js”></script></span><br></pre></td></tr></table></figure><p>其他网页小挂件推荐:</p><ul><li>http://abowman.com/ 里面有很多有趣的小挂件,可以养养鱼、龟、狗、仓鼠等各式各样的虚拟宠物,能根据你的鼠标指针位置移动,直接复制代码就可以用</li><li>http://www.revolvermaps.com/ 它提供网站访客地理信息,可以以2D、3D等形式显示</li><li>http://www.amazingcounters.com/ 免费网站计数器,有非常多的样式供你选择,可以设置计数器初始数值,可以设置按访问量计数,也可以按独立访问者计数</li><li>https://www.seniverse.com/widget/get 心知天气提供基于Web的免费天气插件,可以为你的网站添加一项简洁美观的天气预报功能,并自动适配PC和手机上的浏览</li></ul><hr><h1><a name="t11"></a><a name="t11"></a><a id="font_colorFF000011_font_303"></a><font color="#FF0000">【11】添加网站雪花飘落效果 </font></h1><p><img src="https://img-blog.csdnimg.cn/20191214201330112.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p>实现方法:在 <font color="#FF0000">\Hexo\themes\hexo-theme-spfk\source\js</font> 目录下新建一个 <font color="#FF0000">snow.js</font> 文件,粘贴以下代码:</p><figure class="highlight plain"><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><span class="line">/*样式一*/</span><br><span class="line">(function($){</span><br><span class="line">$.fn.snow = function(options){</span><br><span class="line">var $flake = $('<div id="snowbox" />').css({'position': 'absolute','z-index':'9999', 'top': '-50px'}).html('&#10052;'),</span><br><span class="line">documentHeight = $(document).height(),</span><br><span class="line">documentWidth= $(document).width(),</span><br><span class="line">defaults = {</span><br><span class="line">minSize: 10,</span><br><span class="line">maxSize: 20,</span><br><span class="line">newOn: 1000,</span><br><span class="line">flakeColor: "#AFDAEF" /* 此处可以定义雪花颜色,若要白色可以改为#FFFFFF */</span><br><span class="line">},</span><br><span class="line">options= $.extend({}, defaults, options);</span><br><span class="line">var interval= setInterval( function(){</span><br><span class="line">var startPositionLeft = Math.random() * documentWidth - 100,</span><br><span class="line">startOpacity = 0.5 + Math.random(),</span><br><span class="line">sizeFlake = options.minSize + Math.random() * options.maxSize,</span><br><span class="line">endPositionTop = documentHeight - 200,</span><br><span class="line">endPositionLeft = startPositionLeft - 500 + Math.random() * 500,</span><br><span class="line">durationFall = documentHeight * 10 + Math.random() * 5000;</span><br><span class="line">$flake.clone().appendTo('body').css({</span><br><span class="line">left: startPositionLeft,</span><br><span class="line">opacity: startOpacity,</span><br><span class="line">'font-size': sizeFlake,</span><br><span class="line">color: options.flakeColor</span><br><span class="line">}).animate({</span><br><span class="line">top: endPositionTop,</span><br><span class="line">left: endPositionLeft,</span><br><span class="line">opacity: 0.2</span><br><span class="line">},durationFall,'linear',function(){</span><br><span class="line">$(this).remove()</span><br><span class="line">});</span><br><span class="line">}, options.newOn);</span><br><span class="line"> };</span><br><span class="line">})(jQuery);</span><br><span class="line">$(function(){</span><br><span class="line"> $.fn.snow({ </span><br><span class="line"> minSize: 5, /* 定义雪花最小尺寸 */</span><br><span class="line"> maxSize: 50,/* 定义雪花最大尺寸 */</span><br><span class="line"> newOn: 300 /* 定义密集程度,数字越小越密集 */</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br></pre></td><td class="code"><pre><span class="line">/*样式二*/</span><br><span class="line">/* 控制下雪 */</span><br><span class="line">function snowFall(snow) {</span><br><span class="line"> /* 可配置属性 */</span><br><span class="line"> snow = snow || {};</span><br><span class="line"> this.maxFlake = snow.maxFlake || 200; /* 最多片数 */</span><br><span class="line"> this.flakeSize = snow.flakeSize || 10; /* 雪花形状 */</span><br><span class="line"> this.fallSpeed = snow.fallSpeed || 1; /* 坠落速度 */</span><br><span class="line">}</span><br><span class="line">/* 兼容写法 */</span><br><span class="line">requestAnimationFrame = window.requestAnimationFrame ||</span><br><span class="line"> window.mozRequestAnimationFrame ||</span><br><span class="line"> window.webkitRequestAnimationFrame ||</span><br><span class="line"> window.msRequestAnimationFrame ||</span><br><span class="line"> window.oRequestAnimationFrame ||</span><br><span class="line"> function(callback) { setTimeout(callback, 1000 / 60); };</span><br><span class="line"></span><br><span class="line">cancelAnimationFrame = window.cancelAnimationFrame ||</span><br><span class="line"> window.mozCancelAnimationFrame ||</span><br><span class="line"> window.webkitCancelAnimationFrame ||</span><br><span class="line"> window.msCancelAnimationFrame ||</span><br><span class="line">window.oCancelAnimationFrame;</span><br><span class="line">/* 开始下雪 */</span><br><span class="line">snowFall.prototype.start = function(){</span><br><span class="line"> /* 创建画布 */</span><br><span class="line"> snowCanvas.apply(this);</span><br><span class="line"> /* 创建雪花形状 */</span><br><span class="line"> createFlakes.apply(this);</span><br><span class="line"> /* 画雪 */</span><br><span class="line"> drawSnow.apply(this)</span><br><span class="line">}</span><br><span class="line">/* 创建画布 */</span><br><span class="line">function snowCanvas() {</span><br><span class="line"> /* 添加Dom结点 */</span><br><span class="line"> var snowcanvas = document.createElement("canvas");</span><br><span class="line"> snowcanvas.id = "snowfall";</span><br><span class="line"> snowcanvas.width = window.innerWidth;</span><br><span class="line"> snowcanvas.height = document.body.clientHeight;</span><br><span class="line"> snowcanvas.setAttribute("style", "position:absolute; top: 0; left: 0; z-index: 1; pointer-events: none;");</span><br><span class="line"> document.getElementsByTagName("body")[0].appendChild(snowcanvas);</span><br><span class="line"> this.canvas = snowcanvas;</span><br><span class="line"> this.ctx = snowcanvas.getContext("2d");</span><br><span class="line"> /* 窗口大小改变的处理 */</span><br><span class="line"> window.onresize = function() {</span><br><span class="line"> snowcanvas.width = window.innerWidth;</span><br><span class="line"> /* snowcanvas.height = window.innerHeight */</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">/* 雪运动对象 */</span><br><span class="line">function flakeMove(canvasWidth, canvasHeight, flakeSize, fallSpeed) {</span><br><span class="line"> this.x = Math.floor(Math.random() * canvasWidth); /* x坐标 */</span><br><span class="line"> this.y = Math.floor(Math.random() * canvasHeight); /* y坐标 */</span><br><span class="line"> this.size = Math.random() * flakeSize + 2; /* 形状 */</span><br><span class="line"> this.maxSize = flakeSize; /* 最大形状 */</span><br><span class="line"> this.speed = Math.random() * 1 + fallSpeed; /* 坠落速度 */</span><br><span class="line"> this.fallSpeed = fallSpeed; /* 坠落速度 */</span><br><span class="line"> this.velY = this.speed; /* Y方向速度 */</span><br><span class="line"> this.velX = 0; /* X方向速度 */</span><br><span class="line"> this.stepSize = Math.random() / 30; /* 步长 */</span><br><span class="line"> this.step = 0 /* 步数 */</span><br><span class="line">}</span><br><span class="line">flakeMove.prototype.update = function() {</span><br><span class="line"> var x = this.x,</span><br><span class="line"> y = this.y;</span><br><span class="line"> /* 左右摆动(余弦) */</span><br><span class="line"> this.velX *= 0.98;</span><br><span class="line"> if (this.velY <= this.speed) {</span><br><span class="line"> this.velY = this.speed</span><br><span class="line"> }</span><br><span class="line"> this.velX += Math.cos(this.step += .05) * this.stepSize;</span><br><span class="line"></span><br><span class="line"> this.y += this.velY;</span><br><span class="line"> this.x += this.velX;</span><br><span class="line"> /* 飞出边界的处理 */</span><br><span class="line"> if (this.x >= canvas.width || this.x <= 0 || this.y >= canvas.height || this.y <= 0) {</span><br><span class="line"> this.reset(canvas.width, canvas.height)</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line">/* 飞出边界-放置最顶端继续坠落 */</span><br><span class="line">flakeMove.prototype.reset = function(width, height) {</span><br><span class="line"> this.x = Math.floor(Math.random() * width);</span><br><span class="line"> this.y = 0;</span><br><span class="line"> this.size = Math.random() * this.maxSize + 2;</span><br><span class="line"> this.speed = Math.random() * 1 + this.fallSpeed;</span><br><span class="line"> this.velY = this.speed;</span><br><span class="line"> this.velX = 0;</span><br><span class="line">};</span><br><span class="line">// 渲染雪花-随机形状(此处可修改雪花颜色!!!)</span><br><span class="line">flakeMove.prototype.render = function(ctx) {</span><br><span class="line"> var snowFlake = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.size);</span><br><span class="line"> snowFlake.addColorStop(0, "rgba(255, 255, 255, 0.9)"); /* 此处是雪花颜色,默认是白色 */</span><br><span class="line"> snowFlake.addColorStop(.5, "rgba(255, 255, 255, 0.5)"); /* 若要改为其他颜色,请自行查 */</span><br><span class="line"> snowFlake.addColorStop(1, "rgba(255, 255, 255, 0)"); /* 找16进制的RGB 颜色代码。 */</span><br><span class="line"> ctx.save();</span><br><span class="line"> ctx.fillStyle = snowFlake;</span><br><span class="line"> ctx.beginPath();</span><br><span class="line"> ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);</span><br><span class="line"> ctx.fill();</span><br><span class="line"> ctx.restore();</span><br><span class="line">};</span><br><span class="line">/* 创建雪花-定义形状 */</span><br><span class="line">function createFlakes() {</span><br><span class="line"> var maxFlake = this.maxFlake,</span><br><span class="line"> flakes = this.flakes = [],</span><br><span class="line"> canvas = this.canvas;</span><br><span class="line"> for (var i = 0; i < maxFlake; i++) {</span><br><span class="line"> flakes.push(new flakeMove(canvas.width, canvas.height, this.flakeSize, this.fallSpeed))</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">/* 画雪 */</span><br><span class="line">function drawSnow() {</span><br><span class="line"> var maxFlake = this.maxFlake,</span><br><span class="line"> flakes = this.flakes;</span><br><span class="line"> ctx = this.ctx, canvas = this.canvas, that = this;</span><br><span class="line"> /* 清空雪花 */</span><br><span class="line"> ctx.clearRect(0, 0, canvas.width, canvas.height);</span><br><span class="line"> for (var e = 0; e < maxFlake; e++) {</span><br><span class="line"> flakes[e].update();</span><br><span class="line"> flakes[e].render(ctx);</span><br><span class="line"> }</span><br><span class="line"> /* 一帧一帧的画 */</span><br><span class="line"> this.loop = requestAnimationFrame(function() {</span><br><span class="line"> drawSnow.apply(that);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line">/* 调用及控制方法 */</span><br><span class="line">var snow = new snowFall({maxFlake:60});</span><br><span class="line">snow.start();</span><br></pre></td></tr></table></figure><p>然后在 <font color="#FF0000">\Hexo\themes\hexo-theme-spfk\layout\layout.ejs</font> 文件里引用即可:</p><pre class="prettyprint"><code class="prism language-xml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token comment"><!-- 雪花特效 --></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>\js\snow.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span>/pre><p>如果没效果,请确认网页是否已载入JQurey,如果没有请在下雪代码之前引入JQ即可:</p><pre class="prettyprint"><code class="prism language-xml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://libs.baidu.com/jquery/1.8.3/jquery.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://libs.baidu.com/jquery/1.8.3/jquery.min.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre><p>原文链接:<a href="https://ihuan.me/2172.html" rel="noopener" target="_blank">《分享两种圣诞节雪花特效JS代码(网站下雪效果)》</a></p><hr><h1><a name="t12"></a><a name="t12"></a><a id="font_colorFF000012_font_500"></a><font color="#FF0000">【12】添加背景动态彩带效果 </font></h1><p>样式一是鼠标点击后彩带自动更换样式,样式二是飘动的彩带:<br><img src="https://img-blog.csdnimg.cn/20190807103419221.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p>实现方法:在 <font color="#FF0000">\themes\material-x\layout\layout.ejs</font> 文件的<font color="#FF0000">body</font>前面添加如下代码:</p><pre class="prettyprint"><code class="has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><!-- 样式一(鼠标点击更换样式) --><script src="https://g.joyinshare.com/hc/ribbon.min.js" type="text/javascript"></script></code></pre><pre class="prettyprint"><code class="has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><!-- 样式二(飘动的彩带) --><script src="https://g.joyinshare.com/hc/piao.js" type="text/javascript"></script></code></pre><hr><h1><a name="t13"></a><a name="t13"></a><a id="font_colorFF000013_font_515"></a><font color="#FF0000">【13】添加背景代码雨特效 </font></h1><p>新建 <code>DigitalRain.js</code>,写入以下代码:</p><pre class="prettyprint"><code class="prism language-js has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;">window<span class="token punctuation">.</span><span class="token function-variable function">onload</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">//获取画布对象</span> <span class="token keyword">var</span> canvas <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"canvas"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//获取画布的上下文</span> <span class="token keyword">var</span> context <span class="token operator">=</span>canvas<span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token string">"2d"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">var</span> s <span class="token operator">=</span> window<span class="token punctuation">.</span>screen<span class="token punctuation">;</span> <span class="token keyword">var</span> <span class="token constant">W</span> <span class="token operator">=</span> canvas<span class="token punctuation">.</span>width <span class="token operator">=</span> s<span class="token punctuation">.</span>width<span class="token punctuation">;</span> <span class="token keyword">var</span> <span class="token constant">H</span> <span class="token operator">=</span> canvas<span class="token punctuation">.</span>height<span class="token punctuation">;</span> <span class="token comment">//获取浏览器屏幕的宽度和高度</span> <span class="token comment">//var W = window.innerWidth;</span> <span class="token comment">//var H = window.innerHeight;</span> <span class="token comment">//设置canvas的宽度和高度</span> canvas<span class="token punctuation">.</span>width <span class="token operator">=</span> <span class="token constant">W</span><span class="token punctuation">;</span> canvas<span class="token punctuation">.</span>height <span class="token operator">=</span> <span class="token constant">H</span><span class="token punctuation">;</span> <span class="token comment">//每个文字的字体大小</span> <span class="token keyword">var</span> fontSize <span class="token operator">=</span> <span class="token number">12</span><span class="token punctuation">;</span> <span class="token comment">//计算列</span> <span class="token keyword">var</span> colunms <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span><span class="token constant">W</span> <span class="token operator">/</span>fontSize<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//记录每列文字的y轴坐标</span> <span class="token keyword">var</span> drops <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">//给每一个文字初始化一个起始点的位置</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator"><</span>colunms<span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> drops<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">//运动的文字</span> <span class="token keyword">var</span> str <span class="token operator">=</span><span class="token string">"WELCOME TO WWW.ITRHX.COM"</span><span class="token punctuation">;</span> <span class="token comment">//4:fillText(str,x,y);原理就是去更改y的坐标位置</span> <span class="token comment">//绘画的函数</span> <span class="token keyword">function</span> <span class="token function">draw</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> context<span class="token punctuation">.</span>fillStyle <span class="token operator">=</span> <span class="token string">"rgba(238,238,238,.08)"</span><span class="token punctuation">;</span><span class="token comment">//遮盖层</span> context<span class="token punctuation">.</span><span class="token function">fillRect</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token constant">W</span><span class="token punctuation">,</span><span class="token constant">H</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//给字体设置样式</span> context<span class="token punctuation">.</span>font <span class="token operator">=</span> <span class="token string">"600 "</span><span class="token operator">+</span>fontSize<span class="token operator">+</span><span class="token string">"px Georgia"</span><span class="token punctuation">;</span> <span class="token comment">//给字体添加颜色</span> context<span class="token punctuation">.</span>fillStyle <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"#33B5E5"</span><span class="token punctuation">,</span> <span class="token string">"#0099CC"</span><span class="token punctuation">,</span> <span class="token string">"#AA66CC"</span><span class="token punctuation">,</span> <span class="token string">"#9933CC"</span><span class="token punctuation">,</span> <span class="token string">"#99CC00"</span><span class="token punctuation">,</span> <span class="token string">"#669900"</span><span class="token punctuation">,</span> <span class="token string">"#FFBB33"</span><span class="token punctuation">,</span> <span class="token string">"#FF8800"</span><span class="token punctuation">,</span> <span class="token string">"#FF4444"</span><span class="token punctuation">,</span> <span class="token string">"#CC0000"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token function">parseInt</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token comment">//randColor();可以rgb,hsl, 标准色,十六进制颜色</span> <span class="token comment">//写入画布中</span> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span>i<span class="token operator"><</span>colunms<span class="token punctuation">;</span>i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">var</span> index <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> str<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">var</span> x <span class="token operator">=</span> i<span class="token operator">*</span>fontSize<span class="token punctuation">;</span> <span class="token keyword">var</span> y <span class="token operator">=</span> drops<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">*</span>fontSize<span class="token punctuation">;</span> context<span class="token punctuation">.</span><span class="token function">fillText</span><span class="token punctuation">(</span>str<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">,</span>x<span class="token punctuation">,</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//如果要改变时间,肯定就是改变每次他的起点</span> <span class="token keyword">if</span><span class="token punctuation">(</span>y <span class="token operator">>=</span> canvas<span class="token punctuation">.</span>height <span class="token operator">&&</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0.99</span><span class="token punctuation">)</span><span class="token punctuation">{</span> drops<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> drops<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token operator">++</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">randColor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token comment">//随机颜色</span> <span class="token keyword">var</span> r <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">var</span> g <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">var</span> b <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token string">"rgb("</span><span class="token operator">+</span>r<span class="token operator">+</span><span class="token string">","</span><span class="token operator">+</span>g<span class="token operator">+</span><span class="token string">","</span><span class="token operator">+</span>b<span class="token operator">+</span><span class="token string">")"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">draw</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">setInterval</span><span class="token punctuation">(</span>draw<span class="token punctuation">,</span><span class="token number">35</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre><p>在主题文件的相关css文件中(以 <font color="#FF0000">Material X 1.2.1</font> 主题为例,在<font color="#FF0000">\themes\material-x-1.2.1\source\less\_main.less</font> 文件末尾)添加以下代码:</p><pre class="prettyprint"><code class="prism language-css has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token selector">canvas</span> <span class="token punctuation">{</span> <span class="token property">position</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span> <span class="token property">right</span><span class="token punctuation">:</span> 0px<span class="token punctuation">;</span> <span class="token property">bottom</span><span class="token punctuation">:</span> 0px<span class="token punctuation">;</span> <span class="token property">min-width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">min-height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token property">height</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token property">width</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token property">z-index</span><span class="token punctuation">:</span> -1<span class="token punctuation">;</span><span class="token punctuation">}</span></code></pre><p>然后在主题的 <font color="#FF0000">layout.ejs</font> 文件中引入即可:</p><pre class="prettyprint"><code class="prism language-html has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"> <span class="token comment"><!-- 数字雨 --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>canvas</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>canvas<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>1440<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>900<span class="token punctuation">"</span></span> <span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>canvas</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text/javascript<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/js/DigitalRain.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span></code></pre><p>最终效果:<br><img src="https://img-blog.csdnimg.cn/20190807102432525.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>代码来源:http://www.lxl8800.cn/Main/Resource</p><hr><h1><a name="t14"></a><a name="t14"></a><a id="font_colorFF0000__font_600"></a><font color="#FF0000"> 未完待续… </font></h1><hr></code></pre></code></pre></code></pre></code></pre>]]></content>
<summary type="html">
<p></p><div class="toc"><h3><a name="t0"></a><a name="t0"></a>文章目录</h3><ul><li><a href="#font_colorFF00001_font_20" rel="nofollow" target="_self"><font color="#FF000">【01】添加卡通人物(看板娘) </font></a></li><li><a href="#font_colorFF000002_font_56" rel="nofollow" target="_self"><font color="#FF0000">【02】添加鼠标点击爱心效果 </font></a></li><li><a href="#font_colorFF000003_font_74" rel="nofollow" target="_self"><font color="#FF0000">【03】添加鼠标点击显示字体效果 </font></a></li><li><a href="#font_colorFF000004_font_123" rel="nofollow" target="_self"><font color="#FF0000">【04】添加鼠标点击烟花爆炸效果 </font></a></li><li><a href="#font_colorFF000005_font_140" rel="nofollow" target="_self"><font color="#FF0000">【05】自定义鼠标指针样式 </font></a></li>
</summary>
<category term="Hexo" scheme="ayjcsgm.github.io/tags/Hexo/"/>
</entry>
<entry>
<title>在Hexo 个人博客上添加实用功能</title>
<link href="ayjcsgm.github.io/2019/12/14/%E5%9C%A8Hexo-%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E4%B8%8A%E6%B7%BB%E5%8A%A0%E5%AE%9E%E7%94%A8%E5%8A%9F%E8%83%BD/"/>
<id>ayjcsgm.github.io/2019/12/14/%E5%9C%A8Hexo-%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%E4%B8%8A%E6%B7%BB%E5%8A%A0%E5%AE%9E%E7%94%A8%E5%8A%9F%E8%83%BD/</id>
<published>2019-12-14T12:32:15.000Z</published>
<updated>2019-12-14T12:45:44.271Z</updated>
<content type="html"><![CDATA[<p></p><div class="toc"><h3><a name="t0"></a><a name="t0"></a>文章目录</h3><ul><li><a href="#font_colorFF000001_font_18" rel="nofollow" target="_self"><font color="#FF0000">【01】添加评论系统 </font></a></li><li><a href="#font_colorFF000002_font_66" rel="nofollow" target="_self"><font color="#FF0000">【02】添加字数统计和阅读时长 </font></a></li><li><a href="#font_colorFF000003_font_168" rel="nofollow" target="_self"><font color="#FF0000">【03】添加网站运行时间 </font></a></li><li><a href="#font_colorFF000004_font_197" rel="nofollow" target="_self"><font color="#FF0000">【04】添加百度统计 </font></a></li><li><a href="#font_colorFF000005RSS_font_217" rel="nofollow" target="_self"><font color="#FF0000">【05】添加RSS订阅 </font></a></li><a id="more"></a><li><a href="#font_colorFF000006_Fork_me_on_GitHub__font_249" rel="nofollow" target="_self"><font color="#FF0000">【06】添加 Fork me on GitHub 效果 </font></a></li><li><a href="#font_colorFF000007_font_257" rel="nofollow" target="_self"><font color="#FF0000">【07】更改本地预览端口号 </font></a></li><li><a href="#font_colorFF000008_HTTPS_font_275" rel="nofollow" target="_self"><font color="#FF0000"><hr><h1><a name="t1"></a><a name="t1"></a><a id="font_colorFF000001_font_18"></a><font color="#FF0000">【01】添加评论系统 </font></h1><p>主流的评论系统有很多,比如:网易云跟帖、多说、友言、畅言、来必力(LiveRe)、Disqus、Valine、Gitalk 等等,目前网易云跟帖、多说、友言都已经关闭了,还有些可能国内还访问不了,比较麻烦,百度了一下,最后还是选择了来必力评论系统</p><p>进入<a href="https://livere.com" rel="noopener" target="_blank">来必力官网</a>,注册一个账号:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzAvNWM5ZjkxZWQ4OTczYS5wbmc" alt></p><p>注册完毕之后,登录,进入安装页面,选择 City 免费版安装,安装之后你会得到一段代码<br><img src="https://img-blog.csdnimg.cn/20190807103722227.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p><img src="https://img-blog.csdnimg.cn/20190807103745470.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5ZjkyOWU3ZDYyYi5wbmc" alt></p><p>我们打开主题文件下的 <font color="#FF0000">_config.yml</font> 文件,添加如下代码:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5ZjkyYjVlMjcwZi5wbmc" alt></p><p>在 <font color="#FF0000">\themes\hexo-theme-spfk\layout\_partial\comments</font> 文件夹下新建一个 <font color="#FF0000">livere.ejs</font> 的文件,在里面填写来必力提供的代码:</p><figure class="highlight plain"><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><span class="line"><!-- 来必力City版安装代码 --></span><br><span class="line"><div id="lv-container" data-id="city" data-uid="这里是你的uid"></span><br><span class="line"><script type="text/javascript"></span><br><span class="line">(function(d, s) {</span><br><span class="line"> var j, e = d.getElementsByTagName(s)[0];</span><br><span class="line"> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> LivereTower <span class="token operator">===</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></span><br><span class="line"></span><br><span class="line"> j <span class="token operator">=</span> d<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span>s<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="line"> j<span class="token punctuation">.</span>src <span class="token operator">=</span> <span class="token string">'https://cdn-city.livere.com/js/embed.dist.js'</span><span class="token punctuation">;</span></span><br><span class="line"> j<span class="token punctuation">.</span><span class="token keyword">async</span> <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span></span><br><span class="line"></span><br><span class="line"> e<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">insertBefore</span><span class="token punctuation">(</span>j<span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>document<span class="token punctuation">,</span> <span class="token string">'script'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="line"><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span></span><br><span class="line"><span class="token operator">&lt;</span>noscript<span class="token operator">&gt;</span>为正常使用来必力评论功能请激活JavaScript<span class="token operator">&lt;</span><span class="token operator">/</span>noscript<span class="token operator">&gt;</span></span><br><span class="line"></span><br><span class="line"></div></span><br><span class="line"><!– City版安装代码已完成 –></span><br></pre></td></tr></table></figure><p>打开 <font color="#FF0000">\themes\hexo-theme-spfk\layout\_partial\article.ejs </font>文件,在适当位置添加如下红框中的代码:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5ZjkyZTNlZWJhYS5wbmc" alt></p><p>完成以上操作之后,我们就可以使用来必力评论系统了<br><img src="https://img-blog.csdnimg.cn/20190807103817644.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>另外推荐使用 <a href="https://valine.js.org/" rel="noopener" target="_blank">Valine</a> 评论系统,和 gitalk 评论系统</p><hr><h1><a name="t2"></a><a name="t2"></a><a id="font_colorFF000002_font_66"></a><font color="#FF0000">【02】添加字数统计和阅读时长 </font></h1><p>先在博客目录下执行以下命令安装 <font color="#FF0000">hexo-wordcount</font> 插件:</p><pre class="prettyprint"><code class="prism language-shell has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;">$ <span class="token function">npm</span> i --save hexo-wordcount<p>注意:在 <a href="https://xaoxuu.com/wiki/material-x/" rel="noopener" target="_blank">Material X</a> 主题中,字数统计和阅读时长的功能我已提交 PR,在最新版本中,只需要安装插件后,在主题 <code>config.yml</code> 配置文件里,将 <code>word_count</code> 关键字设置为 <code>true</code> 即可,对于旧版本,可以通过以下方法实现:</p><p>以 <a href="https://xaoxuu.com/wiki/material-x/" rel="noopener" target="_blank">Material X</a> 主题(版本 1.2.1)为例,在 <font color="#FF0000">\themes\material-x\layout\_meta</font> 目录下创建 <font color="#FF0000">word.ejs</font> 文件,在 <font color="#FF0000">word.ejs</font> 文件中写入以下代码:</p><figure class="highlight plain"><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><span class="line"><% if(isPostList || !isPostList){ %></span><br><span class="line"> <% if (theme.word_count && !post.no_word_count) { %></span><br><span class="line"> <div style="margin-right: 10px;"></span><br><span class="line"> <span class="post-time"></span><br><span class="line"> <span class="post-meta-item-icon"></span><br><span class="line"> <i class="fa fa-keyboard"></i></span><br><span class="line"> <span class="post-meta-item-text"> 字数统计: </span></span><br><span class="line"> <span class="post-count"><%= wordcount(post.content) %>字</span></span><br><span class="line"> </span></span><br><span class="line"> </span></span><br><span class="line"> &nbsp; | &nbsp;</span><br><span class="line"> <span class="post-time"></span><br><span class="line"> <span class="post-meta-item-icon"></span><br><span class="line"> <i class="fa fa-hourglass-half"></i></span><br><span class="line"> <span class="post-meta-item-text"> 阅读时长≈</span></span><br><span class="line"> <span class="post-count"><%= min2read(post.content) %>分</span></span><br><span class="line"> </span></span><br><span class="line"> </span></span><br><span class="line"> </div></span><br><span class="line"> <% } %></span><br><span class="line"><% } %></span><br></pre></td></tr></table></figure><p>然后在主题的配置文件 <font color="#FF0000">_config.yml</font> 找到 <font color="#FF0000">meta</font> 关键字,将 <font color="#FF0000">word</font> 填入 <font color="#FF0000">header</font> 中:</p><pre class="prettyprint"><code class="prism language-bash has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;">meta: header: <span class="token punctuation">[</span>title, author, date, categories, tags, counter, word, top<span class="token punctuation">]</span> footer: <span class="token punctuation">[</span>updated, share<span class="token punctuation">]</span><p>最后在主题目录下的 <font color="#FF0000">_config.yml</font> 添加以下配置即可</p><pre class="prettyprint"><code class="prism language-yaml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token key atrule">word_count</span><span class="token punctuation">:</span> <span class="token boolean important">true</span><p>效果图:<br><img src="https://img-blog.csdnimg.cn/20191129201331969.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9pdHJoeC5ibG9nLmNzZG4ubmV0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><hr><p>同样的,以 <a href="https://github.com/luuman/hexo-theme-spfk" rel="noopener" target="_blank">spfk</a> 主题为例,在 <font color="#FF0000">\themes\hexo-theme-spfk\layout\_partial\post</font> 目录下创建 <font color="#FF0000">word.ejs</font> 文件,在 <font color="#FF0000">word.ejs</font> 文件中写入以下代码:</p><figure class="highlight plain"><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><span class="line"><div style="margin-top:10px;"></span><br><span class="line"> <span class="post-time"></span><br><span class="line"> <span class="post-meta-item-icon"></span><br><span class="line"> <i class="fa fa-keyboard-o"></i></span><br><span class="line"> <span class="post-meta-item-text"> 字数统计: </span></span><br><span class="line"> <span class="post-count"><%= wordcount(post.content) %>字</span></span><br><span class="line"> </span></span><br><span class="line"> </span></span><br><span class="line"> &nbsp; | &nbsp;</span><br><span class="line"> <span class="post-time"></span><br><span class="line"> <span class="post-meta-item-icon"></span><br><span class="line"> <i class="fa fa-hourglass-half"></i></span><br><span class="line"> <span class="post-meta-item-text"> 阅读时长: </span></span><br><span class="line"> <span class="post-count"><%= min2read(post.content) %>分</span></span><br><span class="line"> </span></span><br><span class="line"> </span></span><br><span class="line"></div></span><br></pre></td></tr></table></figure><p>然后在 <font color="#FF0000">\themes\hexo-theme-spfk\layout\_partial\article.ejs</font> 中适当位置添加以下代码:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk1ZmU0ZTM2YS5wbmc" alt></p><p>最后在主题目录下的 <font color="#FF0000">_config.yml</font> 添加以下配置即可</p><pre class="prettyprint"><code class="prism language-yaml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token key atrule">word_count</span><span class="token punctuation">:</span> <span class="token boolean important">true</span><p>如果显示的位置不好,可以自行更改其位置,成功配置后的效果如下:<br><img src="https://img-blog.csdnimg.cn/20190807104015998.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk2ODkyZTRkYi5wbmc" alt></p><hr><p>另外:要在博客底部显示所有文章的总字数,可以<a href="https://www.npmjs.com/package/hexo-wordcount" rel="noopener" target="_blank">点击此处</a>,根据你博客底部文件的类型选择相应的代码放在适当的位置即可,前提是要安装好 <font color="#FF0000">hexo-wordcount</font> 插件,例如我使用 <a href="https://xaoxuu.com/wiki/material-x/" rel="noopener" target="_blank">Material X</a> 主题,在 <font color="#FF0000">\themes\material-x\layout\_partial</font> 目录下的 <font color="#FF0000">footer.ejs</font> 文件中添加如下代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><i class="fas fa-chart-area"></i></span><br><span class="line"><span class="post-count">字数统计:<%= totalcount(site) %></span></span><br></pre></td></tr></table></figure><p>实现效果如下:<br><img src="https://img-blog.csdnimg.cn/20190807104042630.png" alt="在这里插入图片描述"></p><hr><h1><a name="t3"></a><a name="t3"></a><a id="font_colorFF000003_font_168"></a><font color="#FF0000">【03】添加网站运行时间 </font></h1><p>一个比较好的小功能,可以看见自己的博客运行多久了,时间一天天的增加,成就感也会一天天增加的<br>在 <font color="#FF0000">\themes\hexo-theme-spfk\layout\_partial\footer.ejs</font> 文件下添加以下代码:</p><figure class="highlight plain"><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><span class="line"><span id="timeDate">载入天数...</span><span id="times">载入时分秒...</span></span><br><span class="line"><script></span><br><span class="line"> var now = new Date(); </span><br><span class="line"> function createtime() { </span><br><span class="line"> var grt= new Date("12/14/2019 17:38:00");//在此处修改你的建站时间,格式:月/日/年 时:分:秒</span><br><span class="line"> now.setTime(now.getTime()+250); </span><br><span class="line"> days = (now - grt ) / 1000 / 60 / 60 / 24; dnum = Math.floor(days); </span><br><span class="line"> hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum); hnum = Math.floor(hours); </span><br><span class="line"> if(String(hnum).length ==1 ){hnum = "0" + hnum;} minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum); </span><br><span class="line"> mnum = Math.floor(minutes); if(String(mnum).length ==1 ){mnum = "0" + mnum;} </span><br><span class="line"> seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum); </span><br><span class="line"> snum = Math.round(seconds); if(String(snum).length ==1 ){snum = "0" + snum;} </span><br><span class="line"> document.getElementById("timeDate").innerHTML = "本站已安全运行 "+dnum+" 天 "; </span><br><span class="line"> document.getElementById("times").innerHTML = hnum + " 小时 " + mnum + " 分 " + snum + " 秒"; </span><br><span class="line"> } </span><br><span class="line">setInterval("createtime()",250);</span><br><span class="line"></script></span><br></pre></td></tr></table></figure><p>最后效果如下:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk3ZGRlY2E4YS5wbmc" alt></p><hr><h1><a name="t4"></a><a name="t4"></a><a id="font_colorFF000004_font_197"></a><font color="#FF0000">【04】添加百度统计 </font></h1><p>百度统计是百度推出的一款免费的专业网站流量分析工具,能够告诉用户访客是如何找到并浏览用户的网站,在网站上做了些什么,非常有趣,接下来我们把百度统计添加到自己博客当中</p><p>访问<a href="https://tongji.baidu.com" rel="noopener" target="_blank">百度统计首页</a>,注册一个账号后登陆,添加你的博客网站<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk4NTk0Yzg5NS5wbmc" alt><br>接着点击代码获取,复制该代码<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk4ODE1M2MzNS5wbmc" alt></p><p>然后到目录 <font color="#FF0000">\themes\hexo-theme-spfk\layout\_partial</font> 下新建一个 <font color="#FF0000">baidu-analytics.ejs</font> 文件,里面粘贴你刚刚复制的代码<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk4YjBiYjRlZi5wbmc" alt></p><p>修改主题文件夹下的 <font color="#FF0000">_config.yml</font> 文件,将你的key(图中涂掉部分)填写进去:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk4ZDQzNTQ5ZC5wbmc" alt></p><p>所有操作完成后可以在百度统计管理页面检查代码是否安装成功,如果代码安装正确,一般20分钟后,可以查看网站分析数据<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMzEvNWM5Zjk5MDBiOWI4Ny5wbmc" alt></p><p>另外推荐:<a href="https://web.umeng.com/main.php?c=user&a=index" rel="noopener" target="_blank">友盟</a>,2010年4月在北京成立,安全、可靠、公正、第三方的网站流量统计分析系统</p><hr><h1><a name="t5"></a><a name="t5"></a><a id="font_colorFF000005RSS_font_217"></a><font color="#FF0000">【05】添加RSS订阅 </font></h1><p>RSS订阅是站点用来和其他站点之间共享内容的一种简易方式,即Really Simple Syndication(简易信息聚合),如果不会使用,可以参见百度百科:https://baike.baidu.com/item/RSS%E8%AE%A2%E9%98%85/663114 ;首先我们安装feed插件,在本地hexo目录下右键<code>Git Bash Here</code>,输入以下命令:</p><pre class="prettyprint"><code class="prism language-shell has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;">$ <span class="token function">npm</span> <span class="token function">install</span> hexo-generator-feed<p>等待安装完成后,打开hexo目录下的配置文件 <font color="#FF0000">_config.yml</font>,在末尾添加以下配置:</p><pre class="prettyprint"><code class="prism language-yaml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token comment"># Extensions</span><span class="token comment">## Plugins: http://hexo.io/plugins/</span><span class="token comment">#RSS订阅</span><span class="token key atrule">plugin</span><span class="token punctuation">:</span><span class="token punctuation">-</span> hexo<span class="token punctuation">-</span>generator<span class="token punctuation">-</span>feed<span class="token comment">#Feed Atom</span><span class="token key atrule">feed</span><span class="token punctuation">:</span><span class="token key atrule">type</span><span class="token punctuation">:</span> atom<span class="token key atrule">path</span><span class="token punctuation">:</span> atom.xml<span class="token key atrule">limit</span><span class="token punctuation">:</span> <span class="token number">20</span><p>随后打开主题配置文件 <font color="#FF0000">_config.yml</font>,添加以下配置:</p><pre class="prettyprint"><code class="prism language-yaml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token key atrule">rss</span><span class="token punctuation">:</span> /atom.xml<p>至此,RSS订阅功能添加完成</p><hr><h1><a name="t6"></a><a name="t6"></a><a id="font_colorFF000006_Fork_me_on_GitHub__font_249"></a><font color="#FF0000">【06】添加 Fork me on GitHub 效果 </font></h1><p>效果图:<br><img src="https://img-blog.csdnimg.cn/20190807104140857.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><a href="https://blog.github.com/2008-12-19-github-ribbons/" rel="noopener" target="_blank">点击此处</a>可以查看更多样式,将相应样式的代码复制到你想要放的地方就OK了,代码里的链接也要替换成你的,更多创意,比如 Follow me on CSDN ,只需要用PS改掉图片里的文字,替换掉相应链接即可</p><hr><h1><a name="t7"></a><a name="t7"></a><a id="font_colorFF000007_font_257"></a><font color="#FF0000">【07】更改本地预览端口号 </font></h1><p>hexo博客在执行<code>hexo s</code>进行本地预览的时候,默认端口号是4000,当该端口号被占用时会报错 <code>Error: listen EADDRINUSE 0.0.0.0:4000</code> ,此时可以关闭占用该端口的进程,也可以更换端口号,更换端口号可以通过以下两种方法实现:</p><p>方法一:在根目录的<code>_config.yml</code>配置文件内加上如下代码更改<code>hexo s</code>运行时的端口号:</p><pre class="prettyprint"><code class="prism language-yaml has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="token key atrule">server</span><span class="token punctuation">:</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">5000</span> <span class="token key atrule">compress</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">header</span><span class="token punctuation">:</span> <span class="token boolean important">true</span><p>方法二:通过 <code>hexo server -p 5000</code> 命令来指定端口,这种方法只是本次执行有效</p><hr></code></pre></code></pre></code></pre></code></pre></code></pre></code></pre></code></pre></code></pre></font></a></li></ul></div>]]></content>
<summary type="html">
<p></p><div class="toc"><h3><a name="t0"></a><a name="t0"></a>文章目录</h3><ul><li><a href="#font_colorFF000001_font_18" rel="nofollow" target="_self"><font color="#FF0000">【01】添加评论系统 </font></a></li><li><a href="#font_colorFF000002_font_66" rel="nofollow" target="_self"><font color="#FF0000">【02】添加字数统计和阅读时长 </font></a></li><li><a href="#font_colorFF000003_font_168" rel="nofollow" target="_self"><font color="#FF0000">【03】添加网站运行时间 </font></a></li><li><a href="#font_colorFF000004_font_197" rel="nofollow" target="_self"><font color="#FF0000">【04】添加百度统计 </font></a></li><li><a href="#font_colorFF000005RSS_font_217" rel="nofollow" target="_self"><font color="#FF0000">【05】添加RSS订阅 </font></a></li>
</summary>
<category term="Hexo" scheme="ayjcsgm.github.io/tags/Hexo/"/>
</entry>
<entry>
<title>用GitHub + Hexo搭建属于自己的个人博客</title>
<link href="ayjcsgm.github.io/2019/12/14/%E7%94%A8GitHub-Hexo%E6%90%AD%E5%BB%BA%E5%B1%9E%E4%BA%8E%E8%87%AA%E5%B7%B1%E7%9A%84%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/"/>
<id>ayjcsgm.github.io/2019/12/14/%E7%94%A8GitHub-Hexo%E6%90%AD%E5%BB%BA%E5%B1%9E%E4%BA%8E%E8%87%AA%E5%B7%B1%E7%9A%84%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/</id>
<published>2019-12-14T12:32:03.000Z</published>
<updated>2019-12-14T12:56:34.646Z</updated>
<content type="html"><![CDATA[<p></p><div class="toc"><h3><a name="t0"></a><a name="t0"></a>文章目录</h3><ul><li><a href="#font_colorFF000_font_12" rel="nofollow" target="_self"><font color="#FF000">● 前言</font></a></li><li><a href="#font_colorFF000_font_20" rel="nofollow" target="_self"><font color="#FF000">● 入门</font></a></li><li><a href="#font_colorFF000__Nodejsfont_31" rel="nofollow" target="_self"><font color="#FF000">● 安装 Node.js</font></a></li><li><a href="#font_colorFF000__Gitfont_38" rel="nofollow" target="_self"><font color="#FF000">● 安装 Git</font></a></li><ul><li><a href="#font_colorFF000__Gitfont_44" rel="nofollow" target="_self"><font color="#FF000"> • 检验Git是否安装成功</font></a></li></ul><a id="more"></a><li><a href="#font_colorFF000_Hexo_font_54" rel="nofollow" target="_self"><font color="#FF000">● 安装Hexo </font></a></li><ul><li><a href="#font_colorFF000_Hexo_font_62" rel="nofollow" target="_self"><font color="#FF000">• Hexo 初始化配置</font></a></li></ul><li><a href="#font_colorFF000_font_71" rel="nofollow" target="_self"><font color="#FF000">● 本地查看效果</font></a></li><li><a href="#font_colorFF000__Github_Pages_font_84" rel="nofollow" target="_self"><font color="#FF000">● 将博客部署到 Github Pages 上</font></a></li><ul><li><a href="#font_colorFF000___Github_font_87" rel="nofollow" target="_self"><font color="#FF000">• 注册 Github 账户</font></a></li><li><a href="#font_colorFF000_font_90" rel="nofollow" target="_self"><font color="#FF000">• 创建项目代码库</font></a></li><li><a href="#font_colorFF000__SSH_font_94" rel="nofollow" target="_self"><font color="#FF000">• 配置 SSH 密钥</font></a></li><li><a href="#font_colorFF000__GitHub_font_125" rel="nofollow" target="_self"><font color="#FF000">• 在 GitHub 账户中添加你的公钥</font></a></li><li><a href="#font_colorFF000__font_138" rel="nofollow" target="_self"><font color="#FF000">• 测试 </font></a></li><li><a href="#font_colorFF000__Git__font_151" rel="nofollow" target="_self"><font color="#FF000">• 配置 Git 个人信息 </font></a></li></ul><li><a href="#font_colorFF000__Hexo__Github_font_160" rel="nofollow" target="_self"><font color="#FF000">● 将本地的 Hexo 文件更新到 Github 的库中</font></a></li><li><a href="#font_colorFF000_font_199" rel="nofollow" target="_self"><font color="#FF000">● 在博客上发表文章</font></a></li><li><a href="#font_colorFF000_font_257" rel="nofollow" target="_self"><font color="#FF000">● 为博客更换自己喜欢的主题</font></a></li><hr><h1><a name="t1"></a><a name="t1"></a><a id="font_colorFF000_font_12"></a><font color="#FF000">● 前言</font></h1><p>这是一篇有关如何使用 <font color="#DC143C">Github</font> 和 <font color="#DC143C">Hexo</font> 搭建属于自己独立博客的详尽教程,刚开始搭建博客的时候自己也是网上各种百度,由于自己属于<font color="#DC143C">小白</font>那种,历经了千辛万苦才弄好,所以借这个机会写一篇小白真正能看懂的博客搭建教程,教你一步一步走向成功的彼岸! </p><p>推荐文章: <a href="http://www.cnblogs.com/jhzhu/p/3893297.html" rel="noopener" target="_blank">《我为什么写博客》</a> (By 知明所以)<br> <a href="http://mindhacks.cn/2009/02/15/why-you-should-start-blogging-now/" rel="noopener" target="_blank">《为什么你应该(从现在开始就)写博客》</a> (By 刘未鹏 | Mind Hacks)</p><h1><a name="t2"></a><a name="t2"></a><a id="font_colorFF000_font_20"></a><font color="#FF000">● 入门</font></h1><blockquote><center>Github Pages</center></blockquote><p>Github Pages可以被认为是用户编写的、托管在github上的静态网页。使用Github Pages可以为你提供一个免费的服务器,免去了自己搭建服务器和写数据库的麻烦。此外还可以绑定自己的域名。</p><blockquote><center>Hexo</center></blockquote><p>Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。</p><h1><a name="t3"></a><a name="t3"></a><a id="font_colorFF000__Nodejsfont_31"></a><font color="#FF000">● 安装 Node.js</font></h1><p><a href="https://nodejs.org/en/download/" rel="noopener" target="_blank">点击此处</a>访问官网,按需下载相应版本,默认安装可以了<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY0ODFkMzc0MC5wbmc" alt></p><p>注:本人在安装过程中出现了Warning 1909,无法创建快捷方式,这种情况很少出现,如果在安装过程中也有这种情况请参考百度文库(win10系统实测可行):<a href="https://wenku.baidu.com/view/4ad59110964bcf84b9d57ba5.html" rel="noopener" target="_blank">《Win7安装程序警告1909无法创建快捷方式》</a><br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY0Yzk5ZjcwOC5wbmc" alt></p><h1><a name="t4"></a><a name="t4"></a><a id="font_colorFF000__Gitfont_38"></a><font color="#FF000">● 安装 Git</font></h1><p><a href="https://git-scm.com/download/win" rel="noopener" target="_blank">点击此处</a>访问官网,按需下载相应版本,默认安装即可 <br>参考资料:<a href="https://www.cnblogs.com/jytx/p/5602927.html" rel="noopener" target="_blank">《如何在windows下安装GIT》</a> (By 俊雨廷休)<br> <a href="http://git.oschina.net/progit/" rel="noopener" target="_blank">《Pro Git(中文版)》</a></p><h2><a name="t5"></a><a name="t5"></a><a id="font_colorFF000__Gitfont_44"></a><font color="#FF000"> • 检验Git是否安装成功</font></h2><p>同时按下 Win 键和 R 键打开运行窗口,输入 <font color="#DC143C">cmd</font> ,然后输入以下命令,有相应版本信息显示则安装成功,若不正确可以卸载软件重新安装,此外若安装成功,在桌面右键鼠标,可以看到菜单里多了 <font color="#DC143C">Git GUI Here</font> 和 <font color="#DC143C">Git Bash Here</font>两个选项,第一个是<font color="#DC143C">图形界面的Git操作</font>,另一个是<font color="#DC143C">命令行</font></p><figure class="highlight plain"><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><span class="line">$ git --version </span><br><span class="line">$ node -v </span><br><span class="line">$ npm -v</span><br></pre></td></tr></table></figure><p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY0ZWU4YTc5NC5wbmc" alt><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY1MGVjMjA5Mi5wbmc" alt></p><h1><a name="t6"></a><a name="t6"></a><a id="font_colorFF000_Hexo_font_54"></a><font color="#FF000">● 安装Hexo </font></h1><p>选择一个磁盘,新建一个文件夹,自己重命名文件夹(如:我的文件夹为:<font color="#DC143C">E\TRHX_Blog</font>),博客相关文件将储存在此文件夹下,在该文件夹下右键鼠标,点击 <font color="#DC143C">Git Bash Here</font>,输入以下 npm 命令即可安装,第一个命令表示安装 hexo,第二个命令表示安装 hexo 部署到 git page 的 deployer,如图所示即为安装成功</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-cli -g </span><br><span class="line">$ npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure><p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY1M2Q0YjAxOS5wbmc" alt></p><h2><a name="t7"></a><a name="t7"></a><a id="font_colorFF000_Hexo_font_62"></a><font color="#FF000">• Hexo 初始化配置</font></h2><p>在刚才新建的文件夹里面再次新建一个 <font color="#DC143C">Hexo</font> 文件夹(如:我的文件夹为:<font color="#DC143C">E\TRHX_Blog\Hexo</font>),进入该 <font color="#DC143C">Hexo</font> 文件夹右键鼠标,点击 <font color="#DC143C">Git Bash Here</font>,输入以下命令,如图所示则安装成功</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo init</span><br></pre></td></tr></table></figure><p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY1NjZiNGI2Yi5wbmc" alt></p><p>Hexo 安装完成后,将会在指定文件夹中新建所需要的文件,Hexo 文件夹下的目录如下:<br></p><p><img src="https://img-blog.csdnimg.cn/2019121418584966.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><h1><a name="t8"></a><a name="t8"></a><a id="font_colorFF000_font_71"></a><font color="#FF000">● 本地查看效果</font></h1><p>执行以下命令,执行完即可登录 <a href="http://localhost:4000/" rel="noopener" target="_blank">http://localhost:4000/</a> 查看效果</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate </span><br><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>显示以下信息说明操作成功:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">INFO Hexo is running at http://0.0.0.0:4000/. Press Ctrl+C to stop.</span><br></pre></td></tr></table></figure><p>登录 <a href="http://localhost:4000/" rel="noopener" target="_blank">http://localhost:4000/</a> 查看效果:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY1YjdjNzRkMi5wbmc" alt></p><h1><a name="t9"></a><a name="t9"></a><a id="font_colorFF000__Github_Pages_font_84"></a><font color="#FF000">● 将博客部署到 Github Pages 上</font></h1><p>到目前为止,我们的本地博客就成功搭建了,但是现在我们只能通过本地连接查看博客,我们要做的是让其他人也能够访问我们的博客,这就需要我们将博客部署到Github Pages上</p><h2><a name="t10"></a><a name="t10"></a><a id="font_colorFF000___Github_font_87"></a><font color="#FF000">• 注册 Github 账户</font></h2><p><a href="https://github.com" rel="noopener" target="_blank">点击此处</a>访问 Github 官网,点击 Sign Up 注册账户</p><h2><a name="t11"></a><a name="t11"></a><a id="font_colorFF000_font_90"></a><font color="#FF000">• 创建项目代码库</font></h2><p>点击 <font color="#DC143C">New repository</font> 开始创建,步骤及注意事项见下图:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY1ZTA5MzBjMi5wbmc" alt></p><h2><a name="t12"></a><a name="t12"></a><a id="font_colorFF000__SSH_font_94"></a><font color="#FF000">• 配置 SSH 密钥</font></h2><p>只有配置好 <font color="#DC143C">SSH</font> 密钥后,我们才可以通过 git 操作实现本地代码库与 Github 代码库同步,在你第一次新建的文件夹里面(如:我的文件夹为:<font color="#DC143C">E\TRHX_Blog</font>) <font color="#DC143C">Git Bash Here</font> 输入以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ ssh-keygen -t rsa -C "your email@example.com" </span><br><span class="line">//引号里面填写你的邮箱地址,比如我的是1141341095@qq.com</span><br></pre></td></tr></table></figure><p>之后会出现:</p><figure class="highlight plain"><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><span class="line">Generating public/private rsa key pair. </span><br><span class="line">Enter file in which to save the key (/c/Users/you/.ssh/id_rsa): </span><br><span class="line">//到这里可以直接回车将密钥按默认文件进行存储</span><br></pre></td></tr></table></figure><p>然后会出现:</p><figure class="highlight plain"><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><span class="line">Enter passphrase (empty for no passphrase): </span><br><span class="line">//这里是要你输入密码,其实不需要输什么密码,直接回车就行 </span><br><span class="line">Enter same passphrase again:</span><br></pre></td></tr></table></figure><p>接下来屏幕会显示:</p><figure class="highlight plain"><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><span class="line">Your identification has been saved in /c/Users/you/.ssh/id_rsa. </span><br><span class="line">Your public key has been saved in /c/Users/you/.ssh/id_rsa.pub. </span><br><span class="line">The key fingerprint is: </span><br><span class="line">这里是各种字母数字组成的字符串,结尾是你的邮箱 </span><br><span class="line">The key's randomart image is: </span><br><span class="line">这里也是各种字母数字符号组成的字符串</span><br></pre></td></tr></table></figure><p>运行以下命令,将公钥的内容复制到系统粘贴板上</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ clip < ~/.ssh/id_rsa.pub</span><br></pre></td></tr></table></figure><p><font color="#FF000">• 在 GitHub 账户中添加你的公钥</font></p><p>① 登陆 GitHub,进入 <font color="#DC143C">Settings</font>:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY2MDZlNzU1OC5wbmc" alt></p><p>② 点击 <font color="#DC143C">SSH and GPG Keys</font>:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY2MjJiNDU4OC5wbmc" alt></p><p>③ 选择 <font color="#DC143C">New SSH key</font>:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY2M2ViMWFiMi5wbmc" alt></p><p>④ 粘贴密钥:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY2YmMxZmM4Ny5wbmc" alt></p><h2><a name="t14"></a><a name="t14"></a><a id="font_colorFF000__font_138"></a><font color="#FF000">• 测试 </font></h2><p>输入以下命令:<font color="#DC143C">注意:git@github.com不要做任何更改!</font></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ ssh -T git@github.com</span><br></pre></td></tr></table></figure><p>之后会显示:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY2ZTNiNDgwMy5wbmc" alt></p><p>输入 <font color="#DC143C">yes</font> 后会显示:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY3MDJjNDZkNi5wbmc" alt></p><p>此时表示设置正确</p><h2><a name="t15"></a><a name="t15"></a><a id="font_colorFF000__Git__font_151"></a><font color="#FF000">• 配置 Git 个人信息 </font></h2><p>Git 会根据用户的名字和邮箱来记录提交,GitHub 也是用这些信息来做权限的处理,输入以下命令进行个人信息的设置,把名称和邮箱替换成你自己的,名字可以不是 GitHub 的昵称,但为了方便记忆,建议与 GitHub 一致</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ git config --global user.name "此处填你的用户名" </span><br><span class="line">$ git config --global user.email "此处填你的邮箱"</span><br></pre></td></tr></table></figure><p>到此为止 SSH Key 配置成功,本机已成功连接到 Github</p><h1><a name="t16"></a><a name="t16"></a><a id="font_colorFF000__Hexo__Github_font_160"></a><font color="#FF000">● 将本地的 Hexo 文件更新到 Github 的库中</font></h1><p>① 登录 Github 打开自己的项目 <font color="#DC143C">your name.github.io</font><br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY3MjBjY2FlMC5wbmc" alt></p><p>② 鼠标移到 <font color="#DC143C">Clone or download</font> 按钮,选择 <font color="#DC143C">Use SSH</font><br><img src="https://img-blog.csdnimg.cn/20190816220246218.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzU5MjI0,size_16,color_FFFFFF,t_70" alt></p><p>③ 一键复制地址<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY3NmNiM2RhNC5wbmc" alt></p><p>④ 打开你创建的 <font color="#DC143C">Hexo</font> 文件夹(如:<font color="#DC143C">E:\TRHX_Blog\Hexo</font>),右键用记事本(Notepad++或者VS code等都可以)打开该文件夹下的 <font color="#DC143C">_config.yml</font> 文件<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY3OGI4YmEzZS5wbmc" alt></p><p>⑤ 按下图修改 <font color="#DC143C">_config.yml</font> 文件并保存<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY3ZTExZWVkNy5wbmc" alt></p><p>⑥ 在 <font color="#DC143C">Hexo</font> 文件夹下分别执行以下命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ hexo g </span><br><span class="line">$ hexo d</span><br></pre></td></tr></table></figure><p>或者直接执行</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo g -d</span><br></pre></td></tr></table></figure><p>执行完之后会让你输入你的 Github 的账号和密码,如果此时报以下错误,说明你的 deployer 没有安装成功</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ERROR Deployer not found: git</span><br></pre></td></tr></table></figure><p>需要执行以下命令再安装一次:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure><p>再执行 <font color="#DC143C"><code>hexo g -d</code></font>,你的博客就会部署到 Github 上了</p><p>⑦ 访问博客</p><p>你的博客地址:<font color="#DC143C">https://你的用户名.github.io</font>,比如我的是:<font color="#DC143C">https://ayjcsgm.github.io/</font> ,现在每个人都可以通过此链接访问你的博客了</p><h1><a name="t17"></a><a name="t17"></a><a id="font_colorFF000_font_199"></a><font color="#FF000">● 在博客上发表文章</font></h1><p>博客已经成功搭建了,但是我们该怎么写博客呢?</p><p>① 新建一个空文章,输入以下命令,会在项目 <font color="#DC143C">\Hexo\source\_posts</font> 中生成 <font color="#DC143C">文章标题.md</font> 文件,文章标题根据需要命名</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo n "文章标题"</span><br></pre></td></tr></table></figure><p>也可以直接在 <font color="#DC143C">\Hexo\source\_posts</font> 目录下右键鼠标新建文本文档,改后缀为 <font color="#DC143C">.md</font> 即可,这种方法比较方便</p><p>② 用编辑器编写文章</p><p><font color="#DC143C">md</font> 全称 Markdown, Markdown 是 2004 年由 John Gruberis 设计和开发的纯文本格式的语法,非常的简单实用,常用的标记符号屈指可数,几分钟即可学会, <font color="#DC143C">.md</font> 文件可以使用支持 Markdown 语法的编辑器编辑,然后保存文件到 <font color="#DC143C">\Hexo\source\_posts</font> 文件夹下即可</p><p>推荐 Windows 上使用 <font color="#DC143C">MarkdownPad2</font> 或者<font color="#DC143C">小书匠</font> 编辑器,macOS 上使用 <font color="#DC143C">Mou</font> 编辑器,Linux 上使用 <font color="#DC143C">Remarkable</font>编辑器,Web 端上使用<font color="#DC143C">CSDN</font> ,其他编辑器推荐可以参考我的另一篇文章:<a href="https://blog.csdn.net/qq_36759224/article/details/82229243" rel="noopener" target="_blank">《最新主流 Markdown 编辑器推荐》</a></p><p>文章标题,标签,分类,封面图片,摘要等,可以在 Front-matter 里面配置(Front-matter 是文件最上方以 — 分隔的区域,用于指定个别文件的变量,官方文档:https://hexo.io/zh-cn/docs/front-matter ),举个例子:</p><pre class="prettyprint"><code class="prism language-markdown has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;">---layout: 页面布局(配合主题文档使用)title: 文章名称date: 文章日期comments: 文章是否开启评论photos: 文章封面图(仅部分主题支持)tags: - 文章标签一 - 文章标签二categories: 文章分类description: 文章描述,即要在首页显示的摘要(仅部分主题支持)---<p>这里是摘要</p><p><!– more –></p><p>这里是正文</p><p>注意:description 和 <!– more –> 方式显示摘要二选一即可,部分主题不支持description,每个配置英文冒号后面要空一格,不同主题配置有所差异,具体要参考主题文档</p><p>当我们用编辑器写好文章后,可以使用以下命令将其推送到服务器上</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ hexo g </span><br><span class="line">$ hexo d</span><br></pre></td></tr></table></figure><p>或者将两个命令合二为一输入以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo d -g</span><br></pre></td></tr></table></figure><p>现在访问你的博客就可以看见写好的文章啦!</p><p>参考资料:<a href="https://blog.csdn.net/jinhui157/article/details/73872795" rel="noopener" target="_blank">《10款流行的Markdown编辑器》</a> (By xiaoxiao_engineer)<br> <a href="https://www.jianshu.com/p/q81RER/" rel="noopener" target="_blank">《献给写作者的 Markdown 新手指南》</a> (By 简书)<br> <a href="https://sspai.com/post/25137" rel="noopener" target="_blank">《认识与入门 Markdown》</a> (By Te_Lee)<br> <a href="http://ibruce.info/2013/11/26/markdown/" rel="noopener" target="_blank">《markdown简明语法》</a> (By 不如)<br> <a href="https://www.jianshu.com/p/191d1e21f7ed" rel="noopener" target="_blank">《markdown基本语法》</a> (By 高鸿祥)<br> <a href="http://www.liuhaihua.cn/archives/143443.html" rel="noopener" target="_blank">《Markdown 公式指导手册》</a> (By Harries)</p><h1><a name="t18"></a><a name="t18"></a><a id="font_colorFF000_font_257"></a><font color="#FF000">● 为博客更换自己喜欢的主题</font></h1><p>博客也搭建好了,文章也会写了,但是!!!默认的主题并不喜欢怎么办?现在,我们就来为自己的博客更换自己喜欢的主题</p><p><a href="https://hexo.io/themes/" rel="noopener" target="_blank">点击此处</a>进入 Hexo 官网的主题专栏,我们可以看见有许多的主题供我们选择<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY4MDI1YzM3Yy5wbmc" alt></p><p>我们要做的就是把主题克隆过来,在此我们以主题 <font color="#DC143C">Aero-Dual</font> 为例,点进去我们就可以看见该主题作者的博客,鼠标滑到底,我们可以看见 <font color="#DC143C">Theme By Levblanc</font> 的字样(其他主题类似),点击作者 <font color="#DC143C">Levblanc</font> ,页面就会跳转到该主题所有的相关文件在 Github 上的地址,复制该地址<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY4MjNmMTI3NS5wbmc" alt><br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY4NDE0Y2VjNi5wbmc" alt><br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY4NWRiZGYyMy5wbmc" alt></p><p>再打开 <font color="#DC143C">Hexo</font> 文件夹下的 <font color="#DC143C">themes</font> 目录(如:<font color="#DC143C">E:\TRHX_Blog\Hexo\themes</font>),右键 <font color="#DC143C">Git Bash Here</font>,输入以下命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone 此处填写你刚才复制的主题地址</span><br></pre></td></tr></table></figure><p>比如要安装 <font color="#DC143C">Aero-Dual</font> 主题,则输入命令:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ git clone https://github.com/levblanc/hexo-theme-aero-dual</span><br></pre></td></tr></table></figure><p>等待下载完成后即可在 <font color="#DC143C">themes</font> 目录下生成 <font color="#DC143C">hexo-theme-aero-dual</font> 文件夹,然后打开 <font color="#DC143C">Hexo</font> 文件夹下的配置文件 <font color="#DC143C">_config.yml</font> ,找到关键字 <font color="#DC143C">theme</font>,修改参数为:<font color="#DC143C">theme:hexo-theme-aero-dual</font> (其他主题修改成相应名称即可),再次注意冒号后面有一个空格!<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pLmxvbGkubmV0LzIwMTkvMDMvMjUvNWM5OGY4N2MwYTdlOS5wbmc" alt></p><p>返回 <font color="#DC143C">Hexo</font> 目录,右键 <font color="#DC143C">Git Bash Here</font> ,输入以下命令开始部署主题:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ hexo g </span><br><span class="line">$ hexo s</span><br></pre></td></tr></table></figure><p>此时打开浏览器,访问 <a href="http://localhost:4000/" rel="noopener" target="_blank">http://localhost:4000/</a> 就可看见我们的主题已经更换了,如果感觉效果满意,我们就可以把它部署到Github上了</p><p>打开 <font color="#DC143C">Hexo</font> 文件夹,右键 <font color="#DC143C">Git Bash Here</font> ,输入以下命令:</p><figure class="highlight plain"><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><span class="line">$ hexo clean </span><br><span class="line">//该命令的作用是清除缓存,若不输入此命令,服务器有可能更新不了主题</span><br><span class="line">$ hexo g -d</span><br></pre></td></tr></table></figure><p>此时访问自己的博客即可看见更换后的主题,但我们仍然需要对主题的相关配置进行修改,比如网站标题,图标等等,Hexo 中有两份主要的配置文件,名称都是 <font color="#DC143C">_config.yml</font> ,它们均是用于站点配置使用的。其中,一份位于站点根目录下(比如我的:<font color="#DC143C">E:\TRHX_Blog\Hexo\_config.yml</font>),主要包含 Hexo 本身整站的配置;另一份位于主题目录下(比如我的:<font color="#DC143C">E:\TRHX_Blog\Hexo\themes\hexo-theme-aero-dual\_config.yml</font>),这份配置由主题作者提供,主要用于配置主题相关的选项,一般 <font color="#DC143C">_config.yml</font> 文件里都有相关注释,按需修改即可</p><blockquote><p>参考资料:</p><p><a href="https://www.zhihu.com/question/24422335" rel="noopener" target="_blank">《有哪些好看的 Hexo 主题?》</a> (知乎)<br> <a href="https://hexo.io/zh-cn/docs/configuration.html" rel="noopener" target="_blank">《Hexo | 配置》</a> (Hexo官方文档)<br> <a href="https://segmentfault.com/a/1190000002632530" rel="noopener" target="_blank">《hexo常用命令笔记》</a> (By 小弟调调)</p></blockquote></code></pre></ul></div>]]></content>
<summary type="html">
<p></p><div class="toc"><h3><a name="t0"></a><a name="t0"></a>文章目录</h3><ul><li><a href="#font_colorFF000_font_12" rel="nofollow" target="_self"><font color="#FF000">● 前言</font></a></li><li><a href="#font_colorFF000_font_20" rel="nofollow" target="_self"><font color="#FF000">● 入门</font></a></li><li><a href="#font_colorFF000__Nodejsfont_31" rel="nofollow" target="_self"><font color="#FF000">● 安装 Node.js</font></a></li><li><a href="#font_colorFF000__Gitfont_38" rel="nofollow" target="_self"><font color="#FF000">● 安装 Git</font></a></li><ul><li><a href="#font_colorFF000__Gitfont_44" rel="nofollow" target="_self"><font color="#FF000"> • 检验Git是否安装成功</font></a></li></ul>
</summary>
<category term="Hexo" scheme="ayjcsgm.github.io/tags/Hexo/"/>
</entry>
<entry>
<title>随机图片API接口</title>
<link href="ayjcsgm.github.io/2019/12/14/%E9%9A%8F%E6%9C%BA%E5%9B%BE%E7%89%87API%E6%8E%A5%E5%8F%A3/"/>
<id>ayjcsgm.github.io/2019/12/14/%E9%9A%8F%E6%9C%BA%E5%9B%BE%E7%89%87API%E6%8E%A5%E5%8F%A3/</id>
<published>2019-12-14T12:31:38.000Z</published>
<updated>2019-12-14T12:44:36.446Z</updated>
<content type="html"><![CDATA[<p><a href="https://unsplash.it/1600/900?random" rel="external nofollow noopener noreferrer" target="_blank">https://unsplash.it/1600/900?random</a>(据说国内加载略慢,我试了一下还好都差不多)<br><a href="https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture" target="_blank" rel="noopener">https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture</a>(必应每日图片)<br><br><a href="https://uploadbeta.com/api/pictures/random" rel="external nofollow noopener noreferrer" target="_blank">https://uploadbeta.com/api/pictures/random</a>(必应图库,一些风景图人物图什么的,都是现代风格)</p><p></p><a id="more"></a> <p><a href="https://source.unsplash.com/random" rel="external nofollow noopener noreferrer" target="_blank">https://source.unsplash.com/random</a>(风格同上,但这个的图尺寸不是大图)</p><p style="background-color:rgba(221,204,222,0.7)">下面是几个二次元的,都差不多ヽ(´o`(大部分都在本地测试过,可以放心食用),但是以下这些唯一的问题就是。。有些图片质量参差不齐。。所以要想完全和自己口味的话还是自己做一个吧,有开源代码<br></p><p>首先最牛逼的大概就是<a href="http://ikmoe.com/8548.html" rel="external nofollow noopener noreferrer" target="_blank"><strong>「漫月API」</strong></a>了,一直被模仿从未被超越那种╮( ̄▽ ̄””)╭,但是现在关了,大概过几个月会开</p><p><a href="https://img.xjh.me/random_img.php" rel="external nofollow noopener noreferrer" target="_blank">https://img.xjh.me/random_img.php</a> (850+)<a href="https://www.xjh.me/3069.html?replytocom=4888" rel="external nofollow noopener noreferrer" target="_blank"> 出处(・ω・)ノ</a></p><p><a href="http://www.dmoe.cc/random.php" rel="external nofollow noopener noreferrer" target="_blank">http://www.dmoe.cc/random.php</a>(1000+)<a href="http://www.dmoe.cc/" rel="external nofollow noopener noreferrer" target="_blank">出处(・ω・)ノ</a></p><p><a href="https://acg.yanwz.cn/api.php" rel="external nofollow noopener noreferrer" target="_blank">https://acg.yanwz.cn/api.php</a> (400+) <a href="https://acg.yanwz.cn/" rel="external nofollow noopener noreferrer" target="_blank">出处(・ω・)ノ</a></p><p><a href="https://img.paulzzh.tech/touhou/random" target="_blank" rel="noopener">https://img.paulzzh.tech/touhou/random</a> (东方的随机图,43000+)<a href="https://img.paulzzh.tech/" target="_blank" rel="noopener">出处(・ω・)ノ</a></p><p><a href="https://acg.toubiec.cn/random.php" rel="external nofollow noopener noreferrer" target="_blank">https://acg.toubiec.cn/random.php</a>(1000+) <a href="https://acg.toubiec.cn/" rel="external nofollow noopener noreferrer" target="_blank">出处(・ω・)ノ</a> 作者开源了 这篇博客里有介绍和源码 先蟹蟹大佬了<a href="https://www.toubiec.cn/164.html" rel="external nofollow noopener noreferrer" target="_blank">[项目]随机二次元图片API-已经开源</a></p><p style="background-color:rgba(221,204,222,0.7)">mark一下慢慢研究➡️ <a href="https://www.xhboke.com/14.html" rel="external nofollow noopener noreferrer" target="_blank">一个可在php中直接输出随机图片的API</a><br></p><hr><blockquote><p><strong>放上自己搭建的博客</strong>:<a href="https://ayjcsgm.github.io/"><strong>https://ayjcsgm.github.io/</strong></a></p></blockquote>]]></content>
<summary type="html">
<p><a href="https://unsplash.it/1600/900?random" rel="external nofollow noopener noreferrer" target="_blank">https://unsplash.it/1600/900?random</a>(据说国内加载略慢,我试了一下还好都差不多)<br><a href="https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture" target="_blank" rel="noopener">https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture</a>(必应每日图片)<br><br><a href="https://uploadbeta.com/api/pictures/random" rel="external nofollow noopener noreferrer" target="_blank">https://uploadbeta.com/api/pictures/random</a>(必应图库,一些风景图人物图什么的,都是现代风格)</p><p></p>
</summary>
<category term="Hexo" scheme="ayjcsgm.github.io/tags/Hexo/"/>
</entry>
<entry>
<title>简述TreeSet排序机制</title>
<link href="ayjcsgm.github.io/2019/12/11/%E7%AE%80%E8%BF%B0TreeSet%E6%8E%92%E5%BA%8F%E6%9C%BA%E5%88%B6/"/>
<id>ayjcsgm.github.io/2019/12/11/%E7%AE%80%E8%BF%B0TreeSet%E6%8E%92%E5%BA%8F%E6%9C%BA%E5%88%B6/</id>
<published>2019-12-11T12:29:46.000Z</published>
<updated>2019-12-14T11:34:18.825Z</updated>
<content type="html"><![CDATA[<h1 id="TreeSet简介"><a href="#TreeSet简介" class="headerlink" title="TreeSet简介"></a>TreeSet简介</h1><p>此集合的实现和树结构有关。与HashSet集合类似,TreeSet也是基于Map来实现,具体实现TreeMap,其底层结构为红黑树(特殊的二叉查找树);<br>与HashSet不同的是,TreeSet具有排序功能,分为自然排序(123456)和自定义排序两类,默认是自然排序;在程序中,我们可以按照任意顺序将元素插入到集合中,等到遍历时TreeSet会按照一定顺序输出(倒序/升序);<br>它继承AbstractSet,实现NavigableSet, Cloneable, Serializable接口。<br>(1)与HashSet同理,TreeSet继承AbstractSet类,获得了Set集合基础实现操作;</p><a id="more"></a> <p>(2)TreeSet实现NavigableSet接口,而NavigableSet又扩展了SortedSet接口。实现NavigableSet接口使得TreeSet具备了元素搜索功能;<br>(3)TreeSet实现Cloneable接口,可以被克隆;<br>(4)TreeSet实现了Serializable接口,可以被序列化;<br>具有如下特点:</p><ul><li>对插入的元素进行排序,是一个有序的集合(主要与HashSet的区别);</li><li>底层使用红黑树结构,而不是哈希表结构;</li><li>允许插入Null值;</li><li>不允许插入重复元素;</li><li>线程不安全;</li></ul><h1 id="TreeSet元素排序"><a href="#TreeSet元素排序" class="headerlink" title="TreeSet元素排序"></a>TreeSet元素排序</h1><p>TreeSet是一个有序集合,可以对集合元素排序,其中分为自然排序和自定义排序。<br>使用String、Intege类型:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">public class TreeSetTest {</span><br><span class="line"> public static void main(String[] agrs){</span><br><span class="line"> naturalSort();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //自然排序顺序:升序</span><br><span class="line"> public static void naturalSort(){</span><br><span class="line"> TreeSet<String> treeSetString = new TreeSet<String>();</span><br><span class="line"> treeSetString.add("a");</span><br><span class="line"> treeSetString.add("z");</span><br><span class="line"> treeSetString.add("d");</span><br><span class="line"> treeSetString.add("b");</span><br><span class="line"> System.out.println("字母顺序:" + treeSetString.toString());</span><br><span class="line"></span><br><span class="line"> TreeSet<Integer> treeSetInteger = new TreeSet<Integer>();</span><br><span class="line"> treeSetInteger.add(1);</span><br><span class="line"> treeSetInteger.add(24);</span><br><span class="line"> treeSetInteger.add(23);</span><br><span class="line"> treeSetInteger.add(6);</span><br><span class="line"> System.out.println(treeSetInteger.toString());</span><br><span class="line"> System.out.println("数字顺序:" + treeSetString.toString());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>测试结果:<br>字母顺序:[a, b, d, z]<br>数字顺序:[1, 6, 23, 24]</p><p>使用对象类型:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">public class App{</span><br><span class="line"></span><br><span class="line"> private String name;</span><br><span class="line"></span><br><span class="line"> private Integer age;</span><br><span class="line"></span><br><span class="line"> public App(){}</span><br><span class="line"></span><br><span class="line"> public App(String name,Integer age){</span><br><span class="line"> this.name = name;</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public String getName() {</span><br><span class="line"> return name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void setName(String name) {</span><br><span class="line"> this.name = name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public Integer getAge() {</span><br><span class="line"> return age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void setAge(Integer age) {</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args ){</span><br><span class="line"> System.out.println( "Hello World!" );</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">public class TreeSetTest {</span><br><span class="line"> public static void main(String[] agrs){</span><br><span class="line"> customSort();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //自定义排序顺序:升序</span><br><span class="line"> public static void customSort(){</span><br><span class="line"> TreeSet<App> treeSet = new TreeSet<App>();</span><br><span class="line"></span><br><span class="line"> //排序对象:</span><br><span class="line"> App app1 = new App("hello",10);</span><br><span class="line"> App app2 = new App("world",20);</span><br><span class="line"> App app3 = new App("my",15);</span><br><span class="line"> App app4 = new App("name",25);</span><br><span class="line"></span><br><span class="line"> //添加到集合:</span><br><span class="line"> treeSet.add(app1);</span><br><span class="line"> treeSet.add(app2);</span><br><span class="line"> treeSet.add(app3);</span><br><span class="line"> treeSet.add(app4);</span><br><span class="line"> System.out.println("TreeSet集合顺序为:"+treeSet);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>测试结果:<br>抛出异常:提示类App不能转换为Comparable对象:<br>Exception in thread “main” java.lang.ClassCastException: com.huangqiuping.collection.App cannot be cast to java.lang.Comparable</p><figure class="highlight plain"><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><span class="line">compare(key, key); // type (and possibly null) check</span><br><span class="line"></span><br><span class="line">final int compare(Object k1, Object k2) {</span><br><span class="line"> return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)</span><br><span class="line"> : comparator.compare((K)k1, (K)k2);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>通过查看源码,在TreeSet调用add方法时,会调用到底层TreeMap的put方法,在put方法中会调用到compare(key, key)方法,进行key大小的比较;<br>在比较的时候,会将传入的key进行类型强转,所以当我们自定义的App类进行比较的时候,自然就会抛出异常,因为App类并没有实现Comparable接口;<br>将App实现Comparable接口,在做比较:</p><figure class="highlight plain"><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><span class="line">public class App implements Comparable<App>{</span><br><span class="line"> private String name;</span><br><span class="line"> private Integer age;</span><br><span class="line"> public App(){}</span><br><span class="line"> public App(String name,Integer age){</span><br><span class="line"> this.name = name;</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line"> public String getName() {</span><br><span class="line"> return name;</span><br><span class="line"> }</span><br><span class="line"> public void setName(String name) {</span><br><span class="line"> this.name = name;</span><br><span class="line"> }</span><br><span class="line"> public Integer getAge() {</span><br><span class="line"> return age;</span><br><span class="line"> }</span><br><span class="line"> public void setAge(Integer age) {</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line"> //自定义比较:先比较name的长度,在比较age的大小;</span><br><span class="line"> public int compareTo(App app) {</span><br><span class="line"> //比较name的长度:</span><br><span class="line"> int num = this.name.length() - app.name.length();</span><br><span class="line"> //如果name长度一样,则比较年龄的大小:</span><br><span class="line"> return num == 0 ? this.age - app.age : num;</span><br><span class="line"> }</span><br><span class="line"> @Override</span><br><span class="line"> public String toString() {</span><br><span class="line"> return "App{" +</span><br><span class="line"> "name='" + name + '\'' +</span><br><span class="line"> ", age=" + age +</span><br><span class="line"> '}';</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>测试结果如下:<br>TreeSet集合顺序为:[App{name=’my’, age=15}, App{name=’name’, age=25}, App{name=’hello’, age=10}, App{name=’world’, age=20}]</p><p>此外,还有另一种方式,那就是实现Comparetor<t>接口,并重写compare方法;</t></p><figure class="highlight plain"><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><span class="line">//自定义App类的比较器:</span><br><span class="line">public class AppComparator implements Comparator<App> {</span><br><span class="line"></span><br><span class="line"> //比较方法:先比较年龄,年龄若相同在比较名字长度;</span><br><span class="line"> public int compare(App app1, App app2) {</span><br><span class="line"> int num = app1.getAge() - app2.getAge();</span><br><span class="line"> return num == 0 ? app1.getName().length() - app2.getName().length() : num;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>此时,App不用在实现Comparerable接口了,单纯的定义一个类即可;</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">public class App{</span><br><span class="line"></span><br><span class="line"> private String name;</span><br><span class="line"></span><br><span class="line"> private Integer age;</span><br><span class="line"></span><br><span class="line"> public App(){}</span><br><span class="line"></span><br><span class="line"> public App(String name,Integer age){</span><br><span class="line"> this.name = name;</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public String getName() {</span><br><span class="line"> return name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void setName(String name) {</span><br><span class="line"> this.name = name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public Integer getAge() {</span><br><span class="line"> return age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void setAge(Integer age) {</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args ){</span><br><span class="line"> System.out.println( "Hello World!" );</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">public class TreeSetTest {</span><br><span class="line"> public static void main(String[] agrs){</span><br><span class="line"> customSort();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //自定义比较器:升序</span><br><span class="line"> public static void customComparatorSort(){</span><br><span class="line"> TreeSet<App> treeSet = new TreeSet<App>(new AppComparator());</span><br><span class="line"> </span><br><span class="line"> //排序对象:</span><br><span class="line"> App app1 = new App("hello",10);</span><br><span class="line"> App app2 = new App("world",20);</span><br><span class="line"> App app3 = new App("my",15);</span><br><span class="line"> App app4 = new App("name",25);</span><br><span class="line"> </span><br><span class="line"> //添加到集合:</span><br><span class="line"> treeSet.add(app1);</span><br><span class="line"> treeSet.add(app2);</span><br><span class="line"> treeSet.add(app3);</span><br><span class="line"> treeSet.add(app4);</span><br><span class="line"></span><br><span class="line"> System.out.println("TreeSet集合顺序为:"+treeSet);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>测试结果:<br>TreeSet集合顺序为:[App{name=’hello’, age=10}, App{name=’my’, age=15}, App{name=’world’, age=20}, App{name=’name’, age=25}]</p><p>关于compareTo()、compare()方法:<br>结果返回大于0时,方法前面的值大于方法中的值;<br>结果返回等于0时,方法前面的值等于方法中的值;<br>结果返回小于0时,方法前面的值小于方法中的值;</p>]]></content>
<summary type="html">
<h1 id="TreeSet简介"><a href="#TreeSet简介" class="headerlink" title="TreeSet简介"></a>TreeSet简介</h1><p>此集合的实现和树结构有关。与HashSet集合类似,TreeSet也是基于Map来实现,具体实现TreeMap,其底层结构为红黑树(特殊的二叉查找树);<br>与HashSet不同的是,TreeSet具有排序功能,分为自然排序(123456)和自定义排序两类,默认是自然排序;在程序中,我们可以按照任意顺序将元素插入到集合中,等到遍历时TreeSet会按照一定顺序输出(倒序/升序);<br>它继承AbstractSet,实现NavigableSet, Cloneable, Serializable接口。<br>(1)与HashSet同理,TreeSet继承AbstractSet类,获得了Set集合基础实现操作;</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>简述HashSet的元素唯一机制</title>
<link href="ayjcsgm.github.io/2019/12/11/%E7%AE%80%E8%BF%B0HashSet%E7%9A%84%E5%85%83%E7%B4%A0%E5%94%AF%E4%B8%80%E6%9C%BA%E5%88%B6/"/>
<id>ayjcsgm.github.io/2019/12/11/%E7%AE%80%E8%BF%B0HashSet%E7%9A%84%E5%85%83%E7%B4%A0%E5%94%AF%E4%B8%80%E6%9C%BA%E5%88%B6/</id>
<published>2019-12-11T12:29:26.000Z</published>
<updated>2019-12-14T11:33:49.321Z</updated>
<content type="html"><![CDATA[<h1 id="HashSet简介"><a href="#HashSet简介" class="headerlink" title="HashSet简介"></a>HashSet简介</h1><p>HashSet实现Set接口,底层由HashMap来实现,为哈希表结构,新增元素相当于HashMap的key,value默认为一个固定的Object。<br>当有元素插入的时候,会计算元素的hashCode值,将元素插入到哈希表对应的位置中来;<br>它继承于AbstractSet,实现了Set, Cloneable, Serializable接口。<br>(1)HashSet继承AbstractSet类,获得了Set接口大部分的实现,减少了实现此接口所需的工作,实际上是又继承了AbstractCollection类;<br>(2)HashSet实现了Set接口,获取Set接口的方法,可以自定义具体实现,也可以继承AbstractSet类中的实现;</p><a id="more"></a> <p>(3)HashSet实现Cloneable,得到了clone()方法,可以实现克隆功能;<br>(4)HashSet实现Serializable,表示可以被序列化。<br>具有如下特点: </p><ul><li>不允许出现重复因素;</li><li>允许插入Null值;</li><li>元素无序(添加顺序和遍历顺序不一致);</li><li>线程不安全,若2个线程同时操作HashSet,必须通过代码实现同步;<h1 id="HashSet基本操作"><a href="#HashSet基本操作" class="headerlink" title="HashSet基本操作"></a>HashSet基本操作</h1>HashSet底层由HashMap实现,插入的元素被当做是HashMap的key,根据hashCode值来确定集合中的位置,由于Set集合中并没有角标的概念,所以并没有像List一样提供get()方法。当获取HashSet中某个元素时,只能通过遍历集合的方式进行equals()比较来实现;<h1 id="HashSet元素添加分析"><a href="#HashSet元素添加分析" class="headerlink" title="HashSet元素添加分析"></a>HashSet元素添加分析</h1>Set集合不允许添加重复元素,实际上是调用了HashMap中的put方法。<br>简单事例:</li></ul><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">public class HashSetTest {</span><br><span class="line"></span><br><span class="line"> public static void main(String[] agrs){</span><br><span class="line"> //hashCode() 和 equals()测试:</span><br><span class="line"> hashCodeAndEquals();</span><br><span class="line"> }</span><br><span class="line"> public static void hashCodeAndEquals(){</span><br><span class="line"> //第一个 Set集合:</span><br><span class="line"> Set<String> set1 = new HashSet<String>();</span><br><span class="line"> String str1 = new String("huangqiuping");</span><br><span class="line"> String str2 = new String("huangqiuping");</span><br><span class="line"> set1.add(str1);</span><br><span class="line"> set1.add(str2);</span><br><span class="line"> System.out.println("长度:"+set1.size()+",内容为:"+set1);</span><br><span class="line"></span><br><span class="line"> //第二个 Set集合:</span><br><span class="line"> Set<App> set2 = new HashSet<App>();</span><br><span class="line"> App app1 = new App();</span><br><span class="line"> app1.setName("huangqiuping");</span><br><span class="line"></span><br><span class="line"> App app2 = new App();</span><br><span class="line"> app2.setName("huangqiuping");</span><br><span class="line"></span><br><span class="line"> set2.add(app1);</span><br><span class="line"> set2.add(app2);</span><br><span class="line"> System.out.println("长度:"+set2.size()+",内容为:"+set2);</span><br><span class="line"></span><br><span class="line"> //第三个 Set集合:</span><br><span class="line"> Set<App> set3 = new HashSet<App>();</span><br><span class="line"> App app3 = new App();</span><br><span class="line"> app3.setName("huangqiuping");</span><br><span class="line"> set3.add(app3);</span><br><span class="line"> set3.add(app3);</span><br><span class="line"> System.out.println("长度:"+set3.size()+",内容为:"+set3);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>测试结果:<br>长度:1,内容为:[huangqiuping]<br>长度:2,内容为:[com.huangqiuping.collection.App@efb78af, com.huangqiuping.collection.App@5f3306ad]<br>长度:1,内容为:[com.huangqiuping.collection.App@1fb030d8]</p><p>可以看到,第一个Set集合中最终只有一个元素;第二个Set集合保留了2个元素;第三个集合也只有1个元素;<br>来看看HashSet的add(E e)方法:</p><figure class="highlight plain"><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><span class="line">public boolean add(E e) {</span><br><span class="line"> return map.put(e, PRESENT)==null;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在底层HashSet调用了HashMap的put(K key, V value)方法:</p><figure class="highlight plain"><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><span class="line">public V put(K key, V value) {</span><br><span class="line"> if (table == EMPTY_TABLE) {</span><br><span class="line"> inflateTable(threshold);</span><br><span class="line"> }</span><br><span class="line"> if (key == null)</span><br><span class="line"> return putForNullKey(value);</span><br><span class="line"> int hash = hash(key);</span><br><span class="line"> int i = indexFor(hash, table.length);</span><br><span class="line"> for (Entry<K,V> e = table[i]; e != null; e = e.next) {</span><br><span class="line"> Object k;</span><br><span class="line"> if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {</span><br><span class="line"> V oldValue = e.value;</span><br><span class="line"> e.value = value;</span><br><span class="line"> e.recordAccess(this);</span><br><span class="line"> return oldValue;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> modCount++;</span><br><span class="line"> addEntry(hash, key, value, i);</span><br><span class="line"> return null;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>通过查看以上的源码,可以了解到:实际的逻辑都是在 <strong>HashMap的put()</strong> 方法中。<br><strong>int hash = hash(key)</strong> 对传入的key计算hash值;<br><strong>int i = indexFor(hash, table.length)</strong> 对hash值进行转换,转换成数组的index(HashMap中底层存储使用了Entry<K,V>[]数组);<br>for (Entry<K,V> e = table[i]; e != null; e = e.next) 判断对应index下是否存在元素;<br>如果存在,则if(e.hash == hash && ((k = e.key) == key || key.equals(k)))判断;<br>如果不存在,则addEntry(hash, key, value, i)直接添加;<br>简单概括如下:<br><strong>在向HashMap中添加元素时,先判断key的hashCode值是否相同,如果相同,则调用equals()、==进行判断,若相同则覆盖原有元素;如果不同,则直接向Map中添加元素</strong>;<br>通过上面的例子可以知道:<br>1、在第一个Set集合中,我们new了两个String对象,赋了相同的值。当传入到HashMap中时,key均为“huangqiuping”,所以hash和i的值都相同。进行if (e.hash == hash && ((k = e.key) == key || key.equals(k)))判断,由于String对象重写了equals()方法,所以在((k = e.key) == key || key.equals(k))判断时,返回了true,所以第二次的插入并不会增加Set集合的长度;<br>2、第二个Set集合中,也是new了两个对象,但没有重写equals()方法(底层调用的Object的equals(),也就是==判断),所以会增加2个元素;<br>3、第三个Set集合中,只new了一个对象,调用的两次add方法都添加的这个新new的对象,所以也只是保留了1个元素;</p>]]></content>
<summary type="html">
<h1 id="HashSet简介"><a href="#HashSet简介" class="headerlink" title="HashSet简介"></a>HashSet简介</h1><p>HashSet实现Set接口,底层由HashMap来实现,为哈希表结构,新增元素相当于HashMap的key,value默认为一个固定的Object。<br>当有元素插入的时候,会计算元素的hashCode值,将元素插入到哈希表对应的位置中来;<br>它继承于AbstractSet,实现了Set, Cloneable, Serializable接口。<br>(1)HashSet继承AbstractSet类,获得了Set接口大部分的实现,减少了实现此接口所需的工作,实际上是又继承了AbstractCollection类;<br>(2)HashSet实现了Set接口,获取Set接口的方法,可以自定义具体实现,也可以继承AbstractSet类中的实现;</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>简单明了的介绍下TreeMap</title>
<link href="ayjcsgm.github.io/2019/12/11/%E7%AE%80%E5%8D%95%E6%98%8E%E4%BA%86%E7%9A%84%E4%BB%8B%E7%BB%8D%E4%B8%8BTreeMap/"/>
<id>ayjcsgm.github.io/2019/12/11/%E7%AE%80%E5%8D%95%E6%98%8E%E4%BA%86%E7%9A%84%E4%BB%8B%E7%BB%8D%E4%B8%8BTreeMap/</id>
<published>2019-12-11T12:29:02.000Z</published>
<updated>2019-12-14T11:34:09.883Z</updated>
<content type="html"><![CDATA[<h1 id="TreeMap简介"><a href="#TreeMap简介" class="headerlink" title="TreeMap简介"></a>TreeMap简介</h1><p>在Map集合框架中,除了HashMap以外,TreeMap也是常用到的集合对象之一。<br>与HashMap相比,TreeMap是一个能比较元素大小的Map集合,会对传入的key进行了大小排序。其中,可以使用元素的自然顺序,也可以使用集合中自定义的比较器来进行排序;<br>不同于HashMap的哈希映射,TreeMap实现了红黑树的结构,形成了一颗二叉树。</p><a id="more"></a> <p><img src="https://img-blog.csdnimg.cn/20191211103308557.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>TreeMap继承于AbstractMap,实现了Map, Cloneable, NavigableMap, Serializable接口。<br>(1)TreeMap 继承于AbstractMap,而AbstractMap实现了Map接口,并实现了Map接口中定义的方法,减少了其子类继承的复杂度;<br>(2)TreeMap 实现了Map接口,成为Map框架中的一员,可以包含着key-value形式的元素;<br>(3)TreeMap 实现了NavigableMap接口,意味着拥有了更强的元素搜索能力;<br>(4)TreeMap 实现了Cloneable接口,实现了clone()方法,可以被克隆;<br>(5)TreeMap 实现了Java.io.Serializable接口,支持序列化操作;<br>TreeMap具有如下特点:</p><ul><li>不允许出现重复的key;</li><li>可以插入null键,null值;</li><li>可以对元素进行排序;</li><li>无序集合(插入和遍历顺序不一致);</li></ul><h1 id="TreeMap基本操作"><a href="#TreeMap基本操作" class="headerlink" title="TreeMap基本操作"></a>TreeMap基本操作</h1><figure class="highlight plain"><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><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line">public class TreeMapTest {</span><br><span class="line"> public static void main(String[] agrs){</span><br><span class="line"> //创建TreeMap对象:</span><br><span class="line"> TreeMap<String,Integer> treeMap = new TreeMap<String,Integer>();</span><br><span class="line"> System.out.println("初始化后,TreeMap元素个数为:" + treeMap.size());</span><br><span class="line"></span><br><span class="line"> //新增元素:</span><br><span class="line"> treeMap.put("hello",1);</span><br><span class="line"> treeMap.put("world",2);</span><br><span class="line"> treeMap.put("my",3);</span><br><span class="line"> treeMap.put("name",4);</span><br><span class="line"> treeMap.put("is",5);</span><br><span class="line"> treeMap.put("huangqiuping",6);</span><br><span class="line"> treeMap.put("i",6);</span><br><span class="line"> treeMap.put("am",6);</span><br><span class="line"> treeMap.put("a",6);</span><br><span class="line"> treeMap.put("developer",6);</span><br><span class="line"> System.out.println("添加元素后,TreeMap元素个数为:" + treeMap.size());</span><br><span class="line"></span><br><span class="line"> //遍历元素:</span><br><span class="line"> Set<Map.Entry<String,Integer>> entrySet = treeMap.entrySet();</span><br><span class="line"> for(Map.Entry<String,Integer> entry : entrySet){</span><br><span class="line"> String key = entry.getKey();</span><br><span class="line"> Integer value = entry.getValue();</span><br><span class="line"> System.out.println("TreeMap元素的key:"+key+",value:"+value);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //获取所有的key:</span><br><span class="line"> Set<String> keySet = treeMap.keySet();</span><br><span class="line"> for(String strKey:keySet){</span><br><span class="line"> System.out.println("TreeMap集合中的key:"+strKey);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //获取所有的value:</span><br><span class="line"> Collection<Integer> valueList = treeMap.values();</span><br><span class="line"> for(Integer intValue:valueList){</span><br><span class="line"> System.out.println("TreeMap集合中的value:" + intValue);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //获取元素:</span><br><span class="line"> //获取集合内元素key为"huangqiuping"的值</span><br><span class="line"> Integer getValue = treeMap.get("huangqiuping");</span><br><span class="line"> //获取集合内第一个元素</span><br><span class="line"> String firstKey = treeMap.firstKey();</span><br><span class="line"> //获取集合内最后一个元素</span><br><span class="line"> String lastKey =treeMap.lastKey();</span><br><span class="line"> //获取集合内的key小于"huangqiuping"的key</span><br><span class="line"> String lowerKey =treeMap.lowerKey("huangqiuping");</span><br><span class="line"> //获取集合内的key大于等于"huangqiuping"的key</span><br><span class="line"> String ceilingKey =treeMap.ceilingKey("huangqiuping");</span><br><span class="line"> //获取集合的key从"a"到"huangqiuping"的元素</span><br><span class="line"> SortedMap<String,Integer> sortedMap =treeMap.subMap("a","my");</span><br><span class="line"></span><br><span class="line"> //删除元素:</span><br><span class="line"> //删除集合中key为"huangqiuping"的元素</span><br><span class="line"> Integer removeValue = treeMap.remove("huangqiuping");</span><br><span class="line"> //清空集合元素:</span><br><span class="line"> treeMap.clear(); </span><br><span class="line"></span><br><span class="line"> //判断方法:</span><br><span class="line"> //判断集合是否为空</span><br><span class="line"> boolean isEmpty = treeMap.isEmpty();</span><br><span class="line"> //判断集合的key中是否包含"huangqiuping"</span><br><span class="line"> boolean isContain = treeMap.containsKey("huangqiuping");</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="TreeMap排序"><a href="#TreeMap排序" class="headerlink" title="TreeMap排序"></a>TreeMap排序</h1><p>(1)使用元素自然排序<br>在使用自然顺序排序时候,需要区分两种情况:一种是Jdk定义的对象,一种是自己定义的对象;</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">public class SortedTest implements Comparable<SortedTest> {</span><br><span class="line"> private int age;</span><br><span class="line"> public SortedTest(int age){</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line"> public int getAge() {</span><br><span class="line"> return age;</span><br><span class="line"> }</span><br><span class="line"> public void setAge(int age) {</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line"> //自定义对象,实现compareTo(T o)方法:</span><br><span class="line"> public int compareTo(SortedTest sortedTest) {</span><br><span class="line"> int num = this.age - sortedTest.getAge();</span><br><span class="line"> //为0时候,两者相同:</span><br><span class="line"> if(num==0){</span><br><span class="line"> return 0;</span><br><span class="line"> //大于0时,传入的参数小:</span><br><span class="line"> }else if(num>0){</span><br><span class="line"> return 1;</span><br><span class="line"> //小于0时,传入的参数大:</span><br><span class="line"> }else{</span><br><span class="line"> return -1;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">public class TreeMapTest {</span><br><span class="line"> public static void main(String[] agrs){</span><br><span class="line"> //自然顺序比较</span><br><span class="line"> naturalSort();</span><br><span class="line"> }</span><br><span class="line"> //自然排序顺序:</span><br><span class="line"> public static void naturalSort(){</span><br><span class="line"> //第一种情况:Integer对象</span><br><span class="line"> TreeMap<Integer,String> treeMapFirst = new TreeMap<Integer, String>();</span><br><span class="line"> treeMapFirst.put(1,"huangqiuping");</span><br><span class="line"> treeMapFirst.put(6,"huangqiuping");</span><br><span class="line"> treeMapFirst.put(3,"huangqiuping");</span><br><span class="line"> treeMapFirst.put(10,"huangqiuping");</span><br><span class="line"> treeMapFirst.put(7,"huangqiuping");</span><br><span class="line"> treeMapFirst.put(13,"huangqiuping");</span><br><span class="line"> System.out.println(treeMapFirst.toString());</span><br><span class="line"></span><br><span class="line"> //第二种情况:SortedTest对象</span><br><span class="line"> TreeMap<SortedTest,String> treeMapSecond = new TreeMap<SortedTest, String>();</span><br><span class="line"> treeMapSecond.put(new SortedTest(10),"huangqiuping");</span><br><span class="line"> treeMapSecond.put(new SortedTest(1),"huangqiuping");</span><br><span class="line"> treeMapSecond.put(new SortedTest(13),"huangqiuping");</span><br><span class="line"> treeMapSecond.put(new SortedTest(4),"huangqiuping");</span><br><span class="line"> treeMapSecond.put(new SortedTest(0),"huangqiuping");</span><br><span class="line"> treeMapSecond.put(new SortedTest(9),"huangqiuping");</span><br><span class="line"> System.out.println(treeMapSecond.toString());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在自然顺序比较中,需要让被比较的元素实现Comparable接口,否则在向集合里添加元素时报:”java.lang.ClassCastException: com.huangqiuping.collection.map.SortedTest cannot be cast to java.lang.Comparable”异常;<br>这是因为在调用put()方法时,会将传入的元素转化成Comparable类型对象,所以当你传入的元素没有实现Comparable接口时,就无法转换,遍会报错;<br>(2)使用自定义比较器排序<br>使用自定义比较器排序,需要在创建TreeMap对象时,将自定义比较器对象传入到TreeMap构造方法中;<br>自定义比较器对象,需要实现Comparator接口,并实现比较方法compare(To1,To2);<br>使用自定义比较器排序的话,被比较的对象无需再实现Comparable接口了;</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">public class SortedTest {</span><br><span class="line"> private int age;</span><br><span class="line"> public SortedTest(int age){</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line"> public int getAge() {</span><br><span class="line"> return age;</span><br><span class="line"> }</span><br><span class="line"> public void setAge(int age) {</span><br><span class="line"> this.age = age;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">public class SortedTestComparator implements Comparator<SortedTest> {</span><br><span class="line"> //自定义比较器:实现compare(To1,To2)方法:</span><br><span class="line"> public int compare(SortedTest sortedTest1, SortedTest sortedTest2) {</span><br><span class="line"> int num = sortedTest1.getAge() - sortedTest2.getAge();</span><br><span class="line"> if(num==0){//为0时候,两者相同:</span><br><span class="line"> return 0;</span><br><span class="line"> }else if(num>0){//大于0时,后面的参数小:</span><br><span class="line"> return 1;</span><br><span class="line"> }else{//小于0时,前面的参数小:</span><br><span class="line"> return -1;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">public class TreeMapTest {</span><br><span class="line"> public static void main(String[] agrs){</span><br><span class="line"> //自定义顺序比较</span><br><span class="line"> customSort();</span><br><span class="line"> }</span><br><span class="line"> //自定义排序顺序:</span><br><span class="line"> public static void customSort(){</span><br><span class="line"> TreeMap<SortedTest,String> treeMap = new TreeMap<SortedTest, String>(new SortedTestComparator());</span><br><span class="line"> treeMap.put(new SortedTest(10),"hello");</span><br><span class="line"> treeMap.put(new SortedTest(21),"my");</span><br><span class="line"> treeMap.put(new SortedTest(15),"name");</span><br><span class="line"> treeMap.put(new SortedTest(2),"is");</span><br><span class="line"> treeMap.put(new SortedTest(1),"huangqiuping");</span><br><span class="line"> treeMap.put(new SortedTest(7),"world");</span><br><span class="line"> System.out.println(treeMap.toString());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h1 id="TreeMap简介"><a href="#TreeMap简介" class="headerlink" title="TreeMap简介"></a>TreeMap简介</h1><p>在Map集合框架中,除了HashMap以外,TreeMap也是常用到的集合对象之一。<br>与HashMap相比,TreeMap是一个能比较元素大小的Map集合,会对传入的key进行了大小排序。其中,可以使用元素的自然顺序,也可以使用集合中自定义的比较器来进行排序;<br>不同于HashMap的哈希映射,TreeMap实现了红黑树的结构,形成了一颗二叉树。</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>解析JDK8中Arrays.sort底层原理及其排序算法的选择</title>
<link href="ayjcsgm.github.io/2019/12/11/%E8%A7%A3%E6%9E%90JDK8%E4%B8%ADArrays-sort%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E5%8F%8A%E5%85%B6%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E9%80%89%E6%8B%A9/"/>
<id>ayjcsgm.github.io/2019/12/11/%E8%A7%A3%E6%9E%90JDK8%E4%B8%ADArrays-sort%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E5%8F%8A%E5%85%B6%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E9%80%89%E6%8B%A9/</id>
<published>2019-12-11T12:26:13.000Z</published>
<updated>2019-12-14T11:34:35.715Z</updated>
<content type="html"><![CDATA[<p>暂时网上看过很多JDK8中Arrays.sort的底层原理,有些说是插入排序,有些说是归并排序,也有说大于域值用计数排序法,否则就使用插入排序。。。其实不全对。让我们对着源码分析个究竟:</p><a id="more"></a> <figure class="highlight plain"><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><span class="line"> // Use Quicksort on small arrays</span><br><span class="line"> if (right - left < QUICKSORT_THRESHOLD) {//QUICKSORT_THRESHOLD = 286</span><br><span class="line"> sort(a, left, right, true);</span><br><span class="line"> return;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>数组一进来,会碰到第一个阀值QUICKSORT_THRESHOLD(286),注解上说,小过这个阀值的进入Quicksort (快速排序),其实并不全是,点进去sort(a, left, right, true);方法能看见:</p><figure class="highlight plain"><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><span class="line">// Use insertion sort on tiny arrays</span><br><span class="line"> if (length < INSERTION_SORT_THRESHOLD) {//INSERTION_SORT_THRESHOLD=47</span><br><span class="line"> if (leftmost) ……</span><br></pre></td></tr></table></figure><p>我们看到第二个阀值INSERTION_SORT_THRESHOLD(47),如果元素少于47这个阀值,就用插入排序,往下看确实如此:</p><figure class="highlight plain"><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><span class="line">/*</span><br><span class="line"> * Traditional (without sentinel) insertion sort,</span><br><span class="line"> * optimized for server VM, is used in case of</span><br><span class="line"> * the leftmost part.</span><br><span class="line"> */</span><br><span class="line"> for (int i = left, j = i; i < right; j = ++i) {</span><br><span class="line"> int ai = a[i + 1];</span><br><span class="line"> while (ai < a[j]) {</span><br><span class="line"> a[j + 1] = a[j];</span><br><span class="line"> if (j-- == left) {</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> a[j + 1] = ai;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>插入排序动图演示:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2dpZi9mRXNXa1ZyU2s1NHB6ZTRjUWRYd002aWJmR3p5cGRwR2hEZmQ3TjZwSlJpY1R6ZmljTlZMSHJUTDJoNjdRNmgyQjk4R2ljMWZyOTZ5Nm50R2liYkRrajMza2lhQS82NDA?x-oss-process=image/format,png" alt="在这里插入图片描述"><br>元素少于47用插入排序</p><p>至于大过INSERTION_SORT_THRESHOLD(47)的,用一种快速排序的方法:<br>1.从数列中挑出五个元素,称为 “基准”(pivot);<br>2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;<br>3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。</p><p>快速排序动图演示:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2dpZi9mRXNXa1ZyU2s1NHB6ZTRjUWRYd002aWJmR3p5cGRwR2hwMnNubkZiR0pIZEJvUTJYTEdyTVE5cFZTanZPUE1jOWliQUgxWmh6VXhUZWtjQ0J1U2hOdUR3LzY0MA?x-oss-process=image/format,png" alt="在这里插入图片描述"></p><p>这是少于阀值QUICKSORT_THRESHOLD(286)的两种情况,至于大于286的,它会进入归并排序(Merge Sort),但在此之前,它有个小动作:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">// Check if the array is nearly sorted</span><br><span class="line">for (int k = left; k < right; run[count] = k) {</span><br><span class="line"> if (a[k] < a[k + 1]) { // ascending</span><br><span class="line"> while (++k <= right && a[k - 1] <= a[k]);</span><br><span class="line"> } else if (a[k] > a[k + 1]) { // descending</span><br><span class="line"> while (++k <= right && a[k - 1] >= a[k]);</span><br><span class="line"> for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {</span><br><span class="line"> int t = a[lo]; a[lo] = a[hi]; a[hi] = t;</span><br><span class="line"> }</span><br><span class="line"> } else { // equal</span><br><span class="line"> for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {</span><br><span class="line"> if (--m == 0) {</span><br><span class="line"> sort(a, left, right, true);</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /*</span><br><span class="line"> * The array is not highly structured,</span><br><span class="line"> * use Quicksort instead of merge sort.</span><br><span class="line"> */</span><br><span class="line"> if (++count == MAX_RUN_COUNT) {</span><br><span class="line"> sort(a, left, right, true);</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里主要作用是看他数组具不具备结构:实际逻辑是分组排序,每降序为一个组,像1,9,8,7,6,8。9到6是降序,为一个组,然后把降序的一组排成升序:1,6,7,8,9,8。然后最后的8后面继续往后面找。。。</p><p>每遇到这样一个降序组,++count,当count大于MAX_RUN_COUNT(67),被判断为这个数组不具备结构(也就是这数据时而升时而降),然后送给之前的sort(里面的快速排序)的方法(The array is not highly structured,use Quicksort instead of merge sort.)。</p><p>如果count少于MAX_RUN_COUNT(67)的,说明这个数组还有点结构,就继续往下走下面的归并排序:</p><figure class="highlight plain"><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><span class="line">// Determine alternation base for merge</span><br><span class="line">byte odd = 0; </span><br><span class="line">for (int n = 1; (n <<= 1) < count;odd ^= 1);</span><br></pre></td></tr></table></figure><p>从这里开始,正式进入归并排序(Merge Sort)!</p><figure class="highlight plain"><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><span class="line">// Merging</span><br><span class="line">for (int last; count > 1; count = last) {</span><br><span class="line"> for (int k = (last = 0) + 2; k <= count; k += 2) {</span><br><span class="line"> int hi = run[k], mi = run[k - 1];</span><br><span class="line"> for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {</span><br><span class="line"> if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {</span><br><span class="line"> b[i + bo] = a[p++ + ao];</span><br><span class="line"> } else {</span><br><span class="line"> b[i + bo] = a[q++ + ao];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> run[++last] = hi;</span><br><span class="line"> }</span><br><span class="line"> if ((count & 1) != 0) {</span><br><span class="line"> for (int i = right, lo = run[count - 1]; --i >= lo;</span><br><span class="line"> b[i + bo] = a[i + ao]</span><br><span class="line"> );</span><br><span class="line"> run[++last] = right;</span><br><span class="line"> }</span><br><span class="line"> int[] t = a; a = b; b = t;</span><br><span class="line"> int o = ao; ao = bo; bo = o;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>归并排序动图演示:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9tbWJpei5xcGljLmNuL21tYml6X2dpZi9mRXNXa1ZyU2s1NHB6ZTRjUWRYd002aWJmR3p5cGRwR2h4WUtndlFEcVhMbFVTdmZQbE5CQW1saWJkOVRPUkZkY0hhbmRSQ2RYWWF5aWFKeWliYUpBMldybXcvNjQw?x-oss-process=image/format,png" alt="在这里插入图片描述"><br>总结:<br>从上面分析,Arrays.sort并不是单一的排序,而是插入排序,快速排序,归并排序三种排序的组合,为此我画了个流程图:<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9pbWcyLm11a2V3YW5nLmNvbS81ZDVkZmMwYTAwMDEwNDcyMDUwMDA1OTMucG5n?x-oss-process=image/format,png" alt="在这里插入图片描述"></p><p>算法的选择:</p><p>PS:关于排序算法的文章,推荐这两篇,个人觉得写得挺好,容易入门:<br><a href="https://mp.weixin.qq.com/s/t0dsJeN397wO41pwBWPeTg" target="_blank" rel="noopener">https://mp.weixin.qq.com/s/t0dsJeN397wO41pwBWPeTg</a><br><a href="https://www.cnblogs.com/huangbw/p/7398418.html" target="_blank" rel="noopener">https://www.cnblogs.com/huangbw/p/7398418.html</a></p><p>稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;</p><p>不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;</p><p>时间复杂度按n越大算法越复杂来排的话:常数阶O(1)、对数阶O(logn)、线性阶O(n)、线性对数阶O(nlogn)、平方阶O(n²)、立方阶O(n³)、……k次方阶O(n的k次方)、指数阶O(2的n次方)。</p><p>O(nlogn)只代表增长量级,同一个量级前面的常数也可以不一样,不同数量下面的实际运算时间也可以不一样。数量非常小的情况下(就像上面说到的,少于47的),插入排序等可能会比快速排序更快。<br>所以数组少于47的会进入插入排序。</p><p>快排数据越无序越快(加入随机化后基本不会退化),平均常数最小,不需要额外空间,不稳定排序。<br>归排速度稳定,常数比快排略大,需要额外空间,稳定排序。<br>所以大于或等于47或少于286会进入快排,而在大于或等于286后,会有个小动作:“// Check if the array is nearly sorted”。<br>这里第一个作用是先梳理一下数据方便后续的归并排序,第二个作用就是即便大于286,但在降序组太多的时候(被判断为没有结构的数据,The array is not highly structured,use Quicksort instead of merge sort.),要转回快速排序。</p><hr>这就是jdk8中Arrays.sort的底层原理,自己在研究和分析中学到很多,希望能给各位工作中或面试中一些启发和帮助!Thanks for watching!]]></content>
<summary type="html">
<p>暂时网上看过很多JDK8中Arrays.sort的底层原理,有些说是插入排序,有些说是归并排序,也有说大于域值用计数排序法,否则就使用插入排序。。。其实不全对。让我们对着源码分析个究竟:</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>为什么 String 的 hashCode 选择 31 作为乘子</title>
<link href="ayjcsgm.github.io/2019/12/11/%E4%B8%BA%E4%BB%80%E4%B9%88-String-%E7%9A%84-hashCode-%E9%80%89%E6%8B%A9-31-%E4%BD%9C%E4%B8%BA%E4%B9%98%E5%AD%90/"/>
<id>ayjcsgm.github.io/2019/12/11/%E4%B8%BA%E4%BB%80%E4%B9%88-String-%E7%9A%84-hashCode-%E9%80%89%E6%8B%A9-31-%E4%BD%9C%E4%B8%BA%E4%B9%98%E5%AD%90/</id>
<published>2019-12-11T12:25:54.000Z</published>
<updated>2019-12-14T11:35:04.141Z</updated>
<content type="html"><![CDATA[<p>某天,我在写代码的时候,无意中点开了 String hashCode 方法。然后大致看了一下 hashCode 的实现,发现并不是很复杂。但是我从源码中发现了一个奇怪的数字,也就是本文的主角31。这个数字居然不是用常量声明的,所以没法从字面意思上推断这个数字的用途。后来带着疑问和好奇心,到网上去找资料查询一下。在看完资料后,默默的感叹了一句,原来是这样啊。那么到底是哪样呢?在接下来章节里,请大家带着好奇心和我揭开数字31的用途之谜。</p><a id="more"></a> <h4 id="选择31的原因"><a href="#选择31的原因" class="headerlink" title="选择31的原因"></a>选择31的原因</h4><p>在详细说明 String hashCode 方法选择数字31的作为乘子的原因之前,我们先来看看 String hashCode 方法是怎样实现的,如下:</p><figure class="highlight plain"><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><span class="line">public int hashCode() {</span><br><span class="line"> int h = hash;</span><br><span class="line"> if (h == 0 && value.length > 0) {</span><br><span class="line"> char val[] = value;</span><br><span class="line"></span><br><span class="line"> for (int i = 0; i < value.length; i++) {</span><br><span class="line"> h = 31 * h + val[i];</span><br><span class="line"> }</span><br><span class="line"> hash = h;</span><br><span class="line"> }</span><br><span class="line"> return h;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面的代码就是 String hashCode 方法的实现,是不是很简单。实际上 hashCode 方法核心的计算逻辑只有三行,也就是代码中的 for 循环。我们可以由上面的 for 循环推导出一个计算公式,hashCode 方法注释中已经给出。如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]</span><br></pre></td></tr></table></figure><p>这里说明一下,上面的 s 数组即源码中的 val 数组,是 String 内部维护的一个 char 类型数组。这里我来简单推导一下这个公式:</p><figure class="highlight plain"><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><span class="line">假设 n=3</span><br><span class="line">i=0 -> h = 31 * 0 + val[0]</span><br><span class="line">i=1 -> h = 31 * (31 * 0 + val[0]) + val[1]</span><br><span class="line">i=2 -> h = 31 * (31 * (31 * 0 + val[0]) + val[1]) + val[2]</span><br><span class="line"> h = 31*31*31*0 + 31*31*val[0] + 31*val[1] + val[2]</span><br><span class="line"> h = 31^(n-1)*val[0] + 31^(n-2)*val[1] + val[2]</span><br></pre></td></tr></table></figure><p>上面的公式包括公式的推导并不是本文的重点,大家了解了解即可。接下来来说说本文的重点,即选择31的理由。从网上的资料来看,一般有如下两个原因:</p><p>第一 31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一。另外一些相近的质数,比如37、41、43等等,也都是不错的选择。那么为啥偏偏选中了31呢?请看第二个原因。</p><p>第二 31可以被 JVM 优化,<strong>31 * i = (i << 5) - i</strong>。</p><p>上面两个原因中,第一个需要解释一下,第二个比较简单,就不说了。下面我来解释第一个理由。一般在设计哈希算法时,会选择一个特殊的质数。至于为啥选择质数,我想应该是可以降低哈希算法的冲突率。至于原因,这个就要问数学家了,我几乎可以忽略的数学水平解释不了这个原因。上面说到,31是一个不大不小的质数,是优选乘子。那为啥同是质数的2和101(或者更大的质数)就不是优选乘子呢,分析如下。</p><p>这里先分析质数2。首先,假设<strong>n = 6</strong>,然后把质数2和 n 带入上面的计算公式。并仅计算公式中次数最高的那一项,结果是<strong>2^5 = 32</strong>,是不是很小。所以这里可以断定,当字符串长度不是很长时,用质数2做为乘子算出的哈希值,数值不会很大。也就是说,哈希值会分布在一个较小的数值区间内,分布性不佳,最终可能会导致冲突率上升。</p><p>上面说了,质数2做为乘子会导致哈希值分布在一个较小区间内,那么如果用一个较大的大质数101会产生什么样的结果呢?根据上面的分析,我想大家应该可以猜出结果了。就是不用再担心哈希值会分布在一个小的区间内了,因为<strong>101^5 = 10,510,100,501</strong>。但是要注意的是,这个计算结果太大了。如果用 int 类型表示哈希值,结果会溢出,最终导致数值信息丢失。尽管数值信息丢失并不一定会导致冲突率上升,但是我们暂且先认为质数101(或者更大的质数)也不是很好的选择。最后,我们再来看看质数31的计算结果:<strong>31^5 = 28629151</strong>,结果值相对于<strong>32和10,510,100,501</strong>来说。是不是很nice,不大不小。</p><p>上面用了比较简陋的数学手段证明了数字31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一。</p><p>接下来我会用详细的实验来验证上面的结论,不过在验证前,我们先看看 Stack Overflow 上关于这个问题的讨论,<a href="https://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier" target="_blank" rel="noopener">Why does Java’s hashCode() in String use 31 as a multiplier?</a> 。其中排名第一的答案引用了《Effective Java》中的一段话,这里也引用一下:</p><blockquote><p>The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting. The advantage of using a prime is less clear, but it is traditional. A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance: 31 * i == (i << 5) - i. Modern VMs do this sort of optimization automatically.</p></blockquote><p>简单翻译一下:</p><blockquote><p>选择数字31是因为它是一个奇质数,如果选择一个偶数会在乘法运算中产生溢出,导致数值信息丢失,因为乘二相当于移位运算。选择质数的优势并不是特别的明显,但这是一个传统。同时,数字31有一个很好的特性,即乘法运算可以被移位和减法运算取代,来获取更好的性能:31 * i == (i << 5) - i,现代的 Java 虚拟机可以自动的完成这个优化。</p></blockquote><p>排名第二的答案设这样说的:</p><blockquote><p>As Goodrich and Tamassia point out, If you take over 50,000 English<br>words (formed as the union of the word lists provided in two variants<br>of Unix), using the constants 31, 33, 37, 39, and 41 will produce less<br>than 7 collisions in each case. Knowing this, it should come as no<br>surprise that many Java implementations choose one of these constants.</p></blockquote><p>这段话也翻译一下:</p><blockquote><p>正如 Goodrich 和 Tamassia 指出的那样,如果你对超过 50,000 个英文单词(由两个不同版本的 Unix<br>字典合并而成)进行 hash code 运算,并使用常数 31, 33, 37, 39 和 41<br>作为乘子,每个常数算出的哈希值冲突数都小于7个,所以在上面几个常数中,常数 31 被 Java 实现所选用也就不足为奇了。</p></blockquote><p>上面的两个答案完美的解释了 Java 源码中选用数字 31 的原因。接下来,我将针对第二个答案就行验证,请大家继续往下看。</p><h5 id="实验及数据可视化"><a href="#实验及数据可视化" class="headerlink" title="实验及数据可视化"></a>实验及数据可视化</h5><p>本节,我将使用不同的数字作为乘子,对超过23万个英文单词进行哈希运算,并计算哈希算法的冲突率。同时,我也将针对不同乘子算出的哈希值分布情况进行可视化处理,让大家可以直观的看到数据分布情况。本次实验所使用的数据是 Unix/Linux 平台中的英文字典文件,文件路径为 <strong>/usr/share/dict/words</strong>。</p><p><strong>哈希值冲突率计算</strong></p><p>计算哈希算法冲突率并不难,比如可以一次性将所有单词的 hash code 算出,并放入 Set 中去除重复值。之后拿单词数减去 set.size() 即可得出冲突数,有了冲突数,冲突率就可以算出来了。当然,如果使用 JDK8 提供的流式计算 API,则可更方便算出,代码片段如下:</p><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">public static Integer hashCode(String str, Integer multiplier) {</span><br><span class="line"> int hash = 0;</span><br><span class="line"> for (int i = 0; i < str.length(); i++) {</span><br><span class="line"> hash = multiplier * hash + str.charAt(i);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return hash;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * 计算 hash code 冲突率,顺便分析一下 hash code 最大值和最小值,并输出</span><br><span class="line"> * @param multiplier</span><br><span class="line"> * @param hashs</span><br><span class="line"> */</span><br><span class="line">public static void calculateConflictRate(Integer multiplier, List<Integer> hashs) {</span><br><span class="line"> Comparator<Integer> cp = (x, y) -> x > y ? 1 : (x < y ? -1 : 0);</span><br><span class="line"> int maxHash = hashs.stream().max(cp).get();</span><br><span class="line"> int minHash = hashs.stream().min(cp).get();</span><br><span class="line"></span><br><span class="line"> // 计算冲突数及冲突率</span><br><span class="line"> int uniqueHashNum = (int) hashs.stream().distinct().count();</span><br><span class="line"> int conflictNum = hashs.size() - uniqueHashNum;</span><br><span class="line"> double conflictRate = (conflictNum * 1.0) / hashs.size();</span><br><span class="line"></span><br><span class="line"> System.out.println(String.format("multiplier=%4d, minHash=%11d, maxHash=%10d, conflictNum=%6d, conflictRate=%.4f%%",</span><br><span class="line"> multiplier, minHash, maxHash, conflictNum, conflictRate * 100));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>结果如下:<br><img src="https://img-blog.csdnimg.cn/20191211161427272.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>从上图可以看出,使用较小的质数做为乘子时,冲突率会很高。尤其是质数2,冲突率达到了 55.14%。同时我们注意观察质数2作为乘子时,哈希值的分布情况。可以看得出来,哈希值分布并不是很广,仅仅分布在了整个哈希空间的正半轴部分,即 0 ~ 231-1。而负半轴 -231 ~ -1,则无分布。</p><p>这也证明了我们上面断言,即质数2作为乘子时,对于短字符串,生成的哈希值分布性不佳。然后再来看看我们之前所说的 31、37、41 这三个不大不小的质数,表现都不错,冲突数都低于7个。而质数 101 和 199 表现的也很不错,冲突率很低,这也说明哈希值溢出并不一定会导致冲突率上升。但是这两个家伙一言不合就溢出,我们认为他们不是哈希算法的优选乘子。最后我们再来看看 32 和 36 这两个偶数的表现,结果并不好,尤其是 32,冲突率超过了了50%。尽管 36 表现的要好一点,不过和 31,37相比,冲突率还是比较高的。当然并非所有的偶数作为乘子时,冲突率都会比较高,大家有兴趣可以自己验证。</p><p><strong>哈希值分布可视化</strong></p><p>上一节分析了不同数字作为乘子时的冲突率情况,这一节来分析一下不同数字作为乘子时,哈希值的分布情况。在详细分析之前,我先说说哈希值可视化的过程。我原本是打算将所有的哈希值用一维散点图进行可视化,但是后来找了一圈,也没找到合适的画图工具。加之后来想了想,一维散点图可能不合适做哈希值可视化,因为这里有超过23万个哈希值。也就意味着会在图上显示超过23万个散点,如果不出意外的话,这23万个散点会聚集的很密,有可能会变成一个大黑块,就失去了可视化的意义了。</p><p>所以这里选择了另一种可视化效果更好的图表,也就是 excel 中的平滑曲线的二维散点图(下面简称散点曲线图)。当然这里同样没有把23万散点都显示在图表上,太多了。所以在实际绘图过程中,我将哈希空间等分成了64个子区间,并统计每个区间内的哈希值数量。最后将分区编号做为X轴,哈希值数量为Y轴,就绘制出了我想要的二维散点曲线图了。</p><p>这里举个例子说明一下吧,以第0分区为例。第0分区数值区间是[-2147483648, -2080374784),我们统计落在该数值区间内哈希值的数量,得到 <分区编号, 哈希值数量> 数值对,这样就可以绘图了。分区代码如下:</p><figure class="highlight plain"><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><span class="line">/**</span><br><span class="line"> * 将整个哈希空间等分成64份,统计每个空间内的哈希值数量</span><br><span class="line"> * @param hashs</span><br><span class="line"> */</span><br><span class="line">public static Map<Integer, Integer> partition(List<Integer> hashs) {</span><br><span class="line"> // step = 2^32 / 64 = 2^26</span><br><span class="line"> final int step = 67108864;</span><br><span class="line"> List<Integer> nums = new ArrayList<>();</span><br><span class="line"> Map<Integer, Integer> statistics = new LinkedHashMap<>();</span><br><span class="line"> int start = 0;</span><br><span class="line"> for (long i = Integer.MIN_VALUE; i <= Integer.MAX_VALUE; i += step) {</span><br><span class="line"> final long min = i;</span><br><span class="line"> final long max = min + step;</span><br><span class="line"> int num = (int) hashs.parallelStream()</span><br><span class="line"> .filter(x -> x >= min && x < max).count();</span><br><span class="line"></span><br><span class="line"> statistics.put(start++, num);</span><br><span class="line"> nums.add(num);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 为了防止计算出错,这里验证一下</span><br><span class="line"> int hashNum = nums.stream().reduce((x, y) -> x + y).get();</span><br><span class="line"> assert hashNum == hashs.size();</span><br><span class="line"></span><br><span class="line"> return statistics;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>本文中的哈希值是用整形表示的,整形的数值区间是 [-2147483648, 2147483647],区间大小为 2^32。所以这里可以将区间等分成64个子区间,每个自子区间大小为 2^26。详细的分区对照表如下:<br><img src="https://img-blog.csdnimg.cn/20191211161648353.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>接下来,让我们按照分区,对数字2、3、17、31、101的散点曲线图进行简单的分析。先从数字2开始,数字2对于的散点曲线图如下:<br><img src="https://img-blog.csdnimg.cn/20191211161923556.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>上面的图还是很一目了然的,乘子2算出的哈希值几乎全部落在第32分区,也就是 [0, 67108864)数值区间内,落在其他区间内的哈希值数量几乎可以忽略不计。这也就不难解释为什么数字2作为乘子时,算出哈希值的冲突率如此之高的原因了。所以这样的哈希算法要它有何用啊,拖出去斩了吧。接下来看看数字3作为乘子时的表现:</p><p><img src="https://img-blog.csdnimg.cn/20191211161934784.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>3作为乘子时,算出的哈希值分布情况和2很像,只不过稍微好了那么一点点。从图中可以看出绝大部分的哈希值最终都落在了第32分区里,哈希值的分布性很差。这个也没啥用,拖出去枪毙5分钟吧。在看看数字17的情况怎么样:<br><img src="https://img-blog.csdnimg.cn/2019121116194580.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p>数字17作为乘子时的表现,明显比上面两个数字好点了。虽然哈希值在第32分区和第34分区有一定的聚集,但是相比较上面2和3,情况明显好好了很多。除此之外,17作为乘子算出的哈希值在其他区也均有分布,且较为均匀,还算是一个不错的乘子吧。<br><img src="https://img-blog.csdnimg.cn/20191211162026276.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></p><p>接下来来看看我们本文的主角31了,31作为乘子算出的哈希值在第33分区有一定的小聚集。不过相比于数字17,主角31的表现又好了一些。首先是哈希值的聚集程度没有17那么严重,其次哈希值在其他区分布的情况也要好于17。总之,选31,准没错啊。<br><img src="https://img-blog.csdnimg.cn/20191211162041824.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>最后再来看看大质数101的表现,不难看出,质数101作为乘子时,算出的哈希值分布情况要好于主角31,有点喧宾夺主的意思。不过不可否认的是,质数101的作为乘子时,哈希值的分布性确实更加均匀。所以如果不在意质数101容易导致数据信息丢失问题,或许其是一个更好的选择。</p><p><strong>写在最后</strong></p><p>经过上面的分析与实践,我想大家应该明白了 String hashCode 方法中选择使用数字31作为乘子的原因了。本文本质是一篇简单的科普文而已,并没有银弹。如果大家读完后觉得又涨知识了,那这篇文章的目的就达到了。最后,本篇文章的配图画的还是很辛苦的,所以如果大家觉得文章不错,不妨就给个赞吧,就当是对我的鼓励了。</p><p>另外,如果文章中有不妥或者错误的地方,也欢迎指出来。</p>]]></content>
<summary type="html">
<p>某天,我在写代码的时候,无意中点开了 String hashCode 方法。然后大致看了一下 hashCode 的实现,发现并不是很复杂。但是我从源码中发现了一个奇怪的数字,也就是本文的主角31。这个数字居然不是用常量声明的,所以没法从字面意思上推断这个数字的用途。后来带着疑问和好奇心,到网上去找资料查询一下。在看完资料后,默默的感叹了一句,原来是这样啊。那么到底是哪样呢?在接下来章节里,请大家带着好奇心和我揭开数字31的用途之谜。</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>Spring面试常见问题</title>
<link href="ayjcsgm.github.io/2019/12/11/Spring%E9%9D%A2%E8%AF%95%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/"/>
<id>ayjcsgm.github.io/2019/12/11/Spring%E9%9D%A2%E8%AF%95%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/</id>
<published>2019-12-11T02:45:43.000Z</published>
<updated>2019-12-14T11:34:14.226Z</updated>
<content type="html"><![CDATA[<p><strong>1、Spring是什么?</strong></p><p> Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于XML的配置、基于注解的配置、基于Java的配置。</p><a id="more"></a> <p>主要由以下几个模块组成:</p><p style="text-indent:50px;">Spring Core:核心类库,提供IOC服务;</p><p style="text-indent:50px;">Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);</p><p style="text-indent:50px;">Spring AOP:AOP服务;</p><p style="text-indent:50px;">Spring DAO:对JDBC的抽象,简化了数据访问异常的处理;</p><p style="text-indent:50px;">Spring ORM:对现有的ORM框架的支持;</p><p style="text-indent:50px;">Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传;</p><p style="text-indent:50px;">Spring MVC:提供面向Web应用的Model-View-Controller实现。</p><p style="text-indent:50px;"> </p><p><strong>2、Spring 的优点?</strong></p><p>(1)spring属于低侵入式设计,代码的污染极低;</p><p>(2)spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;</p><p>(3)Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。</p><p>(4)spring对于主流的应用框架提供了集成支持。</p><p> </p><p><strong>3、Spring的AOP理解:</strong></p><p style="text-indent:50px;">OOP面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。</p><p style="text-indent:50px;">AOP,一般称为面向切面,作为面向对象的一种补充,<span style="color:#f33b45;">用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,</span>这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理。</p><p style="text-indent:50px;">AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。</p><p>(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。</p><p>(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。</p><p><span style="color:#f33b45;">Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:</span></p><p> ①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。</p><p> ②如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。</p><p>(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。</p><blockquote><p> InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。</p></blockquote><p> </p><p><strong>4、Spring的IoC理解:</strong></p><p>(1)IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。</p><p>(2)最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。</p><p>(3)Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。</p><blockquote><p>IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。</p></blockquote><p> </p><p><strong>5、BeanFactory和ApplicationContext有什么区别?</strong></p><p> BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。</p><p>(1)BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:</p><p style="text-indent:50px;">①继承MessageSource,因此支持国际化。</p><p style="text-indent:50px;">②统一的资源文件访问方式。</p><p style="text-indent:50px;">③提供在监听器中注册bean的事件。</p><p style="text-indent:50px;">④同时加载多个配置文件。</p><p style="text-indent:50px;">⑤载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。</p><p>(2)<span style="color:#3399ea;">①BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。</span>这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。</p><p> <span style="color:#3399ea;">②ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。</span>这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。</p><p> ③相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。</p><p>(3)BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。</p><p>(4)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。</p><p> </p><p><strong>6、请解释Spring Bean的生命周期?</strong></p><p> 首先说一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;</p><p> Spring上下文中的Bean生命周期也类似,如下:</p><p><span style="color:#3399ea;">(1)实例化Bean:</span></p><p>对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。</p><p><span style="color:#3399ea;">(2)设置对象属性(依赖注入):</span></p><p>实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。</p><p><span style="color:#3399ea;">(3)处理Aware接口:</span></p><p>接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:</p><p style="text-indent:50px;">①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;</p><p style="text-indent:50px;">②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。</p><p style="text-indent:50px;">③如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;</p><p><span style="color:#3399ea;">(4)BeanPostProcessor:</span></p><p>如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。</p><p><span style="color:#3399ea;">(5)InitializingBean 与 init-method:</span></p><p>如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。</p><p>(6)如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;</p><blockquote><p>以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。</p></blockquote><p><span style="color:#3399ea;">(7)DisposableBean:</span></p><p>当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;</p><p>(8)<span style="color:#3399ea;">destroy-method:</span></p><p>最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。</p><p> </p><p><strong>7、 解释Spring支持的几种bean的作用域。</strong></p><p>Spring容器中的bean可以分为5个范围:</p><p>(1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。</p><p>(2)prototype:为每一个bean请求提供一个实例。</p><p>(3)request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。</p><p>(4)session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。</p><p>(5)global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。</p><p> </p><p><strong>8、Spring框架中的单例Beans是线程安全的么?</strong></p><p> Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。</p><p><strong>9、Spring如何处理线程并发问题?</strong></p><p style="text-indent:50px;">在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态<span style="color:#3399ea;">采用ThreadLocal进行处理,解决线程安全问题。</span></p><p style="text-indent:50px;">ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。</p><p style="text-indent:50px;">ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。</p><p> </p><p><strong>10-1、Spring基于xml注入bean的几种方式:</strong></p><p>(1)Set方法注入;</p><p>(2)构造器注入:①通过index设置参数的位置;②通过type设置参数类型;</p><p>(3)静态工厂注入;</p><p>(4)实例工厂;</p><p>详细内容可以阅读:<a href="https://www.iteye.com/blog/blessht-1162131" rel="noopener" target="_blank">https://www.iteye.com/blog/blessht-1162131</a></p><p><strong>10-2、Spring的自动装配:</strong></p><p>在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,<span style="color:#3399ea;">使用autowire来配置自动装载模式。</span></p><p>在Spring框架xml配置中共有5种自动装配:</p><p>(1)no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。</p><p>(2)byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。 </p><p>(3)byType:通过参数的数据类型进行自动装配。</p><p>(4)constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。</p><p>(5)autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。</p><p>基于注解的方式:</p><p style="text-indent:50px;">使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:</p><p style="text-indent:50px;">如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;</p><p style="text-indent:50px;">如果查询的结果不止一个,那么@Autowired会根据名称来查找;</p><p style="text-indent:50px;">如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。</p><blockquote><p>@Autowired可用于:构造函数、成员变量、Setter方法</p><p>注:@Autowired和@Resource之间的区别</p><p>(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。</p><p>(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。</p></blockquote><p> </p><p><strong>11、Spring 框架中都用到了哪些设计模式?</strong></p><p>(1)工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;</p><p>(2)单例模式:Bean默认为单例模式。</p><p>(3)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;</p><p>(4)模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。</p><p>(5)观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现--ApplicationListener。</p><p> </p><p><strong>12、Spring事务的实现方式和实现原理:</strong></p><p>Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。</p><p><strong>(1)Spring事务的种类:</strong></p><p>spring支持编程式事务管理和声明式事务管理两种方式:</p><p style="text-indent:50px;">①编程式事务管理使用TransactionTemplate。</p><p style="text-indent:50px;">②声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。</p><blockquote><p style="text-indent:50px;">声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中。</p><p style="text-indent:50px;">声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。</p></blockquote><p><strong>(2)spring的事务传播行为:</strong></p><p>spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。</p><blockquote><p>① PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。</p><p>② PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘</p><p>③ PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。</p><p>④ PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。</p><p>⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。</p><p>⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。</p><p>⑦ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。</p></blockquote><p><strong>(3)Spring中的隔离级别:</strong></p><blockquote><p>① ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。</p><p>② ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个事务未提交的数据。</p><p>③ ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。</p><p>④ ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。</p><p>⑤ ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。</p></blockquote><p> </p><p><strong>13、Spring框架中有哪些不同类型的事件?</strong></p><p>Spring 提供了以下5种标准的事件:</p><p>(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。</p><p>(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。</p><p>(3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。</p><p>(4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。</p><p>(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。</p><p>如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。</p><p> </p><p><strong>14、解释一下Spring AOP里面的几个名词:</strong></p><p>(1)切面(Aspect):被抽取的公共模块,可能会横切多个对象。 在Spring AOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @AspectJ 注解来实现。</p><p>(2)连接点(Join point):指方法,在Spring AOP中,一个连接点 总是 代表一个方法的执行。 </p><p>(3)通知(Advice):在切面的某个特定的连接点(Join point)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。许多AOP框架,包括Spring,都是以拦截器做通知模型, 并维护一个以连接点为中心的拦截器链。</p><p>(4)切入点(Pointcut):切入点是指 我们要对哪些Join point进行拦截的定义。通过切入点表达式,指定拦截的方法,比如指定拦截add*、search*。</p><p>(5)引入(Introduction):(也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。</p><p>(6)目标对象(Target Object): 被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(adviced) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied) 对象。</p><p>(7)织入(Weaving):指把增强应用到目标对象来创建新的代理对象的过程。Spring是在运行时完成织入。</p><p>切入点(pointcut)和连接点(join point)匹配的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。 切入点使得定位通知(advice)可独立于OO层次。 例如,一个提供声明式事务管理的around通知可以被应用到一组横跨多个对象中的方法上(例如服务层的所有业务操作)。</p><p><img alt class="has" src="https://img-blog.csdn.net/20180708154818891"></p><p><strong>15、Spring通知有哪些类型?</strong></p><p><a href="https://blog.csdn.net/qq_32331073/article/details/80596084" rel="noopener" target="_blank">https://blog.csdn.net/qq_32331073/article/details/80596084</a></p><p>(1)前置通知(Before advice):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。</p><p>(2)返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。 </p><p>(3)抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。 </p><p>(4)后通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。 </p><p>(5)环绕通知(Around Advice):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。<span style="color:#3399ea;">它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行</span>。 环绕通知是最常用的一种通知类型。大部分基于拦截的AOP框架,例如Nanning和JBoss4,都只提供环绕通知。 </p><blockquote><p>同一个aspect,不同advice的执行顺序:</p><p>①没有异常情况下的执行顺序:</p><p>around before advice<br>before advice<br>target method 执行<br>around after advice<br>after advice<br>afterReturning</p><p>②有异常情况下的执行顺序:</p><p>around before advice<br>before advice<br>target method 执行<br>around after advice<br>after advice<br>afterThrowing:异常发生<br>java.lang.RuntimeException: 异常发生</p></blockquote><p> </p><hr>]]></content>
<summary type="html">
<p><strong>1、Spring是什么?</strong></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于XML的配置、基于注解的配置、基于Java的配置。</p>
</summary>
<category term="Spring" scheme="ayjcsgm.github.io/tags/Spring/"/>
</entry>
<entry>
<title>Java学习路线</title>
<link href="ayjcsgm.github.io/2019/12/11/Java%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/"/>
<id>ayjcsgm.github.io/2019/12/11/Java%E5%AD%A6%E4%B9%A0%E8%B7%AF%E7%BA%BF/</id>
<published>2019-12-11T02:41:58.000Z</published>
<updated>2019-12-14T11:31:49.216Z</updated>
<content type="html"><![CDATA[<blockquote><p>推荐几本Java的书来学习<br>1.《Java编程思想》</p><p>2.《大话设计模式》</p><p>3.《effective java》</p><p>4.《深入理解Java虚拟机》</p><p>5.《Java并发编程实战》</p><p>6.《数据结构与算法》</p></blockquote><a id="more"></a> <pre><code> JVM1. 内存模型 内存分为几部分? 堆溢出、栈溢出原因及实例?线上如何排查? 1、 类加载机制 2、垃圾回收 Java基础什么是接口?什么是抽象类?区别是什么?什么是序列化?网络通信过程及实践什么是线程?java线程池运行过程及实践(Executors) java反射机制实践设计模式 单例模式、原型模式、动态代理模式Spring 什么是IOC 什么是AOP 事务管理:模板事务跟标注事务的区别及运理原理,什么是事务的传播机制数据库 锁机制:锁的作用是什么,什么是乐观锁,什么是悲观锁,怎么实现 ?索引:熟悉联合索引及sql执行计划</code></pre><pre class="prettyprint" name="code"><code class="hljs markdown has-numbering" onclick="mdcp.copyCode(event)" style="position: unset;"><span class="hljs-bullet">1. </span>java内存模型学习内容:学习java内存模型实验方法:写一段代码触发内存溢出,分别触发栈内存和堆内存溢出,写一段代码导致持久代溢出。使用工具查看内存占用情况,学会如何分析内存溢出。<span class="hljs-bullet">2. </span>多线程学习内容:多线程的NIO实现,IO实现(两种实现,阻塞,非阻塞)试验方法:写一段代码实现多人聊天室,包括双人聊天和多人聊天。<span class="hljs-bullet">3. </span>动态代理(工厂模式)(1)学习内容:JDK动态代理实现,试验方法:一个方法的前置拦截,后置拦截,前置打印获取类名,方法名字,调用参数打印。后置打印返回结果(返回的结果是一个复杂的类)。(2)学习内容:spring框架试验方法:对某一包下,所有类的方法做切面,打印日志。<span class="hljs-bullet">4. </span>反射机制学习内容:java的反射机制试验方法:写一段程序,调用一个Bean下实现了标注(“autocall”)的方法<span class="hljs-bullet">5. </span>配置数据源学习内容:配置数据源实验方法:自己建一张表,引入数据库连接池,自动插入20万条数据,创建联合索引,验证走索引和不走索引的耗时,查看sql是否走索引,学会查看查询,计划实验,用标注式事务,编程式事务(查看编程式事务源码实现);用乐观锁和悲观锁实现数据更新。<p><img src="https://img-blog.csdn.net/20160620115227065" alt="这里写图片描述" title></p><p><img src="https://img-blog.csdn.net/20160620115325816" alt="这里写图片描述" title></p><p><img src="https://img-blog.csdn.net/20160621200523990" alt="这里写图片描述" title></p><blockquote> <p>学习需要知道: <br> 1、是什么 <br> 2、解决了什么问题 <br> 3、怎么实现的<br> </p></blockquote> </code></pre>]]></content>
<summary type="html">
<blockquote>
<p>推荐几本Java的书来学习<br>1.《Java编程思想》</p>
<p>2.《大话设计模式》</p>
<p>3.《effective java》</p>
<p>4.《深入理解Java虚拟机》</p>
<p>5.《Java并发编程实战》</p>
<p>6.《数据结构与算法》</p>
</blockquote>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>Java集合框架</title>
<link href="ayjcsgm.github.io/2019/12/11/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6/"/>
<id>ayjcsgm.github.io/2019/12/11/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6/</id>
<published>2019-12-11T02:41:43.000Z</published>
<updated>2019-12-14T11:31:40.445Z</updated>
<content type="html"><![CDATA[<h1>Java 集合框架</h1><p><strong>思维导图</strong>:</p><a id="more"></a> <p><img src="https://imgconvert.csdnimg.cn/aHR0cDovL2Fzc2V0cy5wcm9jZXNzb24uY29tL2NoYXJ0X2ltYWdlLzVkY2E1MTZjZTRiMGE2MDIxNzMzZjBhNC5wbmc?x-oss-process=image/format,png" alt="在这里插入图片描述"></p><p>集合类存放于Java.util包中,主要有3种:set(集)、list(列表包含Queue)和map(映射)。 </p><ol><li>Collection:Collection是集合List、Set、Queue的最基本的接口。 </li><li>Iterator:迭代器,可以通过迭代器遍历集合中的数据 </li><li>Map:是映射表的基础接口 </li></ol><p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly93d3cucnVub29iLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNC8wMS8yMjQzNjkwLTljZDljODk2ZTBkNTEyZWQuZ2lm" alt="image"></p><p>从上面的集合框架图可以看到,Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。</p><p>集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:</p><ul><li><strong></strong><p><strong>接口:</strong>是代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象</p></li> <li> <p><strong>实现(类):</strong>是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。</p></li> <li> <p><strong>算法:</strong>是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。</p></li></ul><p>除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中。</p><h3>集合框架体系如图所示</h3><p><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly93d3cucnVub29iLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNC8wMS9qYXZhLWNvbGwucG5n?x-oss-process=image/format,png" alt="image"></p><p>Java 集合框架提供了一套性能优良,使用方便的接口和类,java集合框架位于java.util包中, 所以当使用集合框架的时候需要进行导包。</p><p>Collections是针对集合类的一个帮助类,它提供了一系列静态方法实现了对各种集合的排序,搜索和线程安全等操作。</p><hr><h2 id="List"><a href="#List" class="headerlink" title="List"></a>List</h2><h3 id="ArrayList(数组)"><a href="#ArrayList(数组)" class="headerlink" title="ArrayList(数组)"></a>ArrayList(数组)</h3><p>ArrayList 是最常用的 List 实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数 组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数 组的数据复制到新的存储空间中。当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进 行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。 详细讲解可以查看我写的《<a href="https://blog.csdn.net/weixin_43664418/article/details/103464783" target="_blank" rel="noopener">学习ArrayList看这篇就够了(源码分析)</a>》。</p><h3 id="Vector(数组实现、线程同步)"><a href="#Vector(数组实现、线程同步)" class="headerlink" title="Vector(数组实现、线程同步)"></a>Vector(数组实现、线程同步)</h3><p>Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一 个线程能够写 Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。</p><h3 id="LinkedList(链表)"><a href="#LinkedList(链表)" class="headerlink" title="LinkedList(链表)"></a>LinkedList(链表)</h3><p>LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较 慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆 栈、队列和双向队列使用。 详细讲解可以查看我写的《<a href="https://blog.csdn.net/weixin_43664418/article/details/101934256" target="_blank" rel="noopener">学习LinkedList看这篇就够了(源码分析)</a>》。</p> <hr><h2 id="Set"><a href="#Set" class="headerlink" title="Set"></a>Set</h2><p>Set注重独一无二的性质,该体系集合用于存储无序(存入和取出的顺序不一定相同)元素,值不能重 复。对象的相等性本质是对象hashCode值(java是依据对象的内存地址计算出的此序号)判断 的,如果想要让两个不同的对象视为相等的,就必须覆盖Object的hashCode方法和equals方 法。 </p><h3 id="HashSet(Hash-表)"><a href="#HashSet(Hash-表)" class="headerlink" title="HashSet(Hash 表)"></a>HashSet(Hash 表)</h3><p>哈希表边存放的是哈希值。HashSet存储元素的顺序并不是按照存入时的顺序(和List显然不 同) 而是按照哈希值来存的所以取数据也是按照哈希值取得。元素的哈希值是通过元素的 hashcode方法来获取的, HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较 equals方法 如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是 同一个元素。<br>哈希值相同equals为false的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相 同的元素放在一个哈希桶中)。也就是哈希一样的存一列,表示hashCode值不相同的情 况;存一竖表示hashCode值相同,但equals不相同的情况。 </p><p>HashSet通过hashCode值来确定元素在内存中的位置。一个hashCode位置上可以存放多个元素。 更多详细介绍可以查看我写的《<a href="https://blog.csdn.net/weixin_43664418/article/details/103477399" target="_blank" rel="noopener">简述HashSet的元素唯一机制</a>》。</p><h3 id="TreeSet(二叉树)"><a href="#TreeSet(二叉树)" class="headerlink" title="TreeSet(二叉树)"></a>TreeSet(二叉树)</h3><ol><li>TreeSet()是使用二叉树的原理对新add()的对象按照指定的顺序排序(升序、降序),每增 加一个对象都会进行排序,将对象插入的二叉树指定的位置。 </li><li>Integer和String对象都可以进行默认的TreeSet排序,而自定义类的对象是不可以的,自 己定义的类必须实现Comparable接口,并且覆写相应的compareTo()函数,才可以正常使 用。 </li><li>在覆写compare()函数时,要返回相应的值才能使TreeSet按照一定的规则来排序 </li><li>比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整 数、零或正整数。</li></ol><p>更多详细介绍可以查看我写的《<a href="https://blog.csdn.net/weixin_43664418/article/details/103487109" target="_blank" rel="noopener">简述TreeSet排序机制</a>》。</p><h3 id="LinkHashSet(HashSet-LinkedHashMap)"><a href="#LinkHashSet(HashSet-LinkedHashMap)" class="headerlink" title="LinkHashSet(HashSet+LinkedHashMap)"></a>LinkHashSet(HashSet+LinkedHashMap)</h3><p> 对于 LinkedHashSet 而言,它继承与 HashSet、又基于 LinkedHashMap 来实现的。 LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法 操作上又与HashSet相同,因此LinkedHashSet 的实现上非常简单,只提供了四个构造方法,并 通过传递一个标识参数,调用父类的构造器,底层构造一个 LinkedHashMap 来实现,在相关操 作上与父类HashSet的操作相同,直接调用父类HashSet的方法即可。 </p><hr><h2 id="Queue"><a href="#Queue" class="headerlink" title="Queue"></a>Queue</h2><p>Queue是Java集合框架中的一员,继承于Collection接口。<br>与List、Set相同的是,Queue也实现了一种数据结构,这就是队列。</p><p>队列是计算机中的一种数据结构,保存在其中的数据具有“先进先出(FIFO,First In First Out)”的特性。<br>在数据结构中,队列不支持从队伍的中间插入和离开,只能从头尾进行。</p><p>详细讲解可以查看我写的《<a href="https://blog.csdn.net/weixin_43664418/article/details/103465317" target="_blank" rel="noopener">学习Queue看这篇就够了(源码分析)</a>》。</p><hr><h2 id="Map"><a href="#Map" class="headerlink" title="Map"></a>Map</h2><h3 id="HashMap(数组-链表-红黑树)"><a href="#HashMap(数组-链表-红黑树)" class="headerlink" title="HashMap(数组+链表+红黑树)"></a>HashMap(数组+链表+红黑树)</h3><p>HashMap根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,因而具有很快 的访问速度,但遍历顺序却是不确定的。 HashMap最多只允许一条记录的键为null,允许多条记 录的值为 null。HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导 致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。</p><h5 id="JAVA7-实现"><a href="#JAVA7-实现" class="headerlink" title="JAVA7 实现"></a>JAVA7 实现</h5><p>HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。每个实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。 </p><ol><li>capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。 2. loadFactor:负载因子,默认为 0.75。 </li></ol><ol start="2"><li>threshold:扩容的阈值,等于 capacity * loadFactor </li></ol><p>可以看看这篇博客《<a href="https://blog.csdn.net/weixin_43664418/article/details/102164483" target="_blank" rel="noopener">重新认识HashMap(JDK1.7与1.8相比较)</a>》介绍7与8的不同。</p><h5 id="JAVA8-实现"><a href="#JAVA8-实现" class="headerlink" title="JAVA8 实现"></a>JAVA8 实现</h5><p>Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑 树 组成。<br>根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的 具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决 于链表的长度,为 O(n)。为了降低这部分的开销,在 Java8 中,当链表中的元素超过了 8 个以后, 会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。 可以查看我写的这篇博客《<a href="https://blog.csdn.net/weixin_43664418/article/details/101999910" target="_blank" rel="noopener">HashMap源码分析(JDK1.8)</a>》。</p><h3 id="HashTable(线程安全)"><a href="#HashTable(线程安全)" class="headerlink" title="HashTable(线程安全)"></a>HashTable(线程安全)</h3><p>Hashtable 是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类, 并且是线程安全的,任一时间只有一个线程能写 Hashtable,并发性不如 ConcurrentHashMap, 因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全 的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。 </p><h3 id="TreeMap(可排序)"><a href="#TreeMap(可排序)" class="headerlink" title="TreeMap(可排序)"></a>TreeMap(可排序)</h3><p>TreeMap 实现 SortedMap 接口,能够把它保存的记录根据键排序,默认是按键值的升序排序, 也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。<br>如果使用排序的映射,建议使用TreeMap。<br>在使用 TreeMap 时,key 必须实现 Comparable 接口或者在构造 TreeMap 传入自定义的 Comparator,否则会在运行时抛出java.lang.ClassCastException类型的异常。 详情可以查看我写的《<a href="https://blog.csdn.net/weixin_43664418/article/details/103488053" target="_blank" rel="noopener">TreeMap</a>》。</p><h3 id="LinkHashMap(记录插入顺序)"><a href="#LinkHashMap(记录插入顺序)" class="headerlink" title="LinkHashMap(记录插入顺序)"></a>LinkHashMap(记录插入顺序)</h3><p>LinkedHashMap 是 HashMap 的一个子类,保存了记录的插入顺序,在用 Iterator 遍历 LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。 </p><hr><h2 id="Iterator"><a href="#Iterator" class="headerlink" title="Iterator"></a>Iterator</h2><p>Iterator是一个接口,它是集合的迭代器。集合可以通过Iterator去遍历集合中的元素。Iterator只能单向移动。hasNext():如果迭代器中还有元素,则返回true。next():返回迭代器中的下一个元素。Iterator.remove()是唯一安全的方式来在迭代过程中修改集合;如果在迭代过程中以任何其它的方式修改了基本集合将会产生未知的行为。而且每调用一次next()方法,remove()方法只能被调用一次。</p><h3 id="ListIterator"><a href="#ListIterator" class="headerlink" title="ListIterator"></a>ListIterator</h3><p>ListIterator是一个功能更加强大的, 它继承于Iterator接口,只能用于各种List类型的访问。可以通过调用listIterator()方法产生一个指向List开始处的ListIterator, 还可以调用listIterator(n)方法创建一个一开始就指向列表索引为n的元素处的ListIterator。</p>]]></content>
<summary type="html">
<h1>Java 集合框架</h1>
<p><strong>思维导图</strong>:</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>学习Queue看这篇就够了</title>
<link href="ayjcsgm.github.io/2019/12/09/%E5%AD%A6%E4%B9%A0Queue%E7%9C%8B%E8%BF%99%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86/"/>
<id>ayjcsgm.github.io/2019/12/09/%E5%AD%A6%E4%B9%A0Queue%E7%9C%8B%E8%BF%99%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86/</id>
<published>2019-12-09T13:15:27.000Z</published>
<updated>2019-12-14T11:35:11.919Z</updated>
<content type="html"><![CDATA[<h1 id="Queue"><a href="#Queue" class="headerlink" title="Queue"></a>Queue</h1><p>Queue是Java集合框架中的一员,继承于Collection接口。<br>与List、Set相同的是,Queue也实现了一种数据结构,这就是队列。</p><p>队列是计算机中的一种数据结构,保存在其中的数据具有“先进先出(FIFO,First In First Out)”的特性。<br>在数据结构中,队列不支持从队伍的中间插入和离开,只能从头尾进行。</p><a id="more"></a> <h3 id="队列的两种形式"><a href="#队列的两种形式" class="headerlink" title="队列的两种形式"></a>队列的两种形式</h3><p>在Java中,队列分为2种形式,一种是单队列,一种是循环队列;<br>通常,都是使用数组来实现队列。假定数组的长度为6,也就是队列的长度为6;</p><ul><li><p>单队列:<br>第一步,创建一个空数组,有两个变量,分别为front、rear,代表着头指针、尾指针;<br><img src="https://img-blog.csdnimg.cn/20191209210739255.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>第二步,向队列中插入数据;<br><img src="https://img-blog.csdnimg.cn/20191209210759101.png" alt="在这里插入图片描述"><br>第三步,移除队头中的数据;<br><img src="https://img-blog.csdnimg.cn/20191209210819593.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>第四步,再次向队列中插数据(此时rear指针指向了一个不存在的角标);<br><img src="https://img-blog.csdnimg.cn/2019120921084136.png" alt="在这里插入图片描述"><br>此时,单队列发生了“假溢出”情况,尾指针指向了一个不存在的数组角标。<br>如果,要解决该情况的发生,有两种方式—–一,无限扩充数组大小;二,引入循环队列;</p><ul><li>循环队列:<br>当尾指针超过了数组角标大小,此时我们会判断队列的头部是否有剩余的空间,如果有就把尾指针指向队列的头部;<br><img src="https://img-blog.csdnimg.cn/20191209210921181.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>此时,循环队列就产生了。<br>其实,循环队列就是将单队列的首位进行相连,形成了一个圆圈,这样就不会发生角标越界的情况了。<br>在Java中,ArrayDeque、LinkedList、PriorityQueue等类实现了Queue接口,模拟了队列的数据结构。<br>其中,PriorityQueue是Queue直接子类实现,在原有基础上实现了元素的排序功能。<br>除此之外,Queue还有一个子接口–Deque,对Queue进行了扩展,定义了头尾操作功能,既可在队头添加(删除)元素,也可在队尾添加(删除)元素,俗称“双端队列”。<br><img src="https://img-blog.csdnimg.cn/20191209210945340.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><h3 id="Queue源码"><a href="#Queue源码" class="headerlink" title="Queue源码"></a>Queue源码</h3></li></ul></li></ul><figure class="highlight plain"><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><span class="line">//接口Queue:</span><br><span class="line">public interface Queue<E> extends Collection<E> {</span><br><span class="line"> //将指定元素插入到队列的尾部(队列满了话,会抛出异常)</span><br><span class="line"> boolean add(E e);</span><br><span class="line"></span><br><span class="line"> //将指定元素插入此队列的尾部(队列满了话,会返回false)</span><br><span class="line"> boolean offer(E e);</span><br><span class="line"></span><br><span class="line"> /返回取队列头部的元素,并删除该元素(如果队列为空,则抛出异常)</span><br><span class="line"> E remove();</span><br><span class="line"></span><br><span class="line"> //返回队列头部的元素,并删除该元素(如果队列为空,则返回null)</span><br><span class="line"> E poll();</span><br><span class="line"></span><br><span class="line"> //返回队列头部的元素,不删除该元素(如果队列为空,则抛出异常)</span><br><span class="line"> E element();</span><br><span class="line"></span><br><span class="line"> //返回队列头部的元素,不删除该元素(如果队列为空,则返回null)</span><br><span class="line"> E peek();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="PriorityQueue源码"><a href="#PriorityQueue源码" class="headerlink" title="PriorityQueue源码"></a>PriorityQueue源码</h3><p>作为Queue的直接子类,PriorityQueue实现了Queue定义的方法。<br>不过,又与传统的队列不相。传统队列实现了“先进先出”数据模型,而PriorityQueue则实现了最小的元素优先出队,剩余元素依次按照大小顺序出队。<br>这就是所谓的“优先级队列”—元素按照任意的顺序插入,却总是按照顺序进行输出;每次从优先队列中取出来的元素要么是最大值,要么是最小值。接下来,我们来看下PriorityQueue具体是如何实现的:</p><p>PriorityQueue成员变量和构造方法:</p><figure class="highlight plain"><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><span class="line">public class PriorityQueue<E> extends AbstractQueue<E></span><br><span class="line"> implements java.io.Serializable {</span><br><span class="line"></span><br><span class="line"> private static final long serialVersionUID = -7720805057305804111L;</span><br><span class="line"></span><br><span class="line"> //默认初始化数组大小:</span><br><span class="line"> private static final int DEFAULT_INITIAL_CAPACITY = 11;</span><br><span class="line"></span><br><span class="line"> //队列底层数据结构:数组</span><br><span class="line"> private transient Object[] queue;</span><br><span class="line"></span><br><span class="line"> //队列长度:</span><br><span class="line"> private int size = 0;</span><br><span class="line"></span><br><span class="line"> //实现元素排序的比较器:</span><br><span class="line"> private final Comparator<? super E> comparator;</span><br><span class="line"></span><br><span class="line"> //对queue的操作次数:</span><br><span class="line"> private transient int modCount = 0;</span><br><span class="line"></span><br><span class="line"> //默认构造函数:</span><br><span class="line"> public PriorityQueue() {</span><br><span class="line"> this(DEFAULT_INITIAL_CAPACITY, null);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //可设置队列长度的构造函数:</span><br><span class="line"> public PriorityQueue(int initialCapacity) {</span><br><span class="line"> this(initialCapacity, null);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //可设置队列长度、元素比较器的构造函数:</span><br><span class="line"> public PriorityQueue(int initialCapacity,</span><br><span class="line"> Comparator<? super E> comparator) {</span><br><span class="line"> if (initialCapacity < 1)</span><br><span class="line"> throw new IllegalArgumentException();</span><br><span class="line"> this.queue = new Object[initialCapacity];</span><br><span class="line"> this.comparator = comparator;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>PriorityQueue新增元素:</p><figure class="highlight plain"><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><span class="line">//队列添加元素,底层调用offer:插入失败抛出异常</span><br><span class="line">public boolean add(E e) {</span><br><span class="line"> return offer(e);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">//队列添加元素: 插入失败返回false</span><br><span class="line">public boolean offer(E e) {</span><br><span class="line"> //不支持添加为null的元素:</span><br><span class="line"> if (e == null)</span><br><span class="line"> throw new NullPointerException();</span><br><span class="line"></span><br><span class="line"> //队列操作数+1:</span><br><span class="line"> modCount++;</span><br><span class="line"> int i = size;</span><br><span class="line"></span><br><span class="line"> //队列长度 >= 数组长度时,扩容:</span><br><span class="line"> if (i >= queue.length)</span><br><span class="line"> grow(i + 1);</span><br><span class="line"></span><br><span class="line"> //队列长度+1</span><br><span class="line"> size = i + 1;</span><br><span class="line"></span><br><span class="line"> //i==0,在数组角标为0处插入第一个元素:</span><br><span class="line"> if (i == 0)</span><br><span class="line"> queue[0] = e;</span><br><span class="line"> else</span><br><span class="line"> //插入的不是第一个元素:</span><br><span class="line"> siftUp(i, e);</span><br><span class="line"> return true;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">//对队列底层数组扩容:</span><br><span class="line">private void grow(int minCapacity) {</span><br><span class="line"> //现阶段数组长度:</span><br><span class="line"> int oldCapacity = queue.length;</span><br><span class="line"> </span><br><span class="line"> //计算新数组的长度:</span><br><span class="line"> // 如果 现阶段数组长度<64,则扩容为现阶段长度的2倍+2;</span><br><span class="line"> // 如果 现阶段数组>=64,则扩容为现阶段长度的2倍+5;</span><br><span class="line"> int newCapacity = oldCapacity + ((oldCapacity < 64) ?</span><br><span class="line"> (oldCapacity + 2) :</span><br><span class="line"> (oldCapacity >> 1));</span><br><span class="line"> if (newCapacity - MAX_ARRAY_SIZE > 0)</span><br><span class="line"> newCapacity = hugeCapacity(minCapacity);</span><br><span class="line"> </span><br><span class="line"> //数组复制:得到新数组</span><br><span class="line"> queue = Arrays.copyOf(queue, newCapacity);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">private static int hugeCapacity(int minCapacity) {</span><br><span class="line"> if (minCapacity < 0)</span><br><span class="line"> throw new OutOfMemoryError();</span><br><span class="line"> return (minCapacity > MAX_ARRAY_SIZE) ?</span><br><span class="line"> Integer.MAX_VALUE :</span><br><span class="line"> MAX_ARRAY_SIZE;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>PriorityQueue获取队列头部元素:</p><figure class="highlight plain"><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><span class="line">//返回队列头部的元素,不删除该元素(如果队列为空,则返回null)</span><br><span class="line">public E peek() {</span><br><span class="line"> if (size == 0)</span><br><span class="line"> return null;</span><br><span class="line"> return (E) queue[0];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">//返回队列头部的元素,并删除该元素(如果队列为空,则返回null)</span><br><span class="line">public E poll() {</span><br><span class="line"> if (size == 0)</span><br><span class="line"> return null;</span><br><span class="line"> int s = --size;</span><br><span class="line"> modCount++;</span><br><span class="line"> E result = (E) queue[0];</span><br><span class="line"> E x = (E) queue[s];</span><br><span class="line"> queue[s] = null;</span><br><span class="line"> if (s != 0)</span><br><span class="line"> siftDown(0, x);</span><br><span class="line"> return result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>PriorityQueue中核心方法:使用了比较器进行元素比较,当插入或者删除的元素后,对PriorityQueue中树的结构进行调整;</p><figure class="highlight plain"><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><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"> private void siftUp(int k, E x) {</span><br><span class="line"> //元素比较器不为null:</span><br><span class="line"> if (comparator != null)</span><br><span class="line"> siftUpUsingComparator(k, x);</span><br><span class="line"> else</span><br><span class="line"> //元素比较器为null:</span><br><span class="line"> siftUpComparable(k, x);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //进行堆结构的siftUp运算:使用元素比较器</span><br><span class="line"> private void siftUpComparable(int k, E x) {</span><br><span class="line"> Comparable<? super E> key = (Comparable<? super E>) x;</span><br><span class="line"> while (k > 0) {</span><br><span class="line"> int parent = (k - 1) >>> 1;</span><br><span class="line"> Object e = queue[parent];</span><br><span class="line"> if (key.compareTo((E) e) >= 0)</span><br><span class="line"> break;</span><br><span class="line"> queue[k] = e;</span><br><span class="line"> k = parent;</span><br><span class="line"> }</span><br><span class="line"> queue[k] = key;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //进行堆结构的siftUp运算:使用自定义元素比较器</span><br><span class="line"> private void siftUpUsingComparator(int k, E x) {</span><br><span class="line"> while (k > 0) {</span><br><span class="line"> int parent = (k - 1) >>> 1;</span><br><span class="line"> Object e = queue[parent];</span><br><span class="line"> if (comparator.compare(x, (E) e) >= 0)</span><br><span class="line"> break;</span><br><span class="line"> queue[k] = e;</span><br><span class="line"> k = parent;</span><br><span class="line"> }</span><br><span class="line"> queue[k] = x;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private void siftDown(int k, E x) {</span><br><span class="line"> //元素比较器不为null:</span><br><span class="line"> if (comparator != null)</span><br><span class="line"> siftDownUsingComparator(k, x);</span><br><span class="line"> else</span><br><span class="line"> //元素比较器为null:</span><br><span class="line"> siftDownComparable(k, x);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //进行堆结构的siftDown运算:使用元素比较器</span><br><span class="line"> private void siftDownComparable(int k, E x) {</span><br><span class="line"> Comparable<? super E> key = (Comparable<? super E>)x;</span><br><span class="line"> int half = size >>> 1; </span><br><span class="line"> while (k < half) {</span><br><span class="line"> int child = (k << 1) + 1; </span><br><span class="line"> Object c = queue[child];</span><br><span class="line"> int right = child + 1;</span><br><span class="line"> if (right < size &&</span><br><span class="line"> ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)</span><br><span class="line"> c = queue[child = right];</span><br><span class="line"> if (key.compareTo((E) c) <= 0)</span><br><span class="line"> break;</span><br><span class="line"> queue[k] = c;</span><br><span class="line"> k = child;</span><br><span class="line"> }</span><br><span class="line"> queue[k] = key;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //进行堆结构的siftDown运算:使用自定义元素比较器</span><br><span class="line"> private void siftDownUsingComparator(int k, E x) {</span><br><span class="line"> int half = size >>> 1;</span><br><span class="line"> while (k < half) {</span><br><span class="line"> int child = (k << 1) + 1;</span><br><span class="line"> Object c = queue[child];</span><br><span class="line"> int right = child + 1;</span><br><span class="line"> if (right < size &&</span><br><span class="line"> comparator.compare((E) c, (E) queue[right]) > 0)</span><br><span class="line"> c = queue[child = right];</span><br><span class="line"> if (comparator.compare(x, (E) c) <= 0)</span><br><span class="line"> break;</span><br><span class="line"> queue[k] = c;</span><br><span class="line"> k = child;</span><br><span class="line"> }</span><br><span class="line"> queue[k] = x;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //返回队列中的比较器:</span><br><span class="line"> public Comparator<? super E> comparator() {</span><br><span class="line"> return comparator;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>从上面的源码中,可以看出,PriorityQueue是由“堆结构”来实现的队列。而“堆结构”又是通过数组形成的一颗完全二叉树。所以,我们在代码中可以看到PriorityQueue最底层数据结构就是数组。<br>经过上面的源码的分析,对PriorityQueue的总结如下:<br>1、<strong>PriorityQueue是线程不安全的队列</strong>;<br>2、<strong>PriorityQueue中不允许插入null元素</strong>;<br>3、<strong>PriorityQueue是一个用“堆结构”来实现的队列</strong>;</p>]]></content>
<summary type="html">
<h1 id="Queue"><a href="#Queue" class="headerlink" title="Queue"></a>Queue</h1><p>Queue是Java集合框架中的一员,继承于Collection接口。<br>与List、Set相同的是,Queue也实现了一种数据结构,这就是队列。</p>
<p>队列是计算机中的一种数据结构,保存在其中的数据具有“先进先出(FIFO,First In First Out)”的特性。<br>在数据结构中,队列不支持从队伍的中间插入和离开,只能从头尾进行。</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>学习ArrayList看这篇就够了</title>
<link href="ayjcsgm.github.io/2019/12/09/%E5%AD%A6%E4%B9%A0ArrayList%E7%9C%8B%E8%BF%99%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86/"/>
<id>ayjcsgm.github.io/2019/12/09/%E5%AD%A6%E4%B9%A0ArrayList%E7%9C%8B%E8%BF%99%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86/</id>
<published>2019-12-09T12:58:34.000Z</published>
<updated>2019-12-14T11:34:56.095Z</updated>
<content type="html"><![CDATA[<h1 id="ArrayList简介"><a href="#ArrayList简介" class="headerlink" title="ArrayList简介"></a>ArrayList简介</h1><p>1、ArrayList是一个数组队列,相当于动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List,RandomAccess,Cloneable,java.io.Serializable这些接口。<br>2、ArrayList与Collection的关系如下图,实现代表继承,虚线代表实现接口:</p><a id="more"></a> <p><img src="https://img-blog.csdnimg.cn/20191209203735109.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>3、ArrayList实现了RandmoAccess接口,即提供了随机访问的功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。<br>4、ArrayList实现了Cloneable接口,即覆盖了函数clone(),能被克隆。<br>5、ArrayList实现了java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。<br>6、ArrayList中的操作不是线程安全的,可以选择CopyOnWriteArrayList或者使用Collections中的synchronizedList方法将其包装成一个线程安全的List。</p><h1 id="ArrayList的API"><a href="#ArrayList的API" class="headerlink" title="ArrayList的API"></a>ArrayList的API</h1><figure class="highlight plain"><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><span class="line">// Collection中定义的API</span><br><span class="line">boolean add(E object)</span><br><span class="line">boolean addAll(Collection<? extends E> collection)</span><br><span class="line">void clear()</span><br><span class="line">boolean contains(Object object)</span><br><span class="line">boolean containsAll(Collection<?> collection)</span><br><span class="line">boolean equals(Object object)</span><br><span class="line">int hashCode()</span><br><span class="line">boolean isEmpty()</span><br><span class="line">Iterator<E> iterator()</span><br><span class="line">boolean remove(Object object)</span><br><span class="line">boolean removeAll(Collection<?> collection)</span><br><span class="line">boolean retainAll(Collection<?> collection)</span><br><span class="line">int size()</span><br><span class="line"><T> T[] toArray(T[] array)</span><br><span class="line">Object[] toArray()</span><br><span class="line">// AbstractCollection中定义的API</span><br><span class="line">void add(int location, E object)</span><br><span class="line">boolean addAll(int location, Collection<? extends E> collection)</span><br><span class="line">E get(int location)</span><br><span class="line">int indexOf(Object object)</span><br><span class="line">int lastIndexOf(Object object)</span><br><span class="line">ListIterator<E> listIterator(int location)</span><br><span class="line">ListIterator<E> listIterator()</span><br><span class="line">E remove(int location)</span><br><span class="line">E set(int location, E object)</span><br><span class="line">List<E> subList(int start, int end)</span><br><span class="line">// ArrayList新增的API</span><br><span class="line">Object clone()</span><br><span class="line">void ensureCapacity(int minimumCapacity)//增加容量</span><br><span class="line">void trimToSize()//修改容量是列表当前大小</span><br><span class="line">void removeRange(int fromIndex, int toIndex)//删除所有索引在 fromIndex (含)和 toIndex之间的元素</span><br></pre></td></tr></table></figure><h1 id="ArrayList源码分析"><a href="#ArrayList源码分析" class="headerlink" title="ArrayList源码分析"></a>ArrayList源码分析</h1><h3 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h3><p>ArrayList的主要属性如下代码所示:</p><figure class="highlight plain"><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><span class="line">//序列化id</span><br><span class="line">private static final long serialVersionUID = 8683452581122892189L;</span><br><span class="line">//容器默认初始化大小</span><br><span class="line">private static final int DEFAULT_CAPACITY = 10;</span><br><span class="line">//一个空对象</span><br><span class="line">private static final Object[] EMPTY_ELEMENTDATA = {};</span><br><span class="line">//一个空对象,如果使用默认构造函数创建ArrayList,则默认对象内容是该值</span><br><span class="line">private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};</span><br><span class="line">//ArrayList存放对象的容器,后面的添加、删除等操作都是基于该属性来进行操作</span><br><span class="line">transient Object[] elementData;</span><br><span class="line">//当前列表已使用的长度</span><br><span class="line">private int size;</span><br><span class="line">//数组最大长度(2147483639),这里为什么是Integer.MAX_VALUE - 8是因为有些虚拟机在数组中保留了一些头部信息,防止内存溢出</span><br><span class="line">private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;</span><br><span class="line">//这个是从AbstractList继承过来的,代表ArrayList集合修改的次数</span><br><span class="line">protected transient int modCount = 0;</span><br></pre></td></tr></table></figure><p>关于Java中<strong>transient</strong>关键字的解释:<br> 我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。<br> 然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。<br> 总之,java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。</p><h1 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h1><p><strong>1、无参构造函数</strong><br>如果不传入参数,则使用默认无参构造方法创建ArrayLisy对象,如下:</p><figure class="highlight plain"><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><span class="line">public ArrayList() {</span><br><span class="line"> this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>注意:<strong>此时我们创建的ArrayList对象中的elementData中的长度是0,size是0,当进行第一次add的时候,elementDate将会变成默认的长度:10</strong>。</p><p><strong>2、带int类型的构造函数</strong><br>如果传入参数,则代表指定ArrayList的初始数组长度;传入参数如果是大于0,则使用用户的参数初始化;如果参数等于0,则用内部的空对象EMPTY_ELEMENTDATA的地址直接赋值给elementData;否则抛出异常,如下:</p><figure class="highlight plain"><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><span class="line">public ArrayList(int initialCapacity) {</span><br><span class="line"> if (initialCapacity > 0) {</span><br><span class="line"> this.elementData = new Object[initialCapacity];</span><br><span class="line"> } else if (initialCapacity == 0) {</span><br><span class="line"> this.elementData = EMPTY_ELEMENTDATA;</span><br><span class="line"> } else {</span><br><span class="line"> throw new IllegalArgumentException("Illegal Capacity: "+</span><br><span class="line"> initialCapacity);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p><strong>3、带Collection对象的构造函数</strong><br>1.将Collection对象转换成数组,然后将数组的地址赋值给elementData。<br>2.更新size的值,如果size的值等于0直接将内部空对象EMPTY_ELEMENTDATA的地址赋值给elementData。<br>3.如果size的值大于0,则执行Arrays.copy方法,把Collection对象的内容copy(可以理解为深拷贝)到elementData中,并且这些元素是按照该collection的迭代器返回它们的顺序排列的。</p><figure class="highlight plain"><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><span class="line">public ArrayList(Collection<? extends E> c) {</span><br><span class="line"> elementData = c.toArray();</span><br><span class="line"> if ((size = elementData.length) != 0) {</span><br><span class="line"> // c.toArray might (incorrectly) not return Object[] (see 6260652)</span><br><span class="line"> if (elementData.getClass() != Object[].class)</span><br><span class="line"> elementData = Arrays.copyOf(elementData, size, Object[].class);</span><br><span class="line"> } else {</span><br><span class="line"> // replace with empty array.</span><br><span class="line"> this.elementData = EMPTY_ELEMENTDATA;</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>介绍下<strong>System.arraycopy</strong>和<strong>Arrays.copy</strong>方法,分析源码时会经常用到。</p><p><strong>System.arraycopy</strong>方法:它就是从指定的源数组将元素中复制到目标数组,复制从指定的位置开始,到设定的复制长度结束,然后从目标数组的指定起始位置依次插入。</p><figure class="highlight plain"><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><span class="line">// src 源数组</span><br><span class="line"> // srcPos 源数组要复制的起始位置</span><br><span class="line"> // dest 要赋值到的目标数组</span><br><span class="line"> // destPos 目标数组放置的起始位置</span><br><span class="line"> // length 复制的长度</span><br><span class="line"> // 使用了native关键字,说明调用的是其他语言写的底层函数</span><br><span class="line"> public static native void arraycopy(Object src, int srcPos,</span><br><span class="line"> Object dest, int destPos,</span><br><span class="line"> int length);</span><br></pre></td></tr></table></figure><p><strong>Arrays.copy</strong>方法:它新建了一个数组并且将原数组的内容拷贝到长度为newLength的新数组中,并且返回该新数组。</p><figure class="highlight plain"><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><span class="line">// original 要复制的数组</span><br><span class="line"> // newLength 要返回副本的长度</span><br><span class="line"> // newwType 要返回的副本类型</span><br><span class="line"> // 内部调用了System.arraycopy方法</span><br><span class="line"> public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {</span><br><span class="line"> @SuppressWarnings("unchecked")</span><br><span class="line"> T[] copy = ((Object)newType == (Object)Object[].class)</span><br><span class="line"> ? (T[]) new Object[newLength]</span><br><span class="line"> : (T[]) Array.newInstance(newType.getComponentType(), newLength);</span><br><span class="line"> System.arraycopy(original, 0, copy, 0,</span><br><span class="line"> Math.min(original.length, newLength));</span><br><span class="line"> return copy;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>两者的区别:<br>1、System.arraycopy需要目标数组,将原数组拷贝到你自己定义的数值里,而且可以选择拷贝的起点和长度以及放入新数组中的位置。<br>2、Arrays.copyof是系统自动在内部新建一个数组,调用System.arraycopy将原数组的内容拷贝到长度为newLength的新数组中,并返回新建的数组。</p><h1 id="添加元素"><a href="#添加元素" class="headerlink" title="添加元素"></a>添加元素</h1><p>ArrayList提供了add(E e)、add(int index, E element)、addAll(Collection<? extends E> c)、addAll(int index, Collection<? extends E> c)、set(int index, E element)这个五个方法来实现ArrayList增加。</p><figure class="highlight plain"><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><span class="line">//官方解释:将指定的元素追加到列表(elementData)的末尾</span><br><span class="line"> public boolean add(E e) {</span><br><span class="line"> ensureCapacityInternal(size + 1); // Increments modCount!!</span><br><span class="line"> elementData[size++] = e;</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>看下ensureCapacityInternal方法,以及它内部调用的方法。</p><figure class="highlight plain"><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><span class="line">看下ensureCapacityInternal方法,以及它内部调用的方法。</span><br><span class="line"> //参数值实际是size+1</span><br><span class="line"> private void ensureCapacityInternal(int minCapacity) {</span><br><span class="line"> ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//这个方式是判断当前数组是否是个空数组,如果是就返回默认长度10,否则就返回size+1;也就是说如果你是用无参构造函数初始化ArrayList,那么在第一次调用add方法时,默认长度会变成10</span><br><span class="line"> private static int calculateCapacity(Object[] elementData, int minCapacity) {</span><br><span class="line"> if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {</span><br><span class="line"> return Math.max(DEFAULT_CAPACITY, minCapacity);</span><br><span class="line"> }</span><br><span class="line"> return minCapacity;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//这个方法首先将集合修改次数加1,然后判断数组的长度是否可以存入下一个元素,如果长度不够会调用grow方法进行扩容</span><br><span class="line"> private void ensureExplicitCapacity(int minCapacity) {</span><br><span class="line"> modCount++;</span><br><span class="line"></span><br><span class="line"> // overflow-conscious code</span><br><span class="line"> if (minCapacity - elementData.length > 0)</span><br><span class="line"> grow(minCapacity);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//这个方法首先定义数组新的长度为原来数组长度的1.5倍,如果新长度减去所需数组的最小长度小于0,那么新长度就等于所需数组最小长度;再下面的判断是如果新长度大于MAX_ARRAY_SIZE(ArrayList内部定义MAX_ARRAY_SIZE的值是:2147483639)就调用hugeCapacity方法,最后调用Arrays.copyOf将扩容后的新数组地址赋值给elementData</span><br><span class="line"> private void grow(int minCapacity) {</span><br><span class="line"> // overflow-conscious code</span><br><span class="line"> int oldCapacity = elementData.length;</span><br><span class="line"> int newCapacity = oldCapacity + (oldCapacity >> 1);</span><br><span class="line"> if (newCapacity - minCapacity < 0)</span><br><span class="line"> newCapacity = minCapacity;</span><br><span class="line"> if (newCapacity - MAX_ARRAY_SIZE > 0)</span><br><span class="line"> newCapacity = hugeCapacity(minCapacity);</span><br><span class="line"> // minCapacity is usually close to size, so this is a win:</span><br><span class="line"> elementData = Arrays.copyOf(elementData, newCapacity);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//如果扩容长度超过MAX_ARRAY_SIZE,则设置长度为Integer.MAX_VALUE,但不是能百分百成功的,这取决于虚拟机。(如果我们可以在某些虚拟机上可以避免OutOfMemory,我们将另外分配Integer.MAX_VALUE,如果你很幸运(取决于虚拟机),我们将成功)</span><br><span class="line"> private static int hugeCapacity(int minCapacity) {</span><br><span class="line"> if (minCapacity < 0) // overflow</span><br><span class="line"> throw new OutOfMemoryError();</span><br><span class="line"> return (minCapacity > MAX_ARRAY_SIZE) ?</span><br><span class="line"> Integer.MAX_VALUE :</span><br><span class="line"> MAX_ARRAY_SIZE;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>最后总结一下add方法的逻辑:</p><ul><li>确保数组已使用长度(size)加1后可以存入下一个元素。</li><li>修改次数modCount标识自增1,如果当前数组已使用长度+1后大于当前数组长度,则调用grow方法,扩容数组,grow方法会将当前数组的长度变为原来容量的1.5倍。</li><li>确保新加的元素有地方存储后,则将新元素添加到位于size++的位置上。</li><li>返回添加成功的布尔值。</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">add(int index, E element)</span><br></pre></td></tr></table></figure><p>这个方法和上面的add类型,该方法可以按照元素的位置,指定新元素位置插入。</p><figure class="highlight plain"><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><span class="line">public void add(int index, E element) {</span><br><span class="line"> //判断索引位置是否正确</span><br><span class="line"> rangeCheckForAdd(index);</span><br><span class="line"> //扩容检测</span><br><span class="line"> ensureCapacityInternal(size + 1); // Increments modCount!!</span><br><span class="line"> //对源数组进行复制处理(位移),从index + 1到size - index</span><br><span class="line"> //即向后移动位于当前位置和后面的元素</span><br><span class="line"> System.arraycopy(elementData, index, elementData, index + 1,</span><br><span class="line"> size - index);</span><br><span class="line"> //在指定的位置赋值</span><br><span class="line"> elementData[index] = element;</span><br><span class="line"> size++;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>该方法首先调用rangeCheckForAdd方法判断指定的位置小于当前数组的长度并且大于0,否则抛出异常。</p><figure class="highlight plain"><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><span class="line">private void rangeCheckForAdd(int index) {</span><br><span class="line"> if (index > size || index < 0)</span><br><span class="line"> throw new IndexOutOfBoundsException(outOfBoundsMsg(index));</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>第二步调用的<strong>ensureCapacityInternal</strong>方法和上面的add方法逻辑一样。<br>第三步调用<strong>System.arraycopy</strong>方法把<strong>指定下标以及后面的元素全部往后移一位</strong>。<br>最后将新的元素放到指定位置(index)上,并将size+1。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">addAll(Collection<? extends E> c)</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//按照指定的Collection迭代器所返回的顺序,依次插入到列表尾部。</span><br><span class="line"> public boolean addAll(Collection<? extends E> c) {</span><br><span class="line"> // 将c转换为数组</span><br><span class="line"> Object[] a = c.toArray();</span><br><span class="line"> int numNew = a.length;</span><br><span class="line"> //扩容处理,大小为size + numNew</span><br><span class="line"> ensureCapacityInternal(size + numNew); // Increments modCount</span><br><span class="line"> System.arraycopy(a, 0, elementData, size, numNew);</span><br><span class="line"> size += numNew;</span><br><span class="line"> return numNew != 0;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>该方法首先传过来的Collection集合转换为数组,然后做扩容处理,接着使用System.arraycopy把转换后的数组复制到列表尾部。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">addAll(int index, Collection<? extends E> c)</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//将指定集合中的所有元素插入到此列表中,从指定的位置开始</span><br><span class="line">public boolean addAll(int index, Collection<? extends E> c) {</span><br><span class="line"> //判断索引位置是否正确</span><br><span class="line"> rangeCheckForAdd(index);</span><br><span class="line"> // 将c转换为数组</span><br><span class="line"> Object[] a = c.toArray();</span><br><span class="line"> int numNew = a.length;</span><br><span class="line"> //扩容处理,大小为size + numNew</span><br><span class="line"> ensureCapacityInternal(size + numNew); // Increments modCount</span><br><span class="line"> //如果插入索引小于列表长度,则将当前索引等于index和大于index的元素往后移numMoved个位置</span><br><span class="line"> int numMoved = size - index;</span><br><span class="line"> if (numMoved > 0)</span><br><span class="line"> System.arraycopy(elementData, index, elementData, index + numNew,</span><br><span class="line"> numMoved);</span><br><span class="line"> //将数组添加到列表尾部</span><br><span class="line"> System.arraycopy(a, 0, elementData, index, numNew);</span><br><span class="line"> //更新列表长度</span><br><span class="line"> size += numNew;</span><br><span class="line"> return numNew != 80;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//set(int index, E element)用指定的元素替换此列表中指定位置的元素</span><br><span class="line"> public E set(int index, E element) {</span><br><span class="line"> //判断插入位置是否正确,如果大于列表长度会抛出异常</span><br><span class="line"> rangeCheck(index);</span><br><span class="line"> //获取插入位置的当前元素</span><br><span class="line"> E oldValue = elementData(index);</span><br><span class="line"> //将新的元素替换当前插入位置的元素</span><br><span class="line"> elementData[index] = element;</span><br><span class="line"> //返回插入位置老的值</span><br><span class="line"> return oldValue;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h1 id="删除元素"><a href="#删除元素" class="headerlink" title="删除元素"></a>删除元素</h1><p>ArrayList提供了外界remove(int index)、remove(Object o)、removeAll(Collection<?> c)、clear()四个方法进行元素的删除。</p><figure class="highlight plain"><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><span class="line">//remove(int index)移除指定位置上的元素</span><br><span class="line"> public E remove(int index) {</span><br><span class="line"> //判断删除位置是否正确,如果大于列表长度会抛出异常</span><br><span class="line"> rangeCheck(index);</span><br><span class="line"> //将集合修改次数加1</span><br><span class="line"> modCount++;</span><br><span class="line"> //获取当前删除位置上的元素</span><br><span class="line"> E oldValue = elementData(index);</span><br><span class="line"> //判断是否删除的是最后一个元素,如果不是将删除位置后的元素向左移numMoved个位置</span><br><span class="line"> int numMoved = size - index - 1;</span><br><span class="line"> if (numMoved > 0)</span><br><span class="line"> System.arraycopy(elementData, index+1, elementData, index,</span><br><span class="line"> numMoved);</span><br><span class="line"> //将列表最后的元素置为null,等待垃圾收集器收集</span><br><span class="line"> elementData[--size] = null; // clear to let GC do its work</span><br><span class="line"> //返回删除位置老的值</span><br><span class="line"> return oldValue;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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></pre></td><td class="code"><pre><span class="line">// remove(Object o)移除指定元素</span><br><span class="line"> public boolean remove(Object o) {</span><br><span class="line"> //因为ArrayList允许存在null,所以需要进行null判断</span><br><span class="line"> if (o == null) {</span><br><span class="line"> for (int index = 0; index < size; index++)</span><br><span class="line"> if (elementData[index] == null) {</span><br><span class="line"> //移除这个位置的元素</span><br><span class="line"> fastRemove(index);</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"> } else {</span><br><span class="line"> for (int index = 0; index < size; index++)</span><br><span class="line"> if (o.equals(elementData[index])) {</span><br><span class="line"> fastRemove(index);</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> private void fastRemove(int index) {</span><br><span class="line"> //将集合修改次数加1 </span><br><span class="line"> modCount++;</span><br><span class="line"> //判断是否删除的是最后一个元素,如果不是将删除位置后的元素向左移numMoved个位置</span><br><span class="line"> int numMoved = size - index - 1;</span><br><span class="line"> if (numMoved > 0)</span><br><span class="line"> System.arraycopy(elementData, index+1, elementData, index,</span><br><span class="line"> numMoved);</span><br><span class="line"> //将列表最后的元素置为null,等待垃圾收集器收集</span><br><span class="line"> elementData[--size] = null; // clear to let GC do its work</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">// removeAll(Collection<?> c)从此列表中删除指定集合中包含的所有元素</span><br><span class="line"> public boolean removeAll(Collection<?> c) {</span><br><span class="line"> //进行判断,如果c为null抛出异常</span><br><span class="line"> Objects.requireNonNull(c);</span><br><span class="line"> return batchRemove(c, false);</span><br><span class="line"> }</span><br><span class="line"> private boolean batchRemove(Collection<?> c, boolean complement) {</span><br><span class="line"> final Object[] elementData = this.elementData;</span><br><span class="line"> int r = 0, w = 0;</span><br><span class="line"> boolean modified = false;</span><br><span class="line"> try {</span><br><span class="line"> //遍历数组,并检查这个集合是否包含对应的值,移动要保留的值到数组前面,w最后值为要保留的元素的数量</span><br><span class="line"> //若保留,就将相同的元素移动到前段;不删除,就将不同元素移动到前段</span><br><span class="line"> for (; r < size; r++)</span><br><span class="line"> if (c.contains(elementData[r]) == complement)</span><br><span class="line"> elementData[w++] = elementData[r];</span><br><span class="line"> } finally {</span><br><span class="line"> // 确保异常抛出前的部分可以完成期望的操作,而被遍历的部分会被接到后面</span><br><span class="line"> //r不等于size表示可能出错了</span><br><span class="line"> if (r != size) {</span><br><span class="line"> System.arraycopy(elementData, r,</span><br><span class="line"> elementData, w,</span><br><span class="line"> size - r);</span><br><span class="line"> w += size - r;</span><br><span class="line"> }</span><br><span class="line"> //如果w等于size,表示全部元素都保留了,所以也就没有删除操作发生,所以会返回false;反之,返回true,并更改数组</span><br><span class="line"> //而w不等于size的时候,即使try块抛出异常,也能正确处理异常抛出前的操作,因为w始终为要保留的前段部分的长度,数组也不会因此乱序</span><br><span class="line"> if (w != size) {</span><br><span class="line"> // clear to let GC do its work</span><br><span class="line"> for (int i = w; i < size; i++)</span><br><span class="line"> elementData[i] = null;</span><br><span class="line"> modCount += size - w;</span><br><span class="line"> size = w;</span><br><span class="line"> modified = true;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return modified;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//clear() 清空ArrayList内的所有元素,不减小数组容量</span><br><span class="line"> public void clear() {</span><br><span class="line"> //将集合修改次数加1 </span><br><span class="line"> modCount++;</span><br><span class="line"> //循环将列表中的所有元素置为null,等待垃圾收集器收集</span><br><span class="line"> // clear to let GC do its work</span><br><span class="line"> for (int i = 0; i < size; i++)</span><br><span class="line"> elementData[i] = null;</span><br><span class="line"> //将列表长度设为0</span><br><span class="line"> size = 0;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h1 id="查找元素"><a href="#查找元素" class="headerlink" title="查找元素"></a>查找元素</h1><p>ArrayList提供了get(int index)用读取ArrayList中的元素。由于ArrayList是动态数组,所以我们完全可以根据下标来获取ArrayList中的元素,而且速度还比较快。</p><figure class="highlight plain"><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><span class="line">public E get(int index) {</span><br><span class="line"> //判断删除位置是否正确,如果大于列表长度会抛出异常</span><br><span class="line"> rangeCheck(index);</span><br><span class="line"> //直接返回列表中下标等于index的元素</span><br><span class="line"> return elementData(index);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h1 id="判断元素是否存在列表中"><a href="#判断元素是否存在列表中" class="headerlink" title="判断元素是否存在列表中"></a>判断元素是否存在列表中</h1><p>ArrayList提供了contains(Object o)用于判断元素是否存在于列表中。<br>注意:contains方法会遍历ArrayList。</p><figure class="highlight plain"><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><span class="line">public boolean contains(Object o) {</span><br><span class="line"> //调用indexOf方法判断需要查找的元素在列表中的下标是否大于等于0,小于0则不存在</span><br><span class="line"> return indexOf(o) >= 0;</span><br><span class="line"> }</span><br><span class="line"> public int indexOf(Object o) {</span><br><span class="line"> //因为ArrayList允许存在null,所以需要进行null判断</span><br><span class="line"> if (o == null) {</span><br><span class="line"> //遍历列表,如果列表存在null值的元素,直接返回其下标位置</span><br><span class="line"> for (int i = 0; i < size; i++)</span><br><span class="line"> if (elementData[i]==null)</span><br><span class="line"> return i;</span><br><span class="line"> } else {</span><br><span class="line"> //遍历列表,使用equals判断是否有相等的元素,有的话直接返回其下标位置</span><br><span class="line"> for (int i = 0; i < size; i++)</span><br><span class="line"> if (o.equals(elementData[i]))</span><br><span class="line"> return i;</span><br><span class="line"> }</span><br><span class="line"> //列表中不能存在传进来的元素,返回-1</span><br><span class="line"> return -1;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h1 id="最小化ArrayList的实际存储量"><a href="#最小化ArrayList的实际存储量" class="headerlink" title="最小化ArrayList的实际存储量"></a>最小化ArrayList的实际存储量</h1><p>ArrayList提供了trimToSize()方法用于将底层数组的容量调整为当前列表保存的实际元素的大小</p><figure class="highlight plain"><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><span class="line">public void trimToSize() {</span><br><span class="line"> //将集合修改次数加1</span><br><span class="line"> modCount++;</span><br><span class="line"> //如果当前ArrayList的实际长度小于列表的长度,将列表超过size后的空余的空间(包括null值)去除,调用Arrays.cppyof方法拷贝elementData,长度为size</span><br><span class="line"> if (size < elementData.length) {</span><br><span class="line"> elementData = (size == 0)</span><br><span class="line"> ? EMPTY_ELEMENTDATA</span><br><span class="line"> : Arrays.copyOf(elementData, size);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h1 id="截取ArrayList部分内容"><a href="#截取ArrayList部分内容" class="headerlink" title="截取ArrayList部分内容"></a>截取ArrayList部分内容</h1><p>ArrayList提供了subList(int fromIndex, int toIndex)方法来实现部分数据的截取。<br>可以从源码中看到其实是创建了一个SubList的内部对象,可以理解为是返回当前ArrayList的部分视图,其实指向的存放数据的还是一个地方。如果修改了subList返回的内容的话,原来的内容也会被修改。</p><figure class="highlight plain"><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><span class="line">public List<E> subList(int fromIndex, int toIndex) {</span><br><span class="line"> //检查需要截取的下标位置是否正确</span><br><span class="line"> subListRangeCheck(fromIndex, toIndex, size);</span><br><span class="line"> return new SubList(this, 0, fromIndex, toIndex);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="其它方法"><a href="#其它方法" class="headerlink" title="其它方法"></a>其它方法</h1><figure class="highlight plain"><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><span class="line">//判断ArrayList是否为空</span><br><span class="line"> public boolean isEmpty() {</span><br><span class="line"> return size == 0;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//反向查找元素位置,与上述的indexOf相反</span><br><span class="line"> public int lastIndexOf(Object o) {</span><br><span class="line"> if (o == null) {</span><br><span class="line"> for (int i = size-1; i >= 0; i--)</span><br><span class="line"> if (elementData[i]==null)</span><br><span class="line"> return i;</span><br><span class="line"> } else {</span><br><span class="line"> for (int i = size-1; i >= 0; i--)</span><br><span class="line"> if (o.equals(elementData[i]))</span><br><span class="line"> return i;</span><br><span class="line"> }</span><br><span class="line"> return -1;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//将元素全部拷贝到v中</span><br><span class="line"> public Object clone() {</span><br><span class="line"> try {</span><br><span class="line"> ArrayList<?> v = (ArrayList<?>) super.clone();</span><br><span class="line"> v.elementData = Arrays.copyOf(elementData, size);</span><br><span class="line"> v.modCount = 0;</span><br><span class="line"> return v;</span><br><span class="line"> } catch (CloneNotSupportedException e) {</span><br><span class="line"> // this shouldn't happen, since we are Cloneable</span><br><span class="line"> throw new InternalError(e);</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//返回ArrayList拷贝后的Object数组</span><br><span class="line"> public Object[] toArray() {</span><br><span class="line"> return Arrays.copyOf(elementData, size);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//返回ArrayList的模板数组。所谓模板数组,即可将T设置为任意数据类型</span><br><span class="line"> public <T> T[] toArray(T[] a) {</span><br><span class="line"> //若a的长度小于ArrayList中的元素个数,返回拷贝了ArrayList中全部元素的新数组</span><br><span class="line"> if (a.length < size)</span><br><span class="line"> // Make a new array of a's runtime type, but my contents:</span><br><span class="line"> return (T[]) Arrays.copyOf(elementData, size, a.getClass());</span><br><span class="line"> //若a的长度大于等于ArrayList中的元素个数,则将ArrayList中的元素全部拷贝到a中</span><br><span class="line"> System.arraycopy(elementData, 0, a, 0, size);</span><br><span class="line"> if (a.length > size)</span><br><span class="line"> a[size] = null;</span><br><span class="line"> return a;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//将ArrayList中的元素写入到输入流中,先写容量,在写元素</span><br><span class="line"> private void writeObject(java.io.ObjectOutputStream s)</span><br><span class="line"> throws java.io.IOException{</span><br><span class="line"> // Write out element count, and any hidden stuff</span><br><span class="line"> int expectedModCount = modCount;</span><br><span class="line"> s.defaultWriteObject();</span><br><span class="line"></span><br><span class="line"> // Write out size as capacity for behavioural compatibility with clone()</span><br><span class="line"> s.writeInt(size);</span><br><span class="line"></span><br><span class="line"> // Write out all elements in the proper order.</span><br><span class="line"> for (int i=0; i<size; i++) {</span><br><span class="line"> s.writeObject(elementData[i]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (modCount != expectedModCount) {</span><br><span class="line"> throw new ConcurrentModificationException();</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><figure class="highlight plain"><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><span class="line">//从输入流中读取数据到elementData中,一样是先读容量,再读数据</span><br><span class="line">private void readObject(java.io.ObjectInputStream s)</span><br><span class="line"> throws java.io.IOException, ClassNotFoundException {</span><br><span class="line"> elementData = EMPTY_ELEMENTDATA;</span><br><span class="line"></span><br><span class="line"> // Read in size, and any hidden stuff</span><br><span class="line"> s.defaultReadObject();</span><br><span class="line"></span><br><span class="line"> // Read in capacity</span><br><span class="line"> s.readInt(); // ignored</span><br><span class="line"></span><br><span class="line"> if (size > 0) {</span><br><span class="line"> // be like clone(), allocate array based upon size not capacity</span><br><span class="line"> int capacity = calculateCapacity(elementData, size);</span><br><span class="line"> SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);</span><br><span class="line"> ensureCapacityInternal(size);</span><br><span class="line"></span><br><span class="line"> Object[] a = elementData;</span><br><span class="line"> // Read in all elements in the proper order.</span><br><span class="line"> for (int i=0; i<size; i++) {</span><br><span class="line"> a[i] = s.readObject();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h1><ul><li>ArrayList自己实现了序列化和反序列化,因为它实现了writeObject和readObject方法。</li><li>ArrayList基于数组实现,会自动扩容。</li><li>添加元素时会自己判断是否需要扩容,最好指定一个大概的大小,防止后面多次扩容带来的内存消耗;删除元素时不会减少容量,删除元素时,将删除掉的位置元素置为null,下次gc就会自动回收这些元素所占的空间。</li><li>ArrayList是线程不安全的。</li><li>使用iterator遍历可能会引发多线程异常。</li></ul>]]></content>
<summary type="html">
<h1 id="ArrayList简介"><a href="#ArrayList简介" class="headerlink" title="ArrayList简介"></a>ArrayList简介</h1><p>1、ArrayList是一个数组队列,相当于动态数组。与Java中的数组相比,它的容量能动态增长。它继承于AbstractList,实现了List,RandomAccess,Cloneable,java.io.Serializable这些接口。<br>2、ArrayList与Collection的关系如下图,实现代表继承,虚线代表实现接口:</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>Java 8 新特性</title>
<link href="ayjcsgm.github.io/2019/11/15/Java-8-%E6%96%B0%E7%89%B9%E6%80%A7/"/>
<id>ayjcsgm.github.io/2019/11/15/Java-8-%E6%96%B0%E7%89%B9%E6%80%A7/</id>
<published>2019-11-15T12:09:27.000Z</published>
<updated>2019-12-14T11:31:52.911Z</updated>
<content type="html"><![CDATA[<p>Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。</p><a id="more"></a><h2 id="新特性"><a href="#新特性" class="headerlink" title="新特性"></a>新特性</h2><p>Java8 新增了非常多的特性,我们主要讨论以下几个:</p><ul><li><p>Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。</p></li><li><p>方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。</p></li><li><p>默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。</p></li><li><p>新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。</p></li><li><p>Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。</p></li><li><p>Date Time API − 加强对日期与时间的处理。</p></li><li><p>Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。</p></li><li><p>Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。</p></li></ul><p>更多的新特性可以参阅官网:<a href="https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html" target="_blank" rel="noopener">What’s New in JDK 8</a></p><p>在关于 Java 8 文章的实例,我们均使用 jdk 1.8 环境,你可以使用以下命令查看当前 jdk 的版本:</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><span class="line">$ java -version</span><br><span class="line">java version <span class="string">"1.8.0_31"</span></span><br><span class="line">Java(TM) <span class="function">SE Runtime <span class="title">Environment</span> <span class="params">(build <span class="number">1.8</span><span class="number">.0</span>_31-b13)</span></span></span><br><span class="line"><span class="function">Java <span class="title">HotSpot</span><span class="params">(TM)</span> 64-Bit Server <span class="title">VM</span> <span class="params">(build <span class="number">25.31</span>-b07, mixed mode)</span></span></span><br></pre></td></tr></table></figure><h2 id="编程风格"><a href="#编程风格" class="headerlink" title="编程风格"></a>编程风格</h2><p>Java 8 希望有自己的编程风格,并与 Java 7 区别开,以下实例展示了 Java 7 和 Java 8 的编程格式:</p><p>Java8Tester.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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Collections;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.Comparator;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Java8Tester</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String args[])</span></span>{</span><br><span class="line"> </span><br><span class="line"> List<String> names1 = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line"> names1.add(<span class="string">"Google "</span>);</span><br><span class="line"> names1.add(<span class="string">"Runoob "</span>);</span><br><span class="line"> names1.add(<span class="string">"Taobao "</span>);</span><br><span class="line"> names1.add(<span class="string">"Baidu "</span>);</span><br><span class="line"> names1.add(<span class="string">"Sina "</span>);</span><br><span class="line"> </span><br><span class="line"> List<String> names2 = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line"> names2.add(<span class="string">"Google "</span>);</span><br><span class="line"> names2.add(<span class="string">"Runoob "</span>);</span><br><span class="line"> names2.add(<span class="string">"Taobao "</span>);</span><br><span class="line"> names2.add(<span class="string">"Baidu "</span>);</span><br><span class="line"> names2.add(<span class="string">"Sina "</span>);</span><br><span class="line"> </span><br><span class="line"> Java8Tester tester = <span class="keyword">new</span> Java8Tester();</span><br><span class="line"> System.out.println(<span class="string">"使用 Java 7 语法: "</span>);</span><br><span class="line"> </span><br><span class="line"> tester.sortUsingJava7(names1);</span><br><span class="line"> System.out.println(names1);</span><br><span class="line"> System.out.println(<span class="string">"使用 Java 8 语法: "</span>);</span><br><span class="line"> </span><br><span class="line"> tester.sortUsingJava8(names2);</span><br><span class="line"> System.out.println(names2);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 使用 java 7 排序</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sortUsingJava7</span><span class="params">(List<String> names)</span></span>{ </span><br><span class="line"> Collections.sort(names, <span class="keyword">new</span> Comparator<String>() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">compare</span><span class="params">(String s1, String s2)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> s1.compareTo(s2);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 使用 java 8 排序</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sortUsingJava8</span><span class="params">(List<String> names)</span></span>{</span><br><span class="line"> Collections.sort(names, (s1, s2) -> s1.compareTo(s2));</span><br><span class="line"> }</span><br><span class="line">}</span><br></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></pre></td><td class="code"><pre><span class="line">$ javac Java8Tester.java</span><br><span class="line">$ java Java8Tester</span><br><span class="line">使用 Java <span class="number">7</span> 语法: </span><br><span class="line">[Baidu , Google , Runoob , Sina , Taobao ]</span><br><span class="line">使用 Java <span class="number">8</span> 语法: </span><br><span class="line">[Baidu , Google , Runoob , Sina , Taobao ]</span><br></pre></td></tr></table></figure><p>接下来我们将详细为大家简介 Java 8 的新特性:</p><table><thead><tr><th>序号</th><th>特性</th></tr></thead><tbody><tr><td>1</td><td><a href="https://blog.csdn.net/weixin_43664418/article/details/103091287" target="_blank" rel="noopener">Lambda 表达式</a></td></tr><tr><td>2</td><td><a href="https://blog.csdn.net/weixin_43664418/article/details/103091326" target="_blank" rel="noopener">方法引用</a></td></tr><tr><td>3</td><td><a href="https://blog.csdn.net/weixin_43664418/article/details/103091361" target="_blank" rel="noopener">函数式接口</a></td></tr><tr><td>4</td><td><a href="https://blog.csdn.net/weixin_43664418/article/details/103091418" target="_blank" rel="noopener">默认方法</a></td></tr><tr><td>5</td><td><a href="https://blog.csdn.net/weixin_43664418/article/details/103091453" target="_blank" rel="noopener">Stream</a></td></tr><tr><td>6</td><td><a href="https://blog.csdn.net/weixin_43664418/article/details/103091530" target="_blank" rel="noopener">Optional 类</a></td></tr><tr><td>7</td><td><a href="https://blog.csdn.net/weixin_43664418/article/details/103091552" target="_blank" rel="noopener">Nashorn, JavaScript 引擎</a></td></tr><tr><td>8</td><td><a href="https://blog.csdn.net/weixin_43664418/article/details/103091591" target="_blank" rel="noopener">新的日期时间 API</a></td></tr><tr><td>9</td><td><a href="https://blog.csdn.net/weixin_43664418/article/details/103091633" target="_blank" rel="noopener">Base64</a></td></tr></tbody></table>]]></content>
<summary type="html">
<p>Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。</p>
</summary>
<category term="Java" scheme="ayjcsgm.github.io/tags/Java/"/>
</entry>
<entry>
<title>8、Spring Boot笔记整理高级篇-监控</title>
<link href="ayjcsgm.github.io/2019/11/08/8%E3%80%81Spring-Boot%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86%E9%AB%98%E7%BA%A7%E7%AF%87-%E7%9B%91%E6%8E%A7/"/>
<id>ayjcsgm.github.io/2019/11/08/8%E3%80%81Spring-Boot%E7%AC%94%E8%AE%B0%E6%95%B4%E7%90%86%E9%AB%98%E7%BA%A7%E7%AF%87-%E7%9B%91%E6%8E%A7/</id>
<published>2019-11-08T10:39:41.000Z</published>
<updated>2019-12-14T11:29:22.761Z</updated>
<content type="html"><![CDATA[<h1 id="八、Spring-Boot与监控管理"><a href="#八、Spring-Boot与监控管理" class="headerlink" title="八、Spring Boot与监控管理"></a>八、Spring Boot与监控管理</h1><h2 id="一、监控管理"><a href="#一、监控管理" class="headerlink" title="一、监控管理"></a>一、监控管理</h2><p>通过引入spring-boot-starter-actuator,可以使用Spring Boot为我们提供的准 生产环境下的应用监控和管理功能。我们可以通过HTTP,JMX,SSH协议来进 行操作,自动得到审计、健康及指标信息等</p><a id="more"></a><ul><li>步骤:<br>– 引入spring-boot-starter-actuator<br>– 通过http方式访问监控端点<br> – 可进行shutdown(POST 提交,此端点默认关闭)<ul><li>监控和管理端点<br><img src="https://img-blog.csdnimg.cn/20191108182131681.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzY2NDQxOA==,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><h2 id="二、定制端点信息"><a href="#二、定制端点信息" class="headerlink" title="二、定制端点信息"></a>二、定制端点信息</h2>– 定制端点一般通过endpoints+端点名+属性名来设置。<br>– 修改端点id(endpoints.beans.id=mybeans)<br>– 开启远程应用关闭功能(endpoints.shutdown.enabled=true)<br>– 关闭端点(endpoints.beans.enabled=false)<br>– 开启所需端点 </li><li>endpoints.enabled=false </li><li>endpoints.beans.enabled=true – 定制端点访问根路径 </li><li>management.context-path=/manage – 关闭http端点 </li><li>management.port=-1</li></ul></li></ul><p>pom.xml</p><figure class="highlight"><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></pre></td><td class="code"><pre><span class="line"><?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"UTF-8"</span>?></span><br><span class="line"><project xmlns=<span class="string">"http://maven.apache.org/POM/4.0.0"</span> xmlns:xsi=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></span><br><span class="line">xsi:schemaLocation=<span class="string">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span>></span><br><span class="line"><modelVersion>4.0.0</modelVersion></span><br><span class="line"></span><br><span class="line"><groupId>com.atguigu</groupId></span><br><span class="line"><artifactId>springboot-08-actuator</artifactId></span><br><span class="line"><version>0.0.1-SNAPSHOT</version></span><br><span class="line"><packaging>jar</packaging></span><br><span class="line"></span><br><span class="line"><name>springboot-08-actuator</name></span><br><span class="line"><description>Demo project for Spring Boot</description></span><br><span class="line"></span><br><span class="line"><parent></span><br><span class="line"><groupId>org.springframework.boot</groupId></span><br><span class="line"><artifactId>spring-boot-starter-parent</artifactId></span><br><span class="line"><version>1.5.12.RELEASE</version></span><br><span class="line"><relativePath/> <!-- lookup parent from repository --></span><br><span class="line"></parent></span><br><span class="line"></span><br><span class="line"><properties></span><br><span class="line"><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></span><br><span class="line"><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding></span><br><span class="line"><java.version>1.8</java.version></span><br><span class="line"></properties></span><br><span class="line"></span><br><span class="line"><dependencies></span><br><span class="line"><dependency></span><br><span class="line"><groupId>org.springframework.boot</groupId></span><br><span class="line"><artifactId>spring-boot-starter-actuator</artifactId></span><br><span class="line"></dependency></span><br><span class="line"><dependency></span><br><span class="line"><groupId>org.springframework.boot</groupId></span><br><span class="line"><artifactId>spring-boot-starter-data-redis</artifactId></span><br><span class="line"></dependency></span><br><span class="line"><dependency></span><br><span class="line"><groupId>org.springframework.boot</groupId></span><br><span class="line"><artifactId>spring-boot-starter-web</artifactId></span><br><span class="line"></dependency></span><br><span class="line"></span><br><span class="line"><dependency></span><br><span class="line"><groupId>org.springframework.boot</groupId></span><br><span class="line"><artifactId>spring-boot-devtools</artifactId></span><br><span class="line"><scope>runtime</scope></span><br><span class="line"></dependency></span><br><span class="line"><dependency></span><br><span class="line"><groupId>org.springframework.boot</groupId></span><br><span class="line"><artifactId>spring-boot-starter-test</artifactId></span><br><span class="line"><scope>test</scope></span><br><span class="line"></dependency></span><br><span class="line"></dependencies></span><br><span class="line"></span><br><span class="line"><build></span><br><span class="line"><plugins></span><br><span class="line"><plugin></span><br><span class="line"><groupId>org.springframework.boot</groupId></span><br><span class="line"><artifactId>spring-boot-maven-plugin</artifactId></span><br><span class="line"></plugin></span><br><span class="line"></plugins></span><br><span class="line"></build></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></project></span><br></pre></td></tr></table></figure><p>application.properties</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><span class="line">management.security.enabled=<span class="keyword">false</span></span><br><span class="line"></span><br><span class="line">spring.redis.host=<span class="number">118.24</span>.44.169</span><br><span class="line"></span><br><span class="line">info.app.id=hello</span><br><span class="line">info.app.version=<span class="number">1.0</span>.0</span><br><span class="line"></span><br><span class="line">#endpoints.metrics.enabled=false</span><br><span class="line">endpoints.shutdown.enabled=<span class="keyword">true</span></span><br><span class="line"></span><br><span class="line">#endpoints.beans.id=mybean</span><br><span class="line">#endpoints.beans.path=/bean</span><br><span class="line">#endpoints.beans.enabled=false</span><br><span class="line">#</span><br><span class="line">#endpoints.dump.path=/du</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"># \u5173\u95ED\u6240\u6709\u7AEF\u70B9\u8BBF\u95EE</span><br><span class="line">#endpoints.enabled=false</span><br><span class="line">#endpoints.beans.enabled=true</span><br><span class="line"></span><br><span class="line">management.context-path=/manage</span><br><span class="line"></span><br><span class="line">management.port=<span class="number">8181</span></span><br></pre></td></tr></table></figure><p>git.properties</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><span class="line">git.branch=master</span><br><span class="line">git.commit.id=xjkd33s</span><br><span class="line">git.commit.time=<span class="number">2017</span>-<span class="number">12</span>-<span class="number">12</span> <span class="number">12</span>:<span class="number">12</span>:<span class="number">56</span></span><br></pre></td></tr></table></figure><p>Springboot08ActuatorApplication.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><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><span class="line"><span class="keyword">package</span> com.atguigu.springboot08actuator;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.SpringApplication;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 自定义健康状态指示器</span></span><br><span class="line"><span class="comment"> * 1、编写一个指示器 实现 HealthIndicator 接口</span></span><br><span class="line"><span class="comment"> * 2、指示器的名字 xxxxHealthIndicator</span></span><br><span class="line"><span class="comment"> * 3、加入容器中</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Springboot08ActuatorApplication</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line">SpringApplication.run(Springboot08ActuatorApplication.class, args);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>MyAppHealthIndicator.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><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><span class="line"><span class="keyword">package</span> com.atguigu.springboot08actuator.health;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.actuate.health.Health;</span><br><span class="line"><span class="keyword">import</span> org.springframework.boot.actuate.health.HealthIndicator;</span><br><span class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyAppHealthIndicator</span> <span class="keyword">implements</span> <span class="title">HealthIndicator</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Health <span class="title">health</span><span class="params">()</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">//自定义的检查方法</span></span><br><span class="line"> <span class="comment">//Health.up().build()代表健康</span></span><br><span class="line"> <span class="keyword">return</span> Health.down().withDetail(<span class="string">"msg"</span>,<span class="string">"服务异常"</span>).build();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h1 id="八、Spring-Boot与监控管理"><a href="#八、Spring-Boot与监控管理" class="headerlink" title="八、Spring Boot与监控管理"></a>八、Spring Boot与监控管理</h1><h2 id="一、监控管理"><a href="#一、监控管理" class="headerlink" title="一、监控管理"></a>一、监控管理</h2><p>通过引入spring-boot-starter-actuator,可以使用Spring Boot为我们提供的准 生产环境下的应用监控和管理功能。我们可以通过HTTP,JMX,SSH协议来进 行操作,自动得到审计、健康及指标信息等</p>
</summary>
<category term="Spring Boot" scheme="ayjcsgm.github.io/tags/Spring-Boot/"/>
</entry>
</feed>