-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
684 lines (452 loc) · 474 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
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
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>一帆磨砺</title>
<subtitle>生活所迫,一叶孤舟</subtitle>
<link href="http://janwarlen.com/atom.xml" rel="self"/>
<link href="http://janwarlen.com/"/>
<updated>2023-01-04T17:09:44.311Z</updated>
<id>http://janwarlen.com/</id>
<author>
<name>Jan Warlen</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>语言入门 - Ruby on Rails</title>
<link href="http://janwarlen.com/2023/01/05/%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A8%20-%20Ruby%20on%20Rails/"/>
<id>http://janwarlen.com/2023/01/05/%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A8%20-%20Ruby%20on%20Rails/</id>
<published>2023-01-04T17:03:33.000Z</published>
<updated>2023-01-04T17:09:44.311Z</updated>
<content type="html"><![CDATA[<h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol><li>本文基于《Ruby on Rails最强教科书》中第7章 Rails的最佳实践,其中有部分因为版本迭代问题有所改动<ul><li>图片上传组件<code>paperclip</code>已过期不再维护,选择了rails新版本自带的<code>Active Storage</code></li></ul></li><li>学习资源<ul><li><a href="https://exercism.org/tracks/ruby">练习网站</a></li><li><a href="https://www.theodinproject.com/paths/full-stack-ruby-on-rails">系统学习教程-全栈</a></li></ul></li><li>Ruby on Rails 似乎是通过大量的组件库,来减少开发的周期(DRY(Don’t Repeat Yourself)不重复造轮子),但是开放通用的组件必然会与定制化的业务有冲突(这样也导致了一定的学习门槛),可能深入学习后,可以较为轻松的定制化</li><li>基于个人入门学习的一些经验,感觉 Ruby on Rails 像是乐高,有丰富的零部件,你可以按照自己的想法快速的搭建出你需要的东西,但是如果没有足够的经验的话,你的成品就仅仅只是玩具</li><li>本文最终产物甚至于连玩具都算不上,仅仅是浅尝辄止的初步了解</li><li>环境安装参考<a href="https://ruby-china.org/wiki/rvm-guide">RVM 实用指南</a></li><li>需要注意的是rails是充血模型,与Java的贫血模型是完全相反的设计理念</li><li>环境依赖数据库:PostgreSQL、缓存:Redis,个人推荐使用docker安装,其他方式也可,只要可以使用</li><li><code>ruby</code>国内官网<a href="https://ruby-china.org/">ruby-china.org</a></li><li>如果是找工作不推荐学习<code>ruby</code>,个人兴趣多学一门语言是推荐的,<code>ruby</code>的一些设计理念还是很不错的</li></ol><h4 id="引用博文"><a href="#引用博文" class="headerlink" title="引用博文"></a>引用博文</h4><ol><li><a href="https://dev.to/eclecticcoding/part-1-rails-active-storage-1ikh">part-1-rails-active-storage-1ikh</a></li><li><a href="https://zhuanlan.zhihu.com/p/576500762">Ruby学习指南</a></li><li><a href="https://ruby-china.github.io/rails-guides/getting_started.html">Rails 入门-官方指导手册</a></li></ol><span id="more"></span><h4 id="创建新的Project"><a href="#创建新的Project" class="headerlink" title="创建新的Project"></a>创建新的Project</h4><ol><li>在指定的工程目录,执行<code>rails new ProjectName</code>,等待命令执行结束,执行结束后,将会在当前目录下创建新的目录,目录名为指定的<code>ProjectName</code>,内容是一个基础的Rails工程</li><li>进入工程目录,执行<code>rails s</code>(<code>rails server</code>的简写版)启动服务,然后浏览器访问<code>http://127.0.0.1:3000/</code>就可以看到欢迎界面了,恭喜你,踏出了在Rails上的第一步</li></ol><h4 id="调整Gemfile"><a href="#调整Gemfile" class="headerlink" title="调整Gemfile"></a>调整Gemfile</h4><h5 id="数据库gem,不调整也可,只是后续数据库操作使用sqlite3"><a href="#数据库gem,不调整也可,只是后续数据库操作使用sqlite3" class="headerlink" title="数据库gem,不调整也可,只是后续数据库操作使用sqlite3"></a>数据库gem,不调整也可,只是后续数据库操作使用sqlite3</h5><figure class="highlight ruby"><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"><span class="comment"># 将</span></span><br><span class="line"><span class="comment"># Use sqlite3 as the database for Active Record</span></span><br><span class="line">gem <span class="string">'sqlite3'</span>, <span class="string">'~> 1.4'</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 替换为</span></span><br><span class="line"><span class="comment"># Use postgresql as the database for Active Record</span></span><br><span class="line">gem <span class="string">'pg'</span>, <span class="string">'~> 1.1'</span></span><br></pre></td></tr></table></figure><p>注:还需额外工作才能完成数据库的切换操作</p><ol><li>在工程根目录执行命令<code>bundle update</code>, 执行命令<code>bundle install</code></li><li>修改数据库配置文件<code>config/database.yml</code><figure class="highlight yml"><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"></span><br><span class="line"><span class="attr">default:</span> <span class="meta">&default</span></span><br><span class="line"> <span class="attr">adapter:</span> <span class="string">postgresql</span></span><br><span class="line"> <span class="attr">encoding:</span> <span class="string">unicode</span></span><br><span class="line"> <span class="attr">pool:</span> <%=<span class="language-ruby"> <span class="variable constant_">ENV</span>.fetch(<span class="string">"RAILS_MAX_THREADS"</span>) { <span class="number">5</span> } </span>%></span><br><span class="line"> <span class="attr">username:</span> <span class="string">postgres</span></span><br><span class="line"> <span class="attr">password:</span> <span class="number">123456</span></span><br><span class="line"> <span class="attr">host:</span> <span class="string">localhost</span></span><br><span class="line"> <span class="attr">port:</span> <span class="number">5432</span></span><br><span class="line"></span><br><span class="line"><span class="attr">development:</span></span><br><span class="line"> <span class="string"><<:</span> <span class="meta">*default</span></span><br><span class="line"> <span class="attr">database:</span> <span class="string">RailsCombineDemo_development</span></span><br><span class="line"></span><br><span class="line"><span class="attr">test:</span></span><br><span class="line"> <span class="string"><<:</span> <span class="meta">*default</span></span><br><span class="line"> <span class="attr">database:</span> <span class="string">RailsCombineDemo_test</span></span><br><span class="line"></span><br><span class="line"><span class="attr">production:</span></span><br><span class="line"> <span class="string"><<:</span> <span class="meta">*default</span></span><br><span class="line"> <span class="attr">database:</span> <span class="string">RailsCombineDemo_production</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails db:create</code></li></ol><h5 id="development环境添加调试辅助组件"><a href="#development环境添加调试辅助组件" class="headerlink" title="development环境添加调试辅助组件"></a>development环境添加调试辅助组件</h5><figure class="highlight ruby"><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">group <span class="symbol">:development</span> <span class="keyword">do</span></span><br><span class="line"> gem <span class="string">'better_errors'</span></span><br><span class="line"> gem <span class="string">'binding_of_caller'</span></span><br><span class="line"> ...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><h5 id="代码风格纠错工具(也可以帮助进行代码简化)"><a href="#代码风格纠错工具(也可以帮助进行代码简化)" class="headerlink" title="代码风格纠错工具(也可以帮助进行代码简化)"></a>代码风格纠错工具(也可以帮助进行代码简化)</h5><figure class="highlight ruby"><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">group <span class="symbol">:development</span> <span class="keyword">do</span></span><br><span class="line"> ...</span><br><span class="line"> gem <span class="string">'rubocop'</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><p>注:需要额外工作完成相关配置</p><ol><li>在工程根目录执行命令<code>bundle install</code></li><li>在工程根目录下,执行命令<code>bundle exec rubocop --auto-gen-config</code>,生成文件<code>.rubocop_todo.yml</code></li><li>在工程根目录下,创建文件<code>.rubocop.yml</code>,填充内容为<figure class="highlight yml"><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="attr">inherit_from:</span> <span class="string">.rubocop_todo.yml</span></span><br><span class="line"></span><br><span class="line"><span class="attr">AllCops:</span></span><br><span class="line"> <span class="attr">NewCops:</span> <span class="string">enable</span></span><br><span class="line"> <span class="attr">Exclude:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">'bin/**/*'</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">'db/*'</span></span><br><span class="line"></span><br><span class="line"><span class="attr">Documentation:</span></span><br><span class="line"> <span class="attr">Enabled:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"><span class="attr">Metrics/LineLength:</span></span><br><span class="line"> <span class="attr">Max:</span> <span class="number">120</span></span><br><span class="line"></span><br><span class="line"><span class="attr">Metrics/BlockLength:</span></span><br><span class="line"> <span class="attr">Exclude:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">spec/**/*</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">config/**/*</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录,执行命令<code>bundle exec rubocop</code>,检查代码风格问题<ul><li>推荐使用IDE - <a href="https://www.jetbrains.com/ruby/">RubyMine</a>,配置好<code>rubocop</code>后,在开发过程中会自动提示优化项</li></ul></li></ol><h5 id="模型表结构注释组件"><a href="#模型表结构注释组件" class="headerlink" title="模型表结构注释组件"></a>模型表结构注释组件</h5><figure class="highlight ruby"><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">group <span class="symbol">:development</span> <span class="keyword">do</span></span><br><span class="line"> ...</span><br><span class="line"> gem <span class="string">'annotate'</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure><ol><li>在工程根目录执行命令<code>bundle install</code></li><li>在工程根目录执行命令<code>rails g annotate:install</code><figure class="highlight shell"><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">Running via Spring preloader in process 71811</span><br><span class="line"> create lib/tasks/auto_annotate_models.rake</span><br></pre></td></tr></table></figure></li></ol><h5 id="环境变量管理组件"><a href="#环境变量管理组件" class="headerlink" title="环境变量管理组件"></a>环境变量管理组件</h5><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gem <span class="string">'dotenv-rails'</span>, <span class="symbol">groups:</span> [<span class="symbol">:development</span>, <span class="symbol">:test</span>]</span><br></pre></td></tr></table></figure><ol><li>在工程根目录执行命令<code>bundle install</code></li><li>在工程根目录执行命令<code>touch .env</code>创建环境变量管理文件</li><li>在文件<code>.gitignore</code>中添加 <code>.env</code> 不提交环境变量信息到git中</li></ol><p>至此,初步的组件以来就都装好了,接下来将会学习<code>devise</code>、<code>mail</code>、<code>sidekiq</code>、<code>image_processing</code>、<code>kaminari</code>、<code>counter_culture</code>、<code>activeadmin</code>组件的入门使用</p><h4 id="Hello-World"><a href="#Hello-World" class="headerlink" title="Hello World"></a>Hello World</h4><ol><li>在工程根目录执行命令<code>rails g controller home</code> 生成首页<code>controller</code></li><li>修改路由配置文件<code>.../config/routes.rb</code> ,添加路由配置<code>root 'home#index'</code></li><li>修改对应的<code>controller</code> -> <code>.../app/controllers/home_controller.rb</code>,添加对应方法函数定义<figure class="highlight ruby"><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 class="keyword">class</span> <span class="title class_">HomeController</span> < <span class="title class_ inherited__">ApplicationController</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">index</span></span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>制作首页视图,创建文件<code>.../app/views/home/index.html.erb</code>,添加内容<code><h1>Hello World!</h1></code></li><li>在项目根目录执行命令<code>rails s</code>启动服务,并访问<code>http://127.0.0.1:3000/</code>即可看到首页内容</li></ol><h4 id="用户登录认证(devise)"><a href="#用户登录认证(devise)" class="headerlink" title="用户登录认证(devise)"></a>用户登录认证(devise)</h4><ol><li>在<code>Gemfile</code>中添加对应<code>gem 'devise'</code></li><li>在工程根目录执行命令<code>bundle install</code></li><li>在工程根目录执行命令<code>rails g devise:install</code>,控制台输出为<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">Running via Spring preloader in process 49687</span><br><span class="line"> create config/initializers/devise.rb</span><br><span class="line"> create config/locales/devise.en.yml</span><br><span class="line">===============================================================================</span><br><span class="line"></span><br><span class="line">Depending on your application's configuration some manual setup may be required:</span><br><span class="line"></span><br><span class="line"> 1. Ensure you have defined default url options in your environments files. Here</span><br><span class="line"> is an example of default_url_options appropriate for a development environment</span><br><span class="line"> in config/environments/development.rb:</span><br><span class="line"></span><br><span class="line"> config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }</span><br><span class="line"></span><br><span class="line"> In production, :host should be set to the actual host of your application.</span><br><span class="line"></span><br><span class="line"> * Required for all applications. *</span><br><span class="line"></span><br><span class="line"> 2. Ensure you have defined root_url to *something* in your config/routes.rb.</span><br><span class="line"> For example:</span><br><span class="line"></span><br><span class="line"> root to: "home#index"</span><br><span class="line"> </span><br><span class="line"> * Not required for API-only Applications *</span><br><span class="line"></span><br><span class="line"> 3. Ensure you have flash messages in app/views/layouts/application.html.erb.</span><br><span class="line"> For example:</span><br><span class="line"></span><br><span class="line"> <p class="notice"><%= notice %></p></span><br><span class="line"> <p class="alert"><%= alert %></p></span><br><span class="line"></span><br><span class="line"> * Not required for API-only Applications *</span><br><span class="line"></span><br><span class="line"> 4. You can copy Devise views (for customization) to your app by running:</span><br><span class="line"></span><br><span class="line"> rails g devise:views</span><br><span class="line"> </span><br><span class="line"> * Not required *</span><br><span class="line"></span><br><span class="line">===============================================================================</span><br></pre></td></tr></table></figure></li><li>修改配置文件<code>.../config/environments/development.rb</code>,添加配置<code>config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }</code></li><li>添加辅助信息打印,在<code>.../app/helpers/application_helper.rb</code>中添加<figure class="highlight ruby"><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"><span class="keyword">def</span> <span class="title function_">flash_message</span>(<span class="params">message, klass</span>)</span><br><span class="line"> content_tag(<span class="symbol">:div</span>, <span class="symbol">class:</span> <span class="string">"alert alert-<span class="subst">#{klass}</span>"</span>) <span class="keyword">do</span></span><br><span class="line"> concat content_tag(<span class="symbol">:button</span>, <span class="string">'x'</span>, <span class="symbol">class:</span> <span class="string">'close'</span>, <span class="symbol">data:</span> { <span class="symbol">dismiss:</span> <span class="string">'alert'</span> })</span><br><span class="line"> concat raw(message)</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在<code>.../app/views/layouts/application.html.erb</code>中添加<figure class="highlight html"><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"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> ...</span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span></span><br><span class="line"> <%= flash_message(flash[:success], :success) if flash[:success] %></span><br><span class="line"> <%= flash_message(flash[:error], :error) if flash[:error] %></span><br><span class="line"> <%= flash_message(flash[:alert], :alert) if flash[:alert] %></span><br><span class="line"> <%= flash_message(flash[:notice], :notice) if flash[:notice] %></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails g devise:views</code>生成视图</li><li>在工程根目录执行命令<code>rails secret</code>发行密钥</li><li>在环境变量配置文件<code>.../.env</code>中添加配置<code>SECRET_TOKEN=</code>,并将上一步生成的密钥配置在其中</li><li>在配置文件<code>.../config/initializers/devise.rb</code>添加配置<figure class="highlight ruby"><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">Devise.setup <span class="keyword">do</span> |<span class="params">config</span>|</span><br><span class="line">...</span><br><span class="line"> config.secret_key = <span class="variable constant_">ENV</span>.fetch(<span class="string">"DEVISE_SECRET_TOKEN"</span>)</span><br><span class="line">...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails g devise user</code>生成用户模型</li><li>修改数据库迁移文件<code>.../db/migrate/20221225161206_devise_create_users.rb</code>,将<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"> <span class="comment">## Confirmable</span></span><br><span class="line"> t.string <span class="symbol">:confirmation_token</span></span><br><span class="line"> t.datetime <span class="symbol">:confirmed_at</span></span><br><span class="line"> t.datetime <span class="symbol">:confirmation_sent_at</span></span><br><span class="line"> t.string <span class="symbol">:unconfirmed_email</span> <span class="comment"># Only if using reconfirmable</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">## Lockable</span></span><br><span class="line"> t.integer <span class="symbol">:failed_attempts</span>, <span class="symbol">default:</span> <span class="number">0</span>, <span class="symbol">null:</span> <span class="literal">false</span> <span class="comment"># Only if lock strategy is :failed_attempts</span></span><br><span class="line"> t.string <span class="symbol">:unlock_token</span> <span class="comment"># Only if unlock strategy is :email or :both</span></span><br><span class="line"> t.datetime <span class="symbol">:locked_at</span></span><br><span class="line">...</span><br><span class="line"> add_index <span class="symbol">:users</span>, <span class="symbol">:confirmation_token</span>, <span class="symbol">unique:</span> <span class="literal">true</span></span><br><span class="line"> add_index <span class="symbol">:users</span>, <span class="symbol">:unlock_token</span>, <span class="symbol">unique:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure>取消注释</li><li>在模型文件<code>.../app/models/user.rb</code>,添加devise配置<code>:confirmable, :lockable, :timeoutable</code><figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">User</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line"> devise <span class="symbol">:database_authenticatable</span>, <span class="symbol">:registerable</span>,</span><br><span class="line"> <span class="symbol">:recoverable</span>, <span class="symbol">:rememberable</span>, <span class="symbol">:validatable</span>,</span><br><span class="line"> <span class="symbol">:confirmable</span>, <span class="symbol">:lockable</span>, <span class="symbol">:timeoutable</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails db:migrate</code>完成数据库表结构升级</li><li>在<code>.../app/helpers/application_helper.rb</code>中添加字符串常量<figure class="highlight ruby"><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 class="keyword">module</span> ApplicationHelper</span><br><span class="line"> <span class="variable constant_">APP_NAME</span> = <span class="string">'RailsCombineDemo'</span>.freeze</span><br><span class="line"> ...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>创建文件<code>.../app/views/layouts/_header.html.erb</code>,并填充代码<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">nav</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">"/"</span>></span><%= ApplicationHelper::APP_NAME %><span class="tag"></<span class="name">a</span>></span></span><br><span class="line"></span><br><span class="line"> <% if user_signed_in? %></span><br><span class="line"> <span class="tag"><<span class="name">ul</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span></span><br><span class="line"> <%= link_to "个人信息编辑", edit_user_registration_path %></span><br><span class="line"> <span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">li</span>></span></span><br><span class="line"> <%= link_to "退出", destroy_user_session_path, method: :delete %></span><br><span class="line"> <span class="tag"></<span class="name">li</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">ul</span>></span></span><br><span class="line"> <% end %></span><br><span class="line"><span class="tag"></<span class="name">nav</span>></span></span><br></pre></td></tr></table></figure></li><li>在<code>.../app/views/layouts/application.html.erb</code>中的<code><body></code>中添加<code><%= render "layouts/header" %></code>将header文件渲染</li><li>在<code>.../app/controllers/application_controller.rb</code>中添加拦截action<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">ApplicationController</span> < <span class="title class_ inherited__">ActionController::Base</span></span><br><span class="line"> before_action <span class="symbol">:authenticate_user!</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails s</code>启动服务,并访问<code>http://127.0.0.1:3000/</code>,将会看到登录界面<br><img src="https://cdn.jsdelivr.net/gh/JanWarlen/PictureBed@dev/blog/Snipaste_2022-12-26_00-28-47.png" alt="登录界面"></li></ol><h4 id="用户注册发送确认邮件(Action-Mailer、SideKiq)"><a href="#用户注册发送确认邮件(Action-Mailer、SideKiq)" class="headerlink" title="用户注册发送确认邮件(Action Mailer、SideKiq)"></a>用户注册发送确认邮件(Action Mailer、SideKiq)</h4><ol><li>大部分的邮件服务提供商都可以开启SMTP服务,可以先将自己的某个邮件开启SMTP服务后再继续</li><li>在配置文件<code>.../config/environments/development.rb</code>中添加配置<figure class="highlight ruby"><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">Rails.application.configure <span class="keyword">do</span></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"> config.action_mailer.delivery_method = <span class="symbol">:smtp</span></span><br><span class="line"> config.action_mailer.smtp_settings = {</span><br><span class="line"> <span class="symbol">address:</span> <span class="variable constant_">ENV</span>.fetch(<span class="string">'MAILER_SMTP_ADDRESS'</span>),</span><br><span class="line"> <span class="symbol">port:</span> <span class="variable constant_">ENV</span>.fetch(<span class="string">'MAILER_SMTP_PORT'</span>),</span><br><span class="line"> <span class="symbol">authentication:</span> <span class="variable constant_">ENV</span>.fetch(<span class="string">'MAILER_SMTP_AUTHENTICATION'</span>),</span><br><span class="line"> <span class="symbol">user_name:</span> <span class="variable constant_">ENV</span>.fetch(<span class="string">'MAILER_SMTP_USER_NAME'</span>),</span><br><span class="line"> <span class="symbol">password:</span> <span class="variable constant_">ENV</span>.fetch(<span class="string">'MAILER_SMTP_PASSWORD'</span>),</span><br><span class="line"> <span class="symbol">enable_starttls_auto:</span> ActiveModel::Type::Boolean.new.cast(<span class="variable constant_">ENV</span>.fetch(<span class="string">'MAILER_SMTP_ENABLE_STARTTLS_AUTO'</span>, <span class="literal">true</span>))</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在环境变量配置文件<code>.../.env</code>中添加配置<figure class="highlight plaintext"><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">MAILER_SMTP_ADDRESS=smtp.qq.com</span><br><span class="line">MAILER_SMTP_PORT=587</span><br><span class="line">MAILER_SMTP_AUTHENTICATION=plain</span><br><span class="line">MAILER_SMTP_USER_NAME=</span><br><span class="line">MAILER_SMTP_PASSWORD=</span><br><span class="line">MAILER_SMTP_ENABLE_STARTTLS_AUTO=true</span><br></pre></td></tr></table></figure></li><li>修改配置文件<code>.../config/initializers/devise.rb</code>中<code> config.mailer_sender = ENV.fetch("MAILER_SMTP_USER_NAME")</code>,确保权限控制组件可以使用指定的邮件服务发送邮件</li><li>安装redis,推荐使用docker安装,方便管理</li><li>在<code>.../Gemfile</code>中添加配置<code>gem 'sidekiq'</code></li><li>在工程根目录执行命令<code>bundle install</code>安装gem</li><li>在配置文件<code>.../config/application.rb</code>中添加配置<code>config.active_job.queue_adapter = :Sidekiq</code></li><li>添加配置文件<code>.../config/initializers/sidekiq.rb</code>,添加配置<figure class="highlight ruby"><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">Sidekiq.configure_client <span class="keyword">do</span> |<span class="params">config</span>|</span><br><span class="line"> config.redis = {</span><br><span class="line"> <span class="symbol">url:</span> <span class="variable constant_">ENV</span>.fetch(<span class="string">'REDIS_URL'</span>)</span><br><span class="line"> }</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">Sidekiq.configure_server <span class="keyword">do</span> |<span class="params">config</span>|</span><br><span class="line"> config.redis = {</span><br><span class="line"> <span class="symbol">url:</span> <span class="variable constant_">ENV</span>.fetch(<span class="string">'REDIS_URL'</span>)</span><br><span class="line"> }</span><br><span class="line"> config.logger.level = Logger.const_get(<span class="variable constant_">ENV</span>.fetch(<span class="string">'LOG_LEVEL'</span>, <span class="string">'info'</span>).upcase.to_s)</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在环境变量配置文件<code>.../.env</code>中配置redis</li><li>创建配置文件<code>.../config/sidekiq.yml</code>,添加配置<figure class="highlight yml"><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"><span class="string">:concurrency:</span> <span class="number">5</span></span><br><span class="line"></span><br><span class="line"><span class="string">:queues:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">default</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">mailers</span></span><br></pre></td></tr></table></figure></li><li>在模型<code>.../app/models/user.rb</code>中添加任务转发异步任务代码<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">User</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">send_devise_notification</span>(<span class="params">notification, *args</span>)</span><br><span class="line"> devise_mailer.send(notification, <span class="variable language_">self</span>, *args).deliver_later</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>额外打开一个终端terminal窗口,在工程根目录执行命令<code>bundle exec sidekiq -C config/sidekiq.yml -e development</code>,该进程为异步任务处理进程</li><li>在工程根目录执行命令<code>rails s</code>启动服务,并访问<code>http://127.0.0.1:3000/</code>,尝试注册账号,成功注册将会收到确认邮件</li></ol><h4 id="个人信息"><a href="#个人信息" class="headerlink" title="个人信息"></a>个人信息</h4><ol><li>添加用户名,在工程根目录执行命令<code>rails g migration add_name_to_users name:string:uniq</code>,生成文件<code>db/migrate/20221218065737_add_name_to_users.rb</code>,确认文件内容<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">AddNameToUsers</span> < <span class="title class_ inherited__">ActiveRecord::Migration</span>[<span class="number">6.1</span>]</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">change</span></span><br><span class="line"> add_column <span class="symbol">:users</span>, <span class="symbol">:name</span>, <span class="symbol">:string</span></span><br><span class="line"> add_index <span class="symbol">:users</span>, <span class="symbol">:name</span>, <span class="symbol">unique:</span> <span class="literal">true</span></span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails db:migrate</code>,完成数据库表结构变更</li><li>在<code>.../app/controllers/application_controller.rb</code>中添加参数过滤<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">ApplicationController</span> < <span class="title class_ inherited__">ActionController::Base</span></span><br><span class="line">...</span><br><span class="line"> before_action <span class="symbol">:configure_permitted_parameters</span>, <span class="symbol">if:</span> <span class="symbol">:devise_controller?</span></span><br><span class="line"></span><br><span class="line"> protected</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">configure_permitted_parameters</span></span><br><span class="line"> added_attrs = [<span class="symbol">:name</span>, <span class="symbol">:email</span>, <span class="symbol">:password</span>, <span class="symbol">:password_confirmation</span>, <span class="symbol">:remember_me</span>]</span><br><span class="line"> devise_parameter_sanitizer.permit <span class="symbol">:sign_up</span>, <span class="symbol">keys:</span> added_attrs</span><br><span class="line"> devise_parameter_sanitizer.permit <span class="symbol">:account_update</span>, <span class="symbol">keys:</span> added_attrs</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在模型<code>.../app/models/user.rb</code>中添加虚拟的login属性<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">User</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line">...</span><br><span class="line"> <span class="keyword">attr_accessor</span> <span class="symbol">:login</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">login=</span>(<span class="params">login</span>)</span><br><span class="line"> <span class="variable">@login</span> = login</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">login</span></span><br><span class="line"> <span class="variable">@login</span> |<span class="params"></span>| <span class="variable language_">self</span>.name |<span class="params"></span>| <span class="variable language_">self</span>.email</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line">...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>修改devise配置文件<code>..../config/initializers/devise.rb</code>,将配置<code>config.authentication_keys</code>取消注释,并且修改为<code>config.authentication_keys = [:login]</code></li><li>重写模型<code>.../app/models/user.rb</code>的<code>find_for_database_authentication</code><figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">User</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line">...</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">self</span>.find_for_database_authentication(warden_conditions)</span><br><span class="line"> conditions = warden_conditions.dup</span><br><span class="line"> conditions[<span class="symbol">:email</span>]&.downcase!</span><br><span class="line"> login = conditions.delete(<span class="symbol">:login</span>)</span><br><span class="line"> where(conditions.to_hash).where(</span><br><span class="line"> [<span class="string">"lower(name) = :value or lower(email) = :value"</span>,</span><br><span class="line"> { <span class="symbol">value:</span> login.downcase }]</span><br><span class="line"> ).first</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在模型<code>.../app/models/user.rb</code>中添加校验<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">User</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line">...</span><br><span class="line"> validates <span class="symbol">:name</span>,</span><br><span class="line"> <span class="symbol">presence:</span> <span class="literal">true</span>,</span><br><span class="line"> <span class="symbol">uniqueness:</span> { <span class="symbol">case_sensitive:</span> <span class="literal">false</span>}</span><br><span class="line"> validates_format_of <span class="symbol">:name</span>, <span class="symbol">with:</span> /^[a-zA-<span class="variable constant_">Z0</span>-9_¥.]*<span class="variable">$/</span>, <span class="symbol">multiline:</span> <span class="literal">true</span></span><br><span class="line"> validate <span class="symbol">:validate_name</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">validate_name</span></span><br><span class="line"> errors.add(<span class="symbol">:name</span>, <span class="symbol">:invalid</span>) <span class="keyword">if</span> User.where(<span class="symbol">email:</span> name).exists?</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line">...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>修改登录界面<code>.../app/views/devise/sessions/new.html.erb</code>,将登录的凭证调整为<code>login</code>属性<figure class="highlight erb"><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 class="language-xml"><span class="comment"><!-- 将 --></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"field"</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.label <span class="symbol">:email</span> </span><span class="language-xml">%><span class="tag"><<span class="name">br</span> /></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.email_field <span class="symbol">:email</span>, <span class="symbol">autofocus:</span> <span class="literal">true</span>, <span class="symbol">autocomplete:</span> <span class="string">"email"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="comment"><!-- 修改为 --></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"field"</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.label <span class="string">"email or username"</span> </span><span class="language-xml">%><span class="tag"><<span class="name">br</span> /></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.text_field <span class="symbol">:login</span>, <span class="symbol">autofocus:</span> <span class="literal">true</span>, <span class="symbol">placeholder:</span> <span class="string">"Enter email or username"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></li><li>修改注册页面<code>.../app/views/devise/registrations/new.html.erb</code>,添加属性<code>name</code><figure class="highlight erb"><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"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"field"</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.label <span class="symbol">:email</span> </span><span class="language-xml">%><span class="tag"><<span class="name">br</span> /></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.email_field <span class="symbol">:email</span>, <span class="symbol">autofocus:</span> <span class="literal">true</span>, <span class="symbol">autocomplete:</span> <span class="string">"email"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="comment"><!-- 在email下方添加name --></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"field"</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.label <span class="symbol">:name</span> </span><span class="language-xml">%><span class="tag"><<span class="name">br</span> /></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.text_field <span class="symbol">:name</span>, <span class="symbol">placeholder:</span> <span class="string">"Username"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></li><li>修改用户信息编辑页面<code>.../app/views/devise/registrations/edit.html.erb</code>,添加属性<code>name</code><figure class="highlight erb"><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"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"field"</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.label <span class="symbol">:email</span> </span><span class="language-xml">%><span class="tag"><<span class="name">br</span> /></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.email_field <span class="symbol">:email</span>, <span class="symbol">autofocus:</span> <span class="literal">true</span>, <span class="symbol">autocomplete:</span> <span class="string">"email"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="comment"><!-- 同样是在email下方添加name --></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"field"</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.label <span class="symbol">:name</span> </span><span class="language-xml">%><span class="tag"><<span class="name">br</span> /></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.text_field <span class="symbol">:name</span>, <span class="symbol">placeholder:</span> <span class="string">"Username"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></li><li>修改首页<code>.../app/views/home/index.html.erb</code>,打印当前登录人<code>name</code><figure class="highlight erb"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="language-xml"><span class="tag"><<span class="name">h2</span>></span><%=</span><span class="language-ruby"> <span class="variable">@user</span>.name </span><span class="language-xml">%><span class="tag"></<span class="name">h2</span>></span></span></span><br></pre></td></tr></table></figure></li><li>修改首页对应的<code>controller</code> <code>.../app/controllers/home_controller.rb</code>,在<code>index</code>中返回当前登录用户<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">HomeController</span> < <span class="title class_ inherited__">ApplicationController</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">index</span></span><br><span class="line"> <span class="variable">@user</span> = current_user</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails db:reset</code>,清理数据库(该步骤需要停止异步任务sidekiq和服务进程,避免数据库占用导致无法清理数据库)</li><li>额外打开一个终端terminal窗口,在工程根目录执行命令<code>bundle exec sidekiq -C config/sidekiq.yml -e development</code>,启动异步任务</li><li>在工程根目录执行命令<code>rails s</code>启动服务,并访问<a href="http://0.0.0.0:3000/">http://0.0.0.0:3000/</a> 完成新用户注册</li><li>使用注册时填写的用户名登录<br><img src="https://cdn.jsdelivr.net/gh/JanWarlen/PictureBed@dev/blog/20230103230914.png"></li></ol><h4 id="头像(image-processing、Active-Storage)"><a href="#头像(image-processing、Active-Storage)" class="headerlink" title="头像(image_processing、Active Storage)"></a>头像(image_processing、Active Storage)</h4><ol><li>书中使用组件<code>paperclip</code>已经deprecated,切换为<a href="https://guides.rubyonrails.org/active_storage_overview.html">ActiveStorage</a></li><li>在配置文件<code>Gemfile</code>中添加依赖<code>gem "image_processing", ">= 1.2"</code>,并在工程根目录执行<code>bundle install</code></li><li>在工程根目录执行<code>rails active_storage:install</code>创建表结构迁移脚本,再执行<code>rails db:migrate</code>完成表创建</li><li>在模型<code>.../app/models/user.rb</code>中添加头像属性<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">User</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line">...</span><br><span class="line"> has_one_attached <span class="symbol">:avatar</span></span><br><span class="line">...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>修改个人信息编辑页面<code>.../app/views/devise/registrations/edit.html.erb</code>,在<code>email</code>上方添加<figure class="highlight erb"><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"><span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"field"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <%</span><span class="language-ruby"> <span class="keyword">if</span> current_user.avatar.attached? </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> image_tag current_user.avatar.variant(<span class="symbol">resize:</span> <span class="string">"128x128!"</span>), <span class="symbol">class:</span> <span class="string">"rounded-circle m-4"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <%</span><span class="language-ruby"> <span class="keyword">end</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.label <span class="symbol">:avatar</span> </span><span class="language-xml">%><span class="tag"><<span class="name">br</span> /></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.file_field <span class="symbol">:avatar</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></li><li>在首页<code>.../app/views/home/index.html.erb</code>中添加<figure class="highlight erb"><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 class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <%</span><span class="language-ruby"> <span class="keyword">if</span> current_user.avatar.attached? </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> image_tag current_user.avatar.variant(<span class="symbol">resize:</span> <span class="string">"128x128!"</span>), <span class="symbol">class:</span> <span class="string">"rounded-circle m-4"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <%</span><span class="language-ruby"> <span class="keyword">end</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> </span><span class="comment"><%#= image_tag current_user.avatar.url, class: 'card-img-top rounded-circle' %></span><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h4</span> <span class="attr">class</span>=<span class="string">""</span>></span><%=</span><span class="language-ruby"> <span class="string">"@<span class="subst">#{<span class="variable">@user</span>.name}</span>"</span> </span><span class="language-xml">%><span class="tag"></<span class="name">h4</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">p</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></li><li>在<code>.../app/controllers/application_controller.rb</code>中的属性过滤添加头像<code>added_attrs = [:name, :email, :password, :password_confirmation, :remember_me, :avatar]</code></li><li>在工程根目录执行命令<code>rails s</code>启动服务,并访问<a href="http://0.0.0.0:3000/">http://0.0.0.0:3000/</a></li><li>登录后进入个人信息编辑界面,上传头像,保存更新(<code>Current password (we need your current password to confirm your changes)</code>需要最下方的输入框输入密码以通过更新拦截)</li><li>更新成功后,首页即可出现头像<img src="https://cdn.jsdelivr.net/gh/JanWarlen/PictureBed@dev/blog/20230104000132.png"></li></ol><h4 id="个人投稿(关联表)"><a href="#个人投稿(关联表)" class="headerlink" title="个人投稿(关联表)"></a>个人投稿(关联表)</h4><ol><li>在工程根目录执行命令<code>rails g model post user_id:integer body:text</code>创建投稿模型</li><li>在工程根目录执行命令<code>rails db:migrate</code>完成表创建</li><li>在模型<code>.../app/models/user.rb</code>中添加投稿关联<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">User</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line">...</span><br><span class="line"> has_many <span class="symbol">:posts</span>, <span class="symbol">inverse_of:</span> <span class="symbol">:user</span></span><br><span class="line">...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在模型<code>.../app/models/post.rb</code>中添加用户关联关系<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">Post</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line"> belongs_to <span class="symbol">:user</span>, <span class="symbol">inverse_of:</span> <span class="symbol">:posts</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在首页<code>.../app/controllers/home_controller.rb</code>中添加参数<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">HomeController</span> < <span class="title class_ inherited__">ApplicationController</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">index</span></span><br><span class="line"> <span class="variable">@user</span> = current_user</span><br><span class="line"> <span class="variable">@post</span> = Post.order(<span class="string">'created_at desc'</span>)</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在首页<code>.../app/views/home/index.html.erb</code>中添加投稿展示<figure class="highlight erb"><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"><span class="language-xml"><span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml">...</span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> render <span class="symbol">partial:</span> <span class="string">"post"</span>, <span class="symbol">collection:</span> <span class="variable">@posts</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></li><li>添加投稿展示页面<code>.../app/views/home/_post.html.erb</code><figure class="highlight erb"><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"><span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"border:2px solid black;"</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> image_tag post.user.avatar.variant(<span class="symbol">resize:</span> <span class="string">"128x128!"</span>), <span class="symbol">class:</span> <span class="string">"rounded-circle m-4"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">strong</span>></span><%=</span><span class="language-ruby"> <span class="string">"@<span class="subst">#{post.user.name}</span>"</span> </span><span class="language-xml">%><span class="tag"></<span class="name">strong</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">""</span>></span><%=</span><span class="language-ruby"> time_ago_in_words(post.created_at) </span><span class="language-xml">%> ago<span class="tag"></<span class="name">span</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">p</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> post.body </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails c</code>,并在控制台执行<code>User.last.posts.create({body: "First Post!"})</code>创建第一个投稿(<code>exit</code>即可退出控制台)</li><li>在工程根目录执行命令<code>rails s</code>启动服务,登录后即可看到投稿展示<img src="https://cdn.jsdelivr.net/gh/JanWarlen/PictureBed@dev/blog/20230104234352.png"></li></ol><h5 id="个人投稿-新增"><a href="#个人投稿-新增" class="headerlink" title="个人投稿 - 新增"></a>个人投稿 - 新增</h5><ol><li>在工程根目录执行命令<code>rails g controller posts create</code></li><li>修改路由配置<code>.../config/routes.rb</code>,删除<code>get 'posts/create'</code>,添加路由配置<code>resources :posts, only: [:create]</code></li><li>在首页<code>.../app/views/home/index.html.erb</code>添加投稿表单和按钮(在投稿展示上方)<figure class="highlight erb"><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"><span class="language-xml"><span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> form_with <span class="symbol">model:</span> <span class="title class_">Post</span>.new <span class="keyword">do</span> |<span class="params">f</span>| </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.text_field <span class="symbol">:body</span>, <span class="symbol">class:</span> <span class="string">""</span>, <span class="symbol">placeholder:</span> <span class="string">"What do we do now?"</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">span</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> f.submit <span class="string">"投稿"</span>, <span class="symbol">class:</span> <span class="string">""</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">span</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="language-xml"> <%</span><span class="language-ruby"> <span class="keyword">end</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"> <%=</span><span class="language-ruby"> render <span class="symbol">partial:</span> <span class="string">"post"</span>, <span class="symbol">collection:</span> <span class="variable">@posts</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></li><li>在对应<code>.../app/controllers/posts_controller.rb</code>中完善投稿保存代码<figure class="highlight ruby"><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">class</span> <span class="title class_">PostsController</span> < <span class="title class_ inherited__">ApplicationController</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">create</span></span><br><span class="line"> <span class="keyword">if</span> current_user.posts.create(post_params)</span><br><span class="line"> flash[<span class="symbol">:notice</span>] = <span class="string">'Post was successfully created.'</span></span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> flash[<span class="symbol">:error</span>] = <span class="string">'Something went wrong.'</span></span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"> redirect_to <span class="symbol">:root</span></span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"> protected</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">post_params</span></span><br><span class="line"> params.<span class="keyword">require</span>(<span class="symbol">:post</span>).permit(<span class="symbol">:body</span>)</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails s</code>启动服务,登录后可以在输入框内输入文本并点击<code>投稿</code>按钮提交保存<img src="https://cdn.jsdelivr.net/gh/JanWarlen/PictureBed@dev/blog/20230104235459.png"></li></ol><h5 id="个人投稿-分页查询-kaminari"><a href="#个人投稿-分页查询-kaminari" class="headerlink" title="个人投稿 - 分页查询(kaminari)"></a>个人投稿 - 分页查询(kaminari)</h5><ol><li>在<code>Gemfile</code>中添加gem依赖<code>gem 'kaminari'</code></li><li>在工程根目录执行命令<code>bundle install</code>安装gem依赖</li><li>在工程根目录执行命令<code>rails g kaminari:config</code>生成分页组件配置</li><li>修改分页配置<code>.../config/initializers/kaminari_config.rb</code>,取消配置<code>config.default_per_page</code>注释,并修改值为3(改小是为了方便体现分页功能)</li><li>修改首页处理器<code>.../app/controllers/home_controller.rb</code>,修改投稿查询代码为<code>@posts = Post.order('created_at desc').page params[:page]</code></li><li>修改首页<code>.../app/views/home/index.html.erb</code>添加分页<figure class="highlight erb"><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 class="language-xml"><%=</span><span class="language-ruby"> render <span class="symbol">partial:</span> <span class="string">"post"</span>, <span class="symbol">collection:</span> <span class="variable">@posts</span> </span><span class="language-xml">%></span></span><br><span class="line"><span class="language-xml"><%=</span><span class="language-ruby"> paginate <span class="variable">@posts</span> </span><span class="language-xml">%></span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails g kaminari:views bootstrap4</code>生成分页前端视图文件</li><li>在工程根目录执行命令<code>rails s</code>启动服务,并人工添加几条投稿,即可看到分页效果<img src="https://cdn.jsdelivr.net/gh/JanWarlen/PictureBed@dev/blog/20230105000718.png"></li></ol><h4 id="投稿统计-counter-culture-和登录时间"><a href="#投稿统计-counter-culture-和登录时间" class="headerlink" title="投稿统计(counter_culture)和登录时间"></a>投稿统计(counter_culture)和登录时间</h4><ol><li>在<code>Gemfile</code>中添加统计计数gem依赖<code>gem 'counter_culture'</code></li><li>在工程根目录执行命令<code>bundle install</code>安装gem依赖</li><li>在工程根目录执行命令<code>rails g counter_culture User posts_count</code>生成表结构迁移文件</li><li>在模型<code>.../app/models/post.rb</code>添加统计数据更新配置<figure class="highlight ruby"><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 class="keyword">class</span> <span class="title class_">Post</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line">...</span><br><span class="line"> counter_culture <span class="symbol">:user</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails db:migrate</code>完成表结构升级</li><li>在工程根目录执行命令<code>rails c</code>,并在控制台执行<code>Post.counter_culture_fix_counts</code>更新统计数据(之前新增的投稿)</li><li>在模型<code>.../app/models/user.rb</code>中添加自定义方法<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">User</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line">...</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">created_month</span></span><br><span class="line"> created_at.strftime(<span class="string">'%Y年%m月'</span>)</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>修改首页<code>.../app/views/home/index.html.erb</code>添加登录信息和投稿统计<figure class="highlight erb"><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"><span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">h4</span> <span class="attr">class</span>=<span class="string">""</span>></span><%=</span><span class="language-ruby"> <span class="string">"@<span class="subst">#{<span class="variable">@user</span>.name}</span>"</span> </span><span class="language-xml">%><span class="tag"></<span class="name">h4</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">""</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">span</span>></span><%=</span><span class="language-ruby"> <span class="string">"<span class="subst">#{<span class="variable">@user</span>.created_month}</span>登录"</span> </span><span class="language-xml">%><span class="tag"></<span class="name">span</span>></span><span class="tag"><<span class="name">br</span>/></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"><<span class="name">span</span>></span><%=</span><span class="language-ruby"> <span class="variable">@user</span>.posts_count </span><span class="language-xml">%>稿件<span class="tag"></<span class="name">span</span>></span></span></span><br><span class="line"><span class="language-xml"> <span class="tag"></<span class="name">p</span>></span></span></span><br><span class="line"><span class="language-xml"><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails s</code>启动服务,登录后即可看到信息<img src="https://cdn.jsdelivr.net/gh/JanWarlen/PictureBed@dev/blog/20230105002831.png"></li></ol><h4 id="权限管理-activeadmin"><a href="#权限管理-activeadmin" class="headerlink" title="权限管理(activeadmin)"></a>权限管理(activeadmin)</h4><ol><li>在<code>Gemfile</code>中添加权限管理gem依赖<code>gem 'activeadmin'</code></li><li>在工程根目录执行命令<code>bundle install</code>安装gem依赖</li><li>在工程根目录执行命令<code>rails g active_admin:install --skip-users</code>生成表结构迁移文件</li><li>在工程根目录执行命令<code>rails db:migrate</code>完成表结构升级</li><li>修改配置文件<code>.../config/initializers/active_admin.rb</code>,将配置<code>config.comments_menu</code>取消注释</li><li>在工程根目录执行命令<code>rails s</code>启动服务,访问<a href="http://0.0.0.0:3000/admin">http://0.0.0.0:3000/admin</a>即可查看dashboard界面</li><li>在工程根目录执行命令<code>rails g active_admin:resource User</code>,即可将用户添加到dashboard界面</li><li>在工程根目录执行命令<code>rails g active_admin:resource Post</code>,即可将投稿添加到dashboard界面</li><li>在工程根目录执行命令<code>rails g migration AddRoleToUser role:integer</code>为模型<code>User</code>添加角色属性</li><li>在工程根目录执行命令<code>rails db:migrate</code>完成表结构升级</li><li>在模型<code>.../app/models/user.rb</code>中添加角色枚举<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">User</span> < <span class="title class_ inherited__">ApplicationRecord</span></span><br><span class="line">...</span><br><span class="line"> enum <span class="symbol">role:</span> { <span class="symbol">user:</span> <span class="number">0</span>, <span class="symbol">admin:</span> <span class="number">1</span> }</span><br><span class="line">...</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails c</code>进入控制台界面,并执行命令<code>User.last.role</code>和<code>User.last.admin?</code>确认角色</li><li>修改权限配置文件<code>.../config/initializers/active_admin.rb</code>将配置<code>config.authentication_method</code>取消注释</li><li>在处理器<code>.../app/controllers/application_controller.rb</code>中添加权限验证方法<figure class="highlight ruby"><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"><span class="keyword">class</span> <span class="title class_">ApplicationController</span> < <span class="title class_ inherited__">ActionController::Base</span></span><br><span class="line">...</span><br><span class="line"> rescue_from SecurityError <span class="keyword">do</span> |<span class="params">e</span>|</span><br><span class="line"> redirect_to root_url, <span class="symbol">notice:</span> <span class="string">'no admin right'</span></span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line">...</span><br><span class="line"> protected</span><br><span class="line">...</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">authenticate_admin_user!</span></span><br><span class="line"> raise SecurityError <span class="keyword">unless</span> current_user.try(<span class="symbol">:admin?</span>)</span><br><span class="line"> <span class="keyword">end</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure></li><li>在工程根目录执行命令<code>rails s</code>启动服务,访问<a href="http://0.0.0.0:3000/admin">http://0.0.0.0:3000/admin</a>确认无法进入dashboard界面(无admin角色权限)</li><li>在控制台界面执行<code>User.last.update_attribute(:role, :admin)</code>,并再次执行步骤12的<code>User.last.role</code>和<code>User.last.admin?</code>确认角色</li><li>再次访问<a href="http://0.0.0.0:3000/admin">http://0.0.0.0:3000/admin</a>即可查看dashboard界面</li></ol>]]></content>
<summary type="html"><h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol>
<li>本文基于《Ruby on Rails最强教科书》中第7章 Rails的最佳实践,其中有部分因为版本迭代问题有所改动<ul>
<li>图片上传组件<code>paperclip</code>已过期不再维护,选择了rails新版本自带的<code>Active Storage</code></li>
</ul>
</li>
<li>学习资源<ul>
<li><a href="https://exercism.org/tracks/ruby">练习网站</a></li>
<li><a href="https://www.theodinproject.com/paths/full-stack-ruby-on-rails">系统学习教程-全栈</a></li>
</ul>
</li>
<li>Ruby on Rails 似乎是通过大量的组件库,来减少开发的周期(DRY(Don’t Repeat Yourself)不重复造轮子),但是开放通用的组件必然会与定制化的业务有冲突(这样也导致了一定的学习门槛),可能深入学习后,可以较为轻松的定制化</li>
<li>基于个人入门学习的一些经验,感觉 Ruby on Rails 像是乐高,有丰富的零部件,你可以按照自己的想法快速的搭建出你需要的东西,但是如果没有足够的经验的话,你的成品就仅仅只是玩具</li>
<li>本文最终产物甚至于连玩具都算不上,仅仅是浅尝辄止的初步了解</li>
<li>环境安装参考<a href="https://ruby-china.org/wiki/rvm-guide">RVM 实用指南</a></li>
<li>需要注意的是rails是充血模型,与Java的贫血模型是完全相反的设计理念</li>
<li>环境依赖数据库:PostgreSQL、缓存:Redis,个人推荐使用docker安装,其他方式也可,只要可以使用</li>
<li><code>ruby</code>国内官网<a href="https://ruby-china.org/">ruby-china.org</a></li>
<li>如果是找工作不推荐学习<code>ruby</code>,个人兴趣多学一门语言是推荐的,<code>ruby</code>的一些设计理念还是很不错的</li>
</ol>
<h4 id="引用博文"><a href="#引用博文" class="headerlink" title="引用博文"></a>引用博文</h4><ol>
<li><a href="https://dev.to/eclecticcoding/part-1-rails-active-storage-1ikh">part-1-rails-active-storage-1ikh</a></li>
<li><a href="https://zhuanlan.zhihu.com/p/576500762">Ruby学习指南</a></li>
<li><a href="https://ruby-china.github.io/rails-guides/getting_started.html">Rails 入门-官方指导手册</a></li>
</ol></summary>
<category term="Ruby" scheme="http://janwarlen.com/categories/Ruby/"/>
<category term="Ruby" scheme="http://janwarlen.com/tags/Ruby/"/>
<category term="Rails" scheme="http://janwarlen.com/tags/Rails/"/>
<category term="编程入门" scheme="http://janwarlen.com/tags/%E7%BC%96%E7%A8%8B%E5%85%A5%E9%97%A8/"/>
</entry>
<entry>
<title>一书一图-深度剖析Apache Dubbo核心技术内幕</title>
<link href="http://janwarlen.com/2022/10/18/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90Apache%20Dubbo%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%E5%86%85%E5%B9%95/"/>
<id>http://janwarlen.com/2022/10/18/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90Apache%20Dubbo%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF%E5%86%85%E5%B9%95/</id>
<published>2022-10-18T10:22:55.000Z</published>
<updated>2022-10-18T18:14:45.086Z</updated>
<content type="html"><![CDATA[<h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol><li>需要重点理解的点是增强SPI和适配器(所有@SPI注解会生成Adaptive适配类),这两点关乎于核心的protocol和invoker的加载和多态控制</li><li>主要关心服务发布流程与消费端启动和调用流程,服务降级、集群容错、负载均衡、线程模型和线程池策略等可以之后单独了解,它们内容多但是代码结构和调用并不复杂</li><li>Dubbo自封装的netty模块可以不用深究,主要分 handler 和 Dubbo自定义的数据协议</li><li>zookeeper主要扮演配置中心和注册中心使用,服务提供者的变动会触发zk节点变动,从而通知到消费者及时变更内存缓存的服务提供者</li><li><a href="https://github.com/JanWarlen/dubbo/tree/comment-2.7.1">注释代码</a></li><li>设计模式多用装饰模式、代理模式、责任链模式、策略模式</li><li>书中有部分内容在阅读时产生了理解歧义,可惜没有联系书籍作者的途径,此处作保留,希望有读者可以交流理解</li></ol><span id="more"></span><h4 id="细节疑问"><a href="#细节疑问" class="headerlink" title="细节疑问"></a>细节疑问</h4><h5 id="图3-6"><a href="#图3-6" class="headerlink" title="图3.6"></a>图3.6</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">invoke</span><span class="params">(Channel channel, String methodKey)</span> {</span><br><span class="line"> <span class="type">Invocation</span> <span class="variable">invocation</span> <span class="operator">=</span> createInvocation(channel, channel.getUrl(), methodKey);</span><br><span class="line"> <span class="comment">// connected 与 disconnected 均不会创建 invocation 实例,因此后续代码不会执行</span></span><br><span class="line"> <span class="keyword">if</span> (invocation != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> received(channel, invocation);</span><br><span class="line"> } <span class="keyword">catch</span> (Throwable t) {</span><br><span class="line"> logger.warn(<span class="string">"Failed to invoke event method "</span> + invocation.getMethodName() + <span class="string">"(), cause: "</span> + t.getMessage(), t);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>关于步骤11,在Debug的过程中发现,对于onconnected事件,此时<code>invocation</code>应该是null,因此不会执行<code>received</code></p><h5 id="P96"><a href="#P96" class="headerlink" title="P96"></a>P96</h5><blockquote><p>从上面的代码可知,MockClusterWrapper 类把 FailoverClusterInvoker 包装成了MockClusterInvoker 实例,所以整个调用链最终调用返回的是 MockClusterInvoker对象。也就是说,本节第一个时序图(见图3.8)中的步骤4返回的是 MockClusterWrapper,然后执行图3.9中的步骤14以获取 MockClusterInvoker 的代理,实现 invoker 到客户端接口的转换…</p></blockquote><p>疑惑点在于在debug过程中,发现<code>org.apache.dubbo.registry.integration.RegistryProtocol#cluster</code>的确是在3.8的步骤4通过IOC注入,但此时并不是返回结果,尚在执行过程中,执行到3-9的步骤14时,通过在<code>org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#buildInvokerChain</code>断点确认 invoker 还是 DubboInvoker,此时只有继续执行,等到3-8的步骤12(org.apache.dubbo.registry.integration.RegistryProtocol#doRefer)时,才会通过之前注入的 MockClusterWrapper 返回 MockClusterInvoker。然后层层返回,最终返回到3-8的步骤4,再通过proxyFactory.getProxy(invoker) 获取代理。</p><h5 id="P230"><a href="#P230" class="headerlink" title="P230"></a>P230</h5><blockquote><p>先看看 DubboCodec 的子类 ExchangeCodec 的 encode() 方法</p></blockquote><p><code>ExchangeCodec</code>应该是<code>DubboCodec</code>的父类</p><p><img src="http://img.janwarlen.com/blog/%E6%B7%B1%E5%BA%A6%E5%89%96%E6%9E%90Apache%20Dubbo%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF.svg"></p>]]></content>
<summary type="html"><h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol>
<li>需要重点理解的点是增强SPI和适配器(所有@SPI注解会生成Adaptive适配类),这两点关乎于核心的protocol和invoker的加载和多态控制</li>
<li>主要关心服务发布流程与消费端启动和调用流程,服务降级、集群容错、负载均衡、线程模型和线程池策略等可以之后单独了解,它们内容多但是代码结构和调用并不复杂</li>
<li>Dubbo自封装的netty模块可以不用深究,主要分 handler 和 Dubbo自定义的数据协议</li>
<li>zookeeper主要扮演配置中心和注册中心使用,服务提供者的变动会触发zk节点变动,从而通知到消费者及时变更内存缓存的服务提供者</li>
<li><a href="https://github.com/JanWarlen/dubbo/tree/comment-2.7.1">注释代码</a></li>
<li>设计模式多用装饰模式、代理模式、责任链模式、策略模式</li>
<li>书中有部分内容在阅读时产生了理解歧义,可惜没有联系书籍作者的途径,此处作保留,希望有读者可以交流理解</li>
</ol></summary>
<category term="中间件" scheme="http://janwarlen.com/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"/>
<category term="读书笔记" scheme="http://janwarlen.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<category term="Dubbo" scheme="http://janwarlen.com/tags/Dubbo/"/>
</entry>
<entry>
<title>一书一图-Netty实战</title>
<link href="http://janwarlen.com/2022/08/12/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-Netty%E5%AE%9E%E6%88%98/"/>
<id>http://janwarlen.com/2022/08/12/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-Netty%E5%AE%9E%E6%88%98/</id>
<published>2022-08-12T06:22:55.000Z</published>
<updated>2022-09-02T02:49:26.906Z</updated>
<content type="html"><![CDATA[<h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol><li>建议先学习不使用Netty的网络编程(即Java内置的Socket/IO/NIO),这样才能更好的学习Netty</li><li>NIO学习推荐<a href="https://ifeve.com/java-nio-all/">java-nio-all</a>,不过有几篇还没有人翻译,需要看原文学习</li><li>代码可以从GitHub上直接下载<a href="https://github.com/normanmaurer/netty-in-action">netty-in-action</a></li><li>整本书的内容是针对Netty的学习,不同的业务场景中Netty的用法未涉及,最后两个章节的案例研究更多的是讨论选择Netty的业务背景,对于Netty的使用细节基本不涉及</li><li>可以的话建议是手动跟着敲一遍,可以加深一些映像</li><li>本书虽然是说实战,但是局限比较大,基本上是相关组件的基本使用,属于入门,推荐通过项目<a href="https://github.com/jwpttcg66/NettyGameServer">NettyGameServer</a>/<a href="https://github.com/eclipse-vertx/vert.x">vert.x</a>/<a href="https://github.com/crossoverJie/cim">cim</a>加深熟练度</li></ol><span id="more"></span><p><img src="http://img.janwarlen.com/blog/Netty%E5%AE%9E%E6%88%98.png"></p>]]></content>
<summary type="html"><h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol>
<li>建议先学习不使用Netty的网络编程(即Java内置的Socket&#x2F;IO&#x2F;NIO),这样才能更好的学习Netty</li>
<li>NIO学习推荐<a href="https://ifeve.com/java-nio-all/">java-nio-all</a>,不过有几篇还没有人翻译,需要看原文学习</li>
<li>代码可以从GitHub上直接下载<a href="https://github.com/normanmaurer/netty-in-action">netty-in-action</a></li>
<li>整本书的内容是针对Netty的学习,不同的业务场景中Netty的用法未涉及,最后两个章节的案例研究更多的是讨论选择Netty的业务背景,对于Netty的使用细节基本不涉及</li>
<li>可以的话建议是手动跟着敲一遍,可以加深一些映像</li>
<li>本书虽然是说实战,但是局限比较大,基本上是相关组件的基本使用,属于入门,推荐通过项目<a href="https://github.com/jwpttcg66/NettyGameServer">NettyGameServer</a>&#x2F;<a href="https://github.com/eclipse-vertx/vert.x">vert.x</a>&#x2F;<a href="https://github.com/crossoverJie/cim">cim</a>加深熟练度</li>
</ol></summary>
<category term="中间件" scheme="http://janwarlen.com/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"/>
<category term="读书笔记" scheme="http://janwarlen.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<category term="RocketMQ" scheme="http://janwarlen.com/tags/RocketMQ/"/>
</entry>
<entry>
<title>一书一图-《RocketMQ技术内幕》第2版</title>
<link href="http://janwarlen.com/2022/07/26/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-%E3%80%8ARocketMQ%E6%8A%80%E6%9C%AF%E5%86%85%E5%B9%95%E3%80%8B%E7%AC%AC2%E7%89%88/"/>
<id>http://janwarlen.com/2022/07/26/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-%E3%80%8ARocketMQ%E6%8A%80%E6%9C%AF%E5%86%85%E5%B9%95%E3%80%8B%E7%AC%AC2%E7%89%88/</id>
<published>2022-07-26T15:22:55.000Z</published>
<updated>2022-09-02T02:49:26.957Z</updated>
<content type="html"><![CDATA[<h4 id="引用链接"><a href="#引用链接" class="headerlink" title="引用链接"></a>引用链接</h4><ol><li><a href="https://blog.csdn.net/qq_32099833/article/details/120247549">RocketMQ定时消息实现原理分析</a>:时序图</li><li><a href="https://developer.aliyun.com/article/827385">RocketMQ 实战(四) - 订阅机制和定时消息</a>:关于correctDeliverTimestamp的理解</li><li><a href="https://xie.infoq.cn/article/cd087fb325ac2ceb0bf0ccf2d">深入了解 RocketMQ 之过滤器</a>:BloomFilter</li><li><a href="https://blog.csdn.net/jiaomeng/article/details/1495500">Bloom Filter概念和原理</a>:BloomFilter</li><li><a href="https://rocketmq.apache.org/rocketmq/filter-messages-by-sql92-in-rocketmq/">Filter Messages By SQL92 In RocketMQ</a></li><li><a href="https://www.cnblogs.com/sunshine-2015/p/8998549.html">RocketMQ源码 — 七、 RocketMQ高可用(2)</a>:主从同步时序图</li></ol><h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol><li><p>本书存在较多的错误,主要问题是代码片段与文字描述的衔接对不上,并且存在少量的代码注释错误(可能是我看的代码分支与作者看的分支版本不同导致理解产生偏差),但是不能否认这本书的价值,尤其是一些关键地方的逻辑描述,能够帮助减少很多源码阅读理解的时间</p></li><li><p>建议以源码阅读为主,如果已经买了,可以同步对照书籍中的文字描述确认自己理解是否正确</p></li><li><p>个人已上传<a href="https://github.com/JanWarlen/rocketmq/tree/release-4.9.4">RocketMQ(release-4.9.4)</a>与<a href="https://github.com/JanWarlen/dledger/tree/0.2.6">DLedger(tag:dledger-0.2.6)</a>带注释版本,部分核心入口已在工程的 ReadME 文档中标注</p></li><li><p>建议提前熟悉多线程编码以及<strong>异步编码</strong>(如<code>Future</code>),其中异步编码建议多练习,在RocketMQ和Dledger中使用较多,如果未能掌握该知识点,则会较难理解整体的流程</p></li><li><p>需要提前学习网络编程相关知识,最少也要熟悉Socket NIO编码,未开启DLedger时的主从同步主要依靠 NIO解决</p></li><li><p>关键点:NameServer路由管理、消息发送(客户端、Broker端)、消息存储(刷盘、主从同步)、消息消费(客户端、Broker端)、ACL控制、消息轨迹、主从切换(DLedger)</p></li><li><p>下方思维导图仅可用作个人回顾,无法作为学习材料(不成系统,仅仅是记录了源码阅读总结,其中DLedger部分的流程图主要参考书中对应流程图)</p></li><li><p>发现源码阅读不适合弄思维导图,流程图和时序图时非常合适作为源码阅读的总结,以后不再把代码的细节放到思维导图里了,以后估计是概述+一些关键点描述,然后加上流程图和时序图(本质上是最近几本书的阅读体验产生的总结,阅读源码有个大致的概念,知道大体的框架就可以开始阅读了,把源码的细节展示出来反而会增加理解难度)</p><span id="more"></span><h4 id="疑问集中营"><a href="#疑问集中营" class="headerlink" title="疑问集中营"></a>疑问集中营</h4></li><li><p>使用DLedger模式的MQ,集群状态:A(120)、B(120)、C(100)、D(90)、E(90),此时主节点是A,100-120之间的数据追加请求还在ACK确认中,如果此时节点A进入了选举状态,并且重新选举后,节点B当选Leader,那么因为之前的请求在节点A上,会导致超时返回失败,那么客户端必然会重试,在此中场景中如何保证数据不重复</p><figure class="highlight plaintext"><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">1. 主节点的ack逻辑是将存储实现的 committedIndex 更新为集群水位,从节点是每次数据追加则更新 committedIndex 为追加请求中的 commitIndex</span><br><span class="line">2. 节点当选Leader时,会发起 Compare,而发起请求中的 commitIndex 正是集群水位,此时可保证从节点不会有多余数据(即节点A会100)</span><br><span class="line">3. 疑问是此时的节点B,如何保证数据不会多余</span><br></pre></td></tr></table></figure></li><li><p>消息发送客户端发送消息后,在接受ACK时因网络问题未成功接收导致重试,RocketMQ如何保证不会出现重复消息?<br>看了RocketMQ的接收处理存储代码,发现并未做去重处理,如果出现此种情况,只能业务上做幂等的额外处理了。通过业务的id或者MQ的msgId。</p></li><li><p><img src="http://img.janwarlen.com/blog%2FRocketMQ.svg"></p></li></ol>]]></content>
<summary type="html"><h4 id="引用链接"><a href="#引用链接" class="headerlink" title="引用链接"></a>引用链接</h4><ol>
<li><a href="https://blog.csdn.net/qq_32099833/article/details/120247549">RocketMQ定时消息实现原理分析</a>:时序图</li>
<li><a href="https://developer.aliyun.com/article/827385">RocketMQ 实战(四) - 订阅机制和定时消息</a>:关于correctDeliverTimestamp的理解</li>
<li><a href="https://xie.infoq.cn/article/cd087fb325ac2ceb0bf0ccf2d">深入了解 RocketMQ 之过滤器</a>:BloomFilter</li>
<li><a href="https://blog.csdn.net/jiaomeng/article/details/1495500">Bloom Filter概念和原理</a>:BloomFilter</li>
<li><a href="https://rocketmq.apache.org/rocketmq/filter-messages-by-sql92-in-rocketmq/">Filter Messages By SQL92 In RocketMQ</a></li>
<li><a href="https://www.cnblogs.com/sunshine-2015/p/8998549.html">RocketMQ源码 — 七、 RocketMQ高可用(2)</a>:主从同步时序图</li>
</ol>
<h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol>
<li><p>本书存在较多的错误,主要问题是代码片段与文字描述的衔接对不上,并且存在少量的代码注释错误(可能是我看的代码分支与作者看的分支版本不同导致理解产生偏差),但是不能否认这本书的价值,尤其是一些关键地方的逻辑描述,能够帮助减少很多源码阅读理解的时间</p>
</li>
<li><p>建议以源码阅读为主,如果已经买了,可以同步对照书籍中的文字描述确认自己理解是否正确</p>
</li>
<li><p>个人已上传<a href="https://github.com/JanWarlen/rocketmq/tree/release-4.9.4">RocketMQ(release-4.9.4)</a>与<a href="https://github.com/JanWarlen/dledger/tree/0.2.6">DLedger(tag:dledger-0.2.6)</a>带注释版本,部分核心入口已在工程的 ReadME 文档中标注</p>
</li>
<li><p>建议提前熟悉多线程编码以及<strong>异步编码</strong>(如<code>Future</code>),其中异步编码建议多练习,在RocketMQ和Dledger中使用较多,如果未能掌握该知识点,则会较难理解整体的流程</p>
</li>
<li><p>需要提前学习网络编程相关知识,最少也要熟悉Socket NIO编码,未开启DLedger时的主从同步主要依靠 NIO解决</p>
</li>
<li><p>关键点:NameServer路由管理、消息发送(客户端、Broker端)、消息存储(刷盘、主从同步)、消息消费(客户端、Broker端)、ACL控制、消息轨迹、主从切换(DLedger)</p>
</li>
<li><p>下方思维导图仅可用作个人回顾,无法作为学习材料(不成系统,仅仅是记录了源码阅读总结,其中DLedger部分的流程图主要参考书中对应流程图)</p>
</li>
<li><p>发现源码阅读不适合弄思维导图,流程图和时序图时非常合适作为源码阅读的总结,以后不再把代码的细节放到思维导图里了,以后估计是概述+一些关键点描述,然后加上流程图和时序图(本质上是最近几本书的阅读体验产生的总结,阅读源码有个大致的概念,知道大体的框架就可以开始阅读了,把源码的细节展示出来反而会增加理解难度)</p></summary>
<category term="中间件" scheme="http://janwarlen.com/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/"/>
<category term="读书笔记" scheme="http://janwarlen.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<category term="RocketMQ" scheme="http://janwarlen.com/tags/RocketMQ/"/>
</entry>
<entry>
<title>一书一图-《Effective Java》第三版</title>
<link href="http://janwarlen.com/2022/07/09/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-%E3%80%8AEffective%20Java%E3%80%8B%E7%AC%AC%E4%B8%89%E7%89%88/"/>
<id>http://janwarlen.com/2022/07/09/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-%E3%80%8AEffective%20Java%E3%80%8B%E7%AC%AC%E4%B8%89%E7%89%88/</id>
<published>2022-07-09T09:26:32.000Z</published>
<updated>2022-09-02T02:49:26.933Z</updated>
<content type="html"><![CDATA[<h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol><li>不建议购买中文版,有些地方的翻译瑕疵会让你十分难受,让人很难继续读下去</li><li>这本书的内容算是经验总结,如果没有对应的编码经验的话,阅读也会十分难受,如果看不懂,可以先停下去敲一些相关的代码熟悉相关的类,或者跳过也可以,不用死磕在那</li><li>英文不好的同学可以去看<a href="https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual">Effective-Java-3rd-edition-Chinese-English</a>(如果觉得这种行为和盗版无异良心过不去的同学可以买本英文版/中文版的然后看这个)</li><li>英文还可以的同学建议购买原版书籍<a href="https://item.jd.com/12393866.html">Effective Java(第3版)(英文版)</a></li><li>不推荐购买并阅读中文翻译版本,痛,太痛了(半价买我都觉得亏)</li><li>思维导图就是目录+个人总结</li></ol><h4 id="引用博文"><a href="#引用博文" class="headerlink" title="引用博文"></a>引用博文</h4><ol><li><a href="https://www.cnblogs.com/alltime/p/6729295.html">依赖注入的简单理解</a></li><li><a href="https://kb.cnblogs.com/page/45266/4/">深度理解依赖注入</a></li><li><a href="https://openjdk.org/jeps/421">Deprecate Finalization for Removal</a></li><li><a href="https://www.jianshu.com/p/c2c54664d4c9?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation">静态内部类的使用场景</a></li><li><a href="https://mp.weixin.qq.com/s/KneomYX_7yQ78RzAmvIoHg">抽象泄漏(Leaky Abstractions)</a></li><li><a href="https://segmentfault.com/a/1190000016596774?utm_source=tag-newest">JAVA 8 函数式接口( java.util.function 详解)</a></li></ol><span id="more"></span><p><img src="http://img.janwarlen.com/blog/%E3%80%8AEffective%20Java%E3%80%8B%E7%AC%AC%E4%B8%89%E7%89%88.png" alt="《Effective Java》第三版"></p>]]></content>
<summary type="html"><h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol>
<li>不建议购买中文版,有些地方的翻译瑕疵会让你十分难受,让人很难继续读下去</li>
<li>这本书的内容算是经验总结,如果没有对应的编码经验的话,阅读也会十分难受,如果看不懂,可以先停下去敲一些相关的代码熟悉相关的类,或者跳过也可以,不用死磕在那</li>
<li>英文不好的同学可以去看<a href="https://github.com/clxering/Effective-Java-3rd-edition-Chinese-English-bilingual">Effective-Java-3rd-edition-Chinese-English</a>(如果觉得这种行为和盗版无异良心过不去的同学可以买本英文版&#x2F;中文版的然后看这个)</li>
<li>英文还可以的同学建议购买原版书籍<a href="https://item.jd.com/12393866.html">Effective Java(第3版)(英文版)</a></li>
<li>不推荐购买并阅读中文翻译版本,痛,太痛了(半价买我都觉得亏)</li>
<li>思维导图就是目录+个人总结</li>
</ol>
<h4 id="引用博文"><a href="#引用博文" class="headerlink" title="引用博文"></a>引用博文</h4><ol>
<li><a href="https://www.cnblogs.com/alltime/p/6729295.html">依赖注入的简单理解</a></li>
<li><a href="https://kb.cnblogs.com/page/45266/4/">深度理解依赖注入</a></li>
<li><a href="https://openjdk.org/jeps/421">Deprecate Finalization for Removal</a></li>
<li><a href="https://www.jianshu.com/p/c2c54664d4c9?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation">静态内部类的使用场景</a></li>
<li><a href="https://mp.weixin.qq.com/s/KneomYX_7yQ78RzAmvIoHg">抽象泄漏(Leaky Abstractions)</a></li>
<li><a href="https://segmentfault.com/a/1190000016596774?utm_source=tag-newest">JAVA 8 函数式接口( java.util.function 详解)</a></li>
</ol></summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="读书笔记" scheme="http://janwarlen.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<category term="Effective Java" scheme="http://janwarlen.com/tags/Effective-Java/"/>
</entry>
<entry>
<title>一书一图-Java多线程编程核心技术</title>
<link href="http://janwarlen.com/2022/06/30/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF/"/>
<id>http://janwarlen.com/2022/06/30/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF/</id>
<published>2022-06-30T02:32:55.000Z</published>
<updated>2022-09-02T02:49:26.850Z</updated>
<content type="html"><![CDATA[<h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol><li>本书难度比较简单,属于实践类,难度不高建议跟随书中代码敲一遍,有兴趣的可以点到源码看一看</li><li>正版定价129,建议京东活动时买入,半价还是可以的</li><li>本书大量的代码案例都是简单的Api使用,原理与源码讲解较少,深入理解需要自己看源码或者搜索相关知识点的博文</li><li>推荐入手,整本书如果代码都跟着敲一遍,预计有10-20个小时的工作量</li><li>下方的思维导图可以用作知识点大纲,有感兴趣的知识点可以额外单独搜索或者阅读对应书中章节细节内容</li><li>多线程相关实践代码可以参考我的<a href="https://github.com/JanWarlen/JavaPractice/tree/master/Basic/src/main/java/com/janwarlen/learn/thread">练习代码</a><span id="more"></span></li></ol><p><img src="http://img.janwarlen.com/blog/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF.png" alt="Java多线程编程核心技术"></p>]]></content>
<summary type="html"><h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol>
<li>本书难度比较简单,属于实践类,难度不高建议跟随书中代码敲一遍,有兴趣的可以点到源码看一看</li>
<li>正版定价129,建议京东活动时买入,半价还是可以的</li>
<li>本书大量的代码案例都是简单的Api使用,原理与源码讲解较少,深入理解需要自己看源码或者搜索相关知识点的博文</li>
<li>推荐入手,整本书如果代码都跟着敲一遍,预计有10-20个小时的工作量</li>
<li>下方的思维导图可以用作知识点大纲,有感兴趣的知识点可以额外单独搜索或者阅读对应书中章节细节内容</li>
<li>多线程相关实践代码可以参考我的<a href="https://github.com/JanWarlen/JavaPractice/tree/master/Basic/src/main/java/com/janwarlen/learn/thread">练习代码</a></summary>
<category term="多线程" scheme="http://janwarlen.com/categories/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
<category term="读书笔记" scheme="http://janwarlen.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<category term="多线程编程" scheme="http://janwarlen.com/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B/"/>
</entry>
<entry>
<title>一书一图-Java高并发与集合框架</title>
<link href="http://janwarlen.com/2022/06/26/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-Java%E9%AB%98%E5%B9%B6%E5%8F%91%E4%B8%8E%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6/"/>
<id>http://janwarlen.com/2022/06/26/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-Java%E9%AB%98%E5%B9%B6%E5%8F%91%E4%B8%8E%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6/</id>
<published>2022-06-26T02:46:21.000Z</published>
<updated>2022-09-02T02:49:26.876Z</updated>
<content type="html"><![CDATA[<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>书中主要内容是源码解读,因为是黑白印刷,阅读体验会有所影响</li><li>书中关于大顶堆/小顶堆/红黑树/管程(Object Monitor/AQS)建议单独拎出来阅读</li><li>并发部分对于没有足够编程经验的同学门槛会有一些高,建议同时搜索相关博文多方参考学习</li><li>因为是源码解读,建议读者先使用相关类做简单的编码demo,可以更容易理解解读内容(最好可以先自己阅读一遍源码),可以先阅读《Java多线程编程核心技术》再来看本书</li><li>因书中主要内容是源码解析,因此不建议作为系统化学习的教材使用,可以用作学习过程中的辅助教材</li><li>打折时推荐入手,原价不推荐</li><li>下方思维导图仅是个人的简单记录,可以用作知识点列表按图索骥的单独去学习,图中无任何具体知识内容<span id="more"></span></li></ol><p><img src="http://img.janwarlen.com/blog/Java%E9%AB%98%E5%B9%B6%E5%8F%91%E4%B8%8E%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6.png" alt="Java高并发与集合框架"></p>]]></content>
<summary type="html"><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>书中主要内容是源码解读,因为是黑白印刷,阅读体验会有所影响</li>
<li>书中关于大顶堆&#x2F;小顶堆&#x2F;红黑树&#x2F;管程(Object Monitor&#x2F;AQS)建议单独拎出来阅读</li>
<li>并发部分对于没有足够编程经验的同学门槛会有一些高,建议同时搜索相关博文多方参考学习</li>
<li>因为是源码解读,建议读者先使用相关类做简单的编码demo,可以更容易理解解读内容(最好可以先自己阅读一遍源码),可以先阅读《Java多线程编程核心技术》再来看本书</li>
<li>因书中主要内容是源码解析,因此不建议作为系统化学习的教材使用,可以用作学习过程中的辅助教材</li>
<li>打折时推荐入手,原价不推荐</li>
<li>下方思维导图仅是个人的简单记录,可以用作知识点列表按图索骥的单独去学习,图中无任何具体知识内容</summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="读书笔记" scheme="http://janwarlen.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>Java版本特性-JDK18</title>
<link href="http://janwarlen.com/2022/06/22/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK18/"/>
<id>http://janwarlen.com/2022/06/22/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK18/</id>
<published>2022-06-22T05:53:21.000Z</published>
<updated>2022-09-02T02:49:26.633Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://openjdk.org/projects/jdk/18/">jdk18特性列表</a></li><li><a href="https://www.jdon.com/60407">迁移到JDK18为何写一个空的finalize()方法?</a></li><li><a href="https://www.oracle.com/java/technologies/javase/seccodeguide.html">Java-SE 安全编码指南</a></li><li><a href="https://www.hboehm.info/misc_slides/java_finalizers.pdf">Finalization对GC的影响</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>反射核心重新实现,从API的维护成本和性能都有所提升</li><li>互联网地址解析器可自定义</li><li><code>switch</code>针对<code>case</code>覆盖范围和<code>sealed</code>类的检测进行了加强</li><li><code>Finalization</code>标注弃用,因目前仍默认启用,因此迁移成本暂时无变化,后期该特性正式删除后,迁移的成本估计需要仔细评估。<span id="more"></span></li></ol><h1 id="默认字符集为UTF-8"><a href="#默认字符集为UTF-8" class="headerlink" title="默认字符集为UTF-8"></a>默认字符集为UTF-8</h1><p> Java API 默认字符集为UTF-8,除了console I/O</p><h1 id="极简web服务器"><a href="#极简web服务器" class="headerlink" title="极简web服务器"></a>极简web服务器</h1><p> 从官方文档描述,功能基本与nginx重合。并且从<code>Motivation</code>中得知它的定位主要是校园内的教学场景。</p><h2 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h2><h3 id="代码demo"><a href="#代码demo" class="headerlink" title="代码demo"></a>代码demo</h3> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * snippet</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CodeSnippets</span> {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@snippet</span> :</span></span><br><span class="line"><span class="comment"> * CodeSnippets.func(); // <span class="doctag">@highlight</span> substring="func"</span></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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"CodeSnippets"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * {<span class="doctag">@snippet</span> file="CodeSnippets.java" region="test"}</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> text 打印内容</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func2</span><span class="params">(String text)</span> {</span><br><span class="line"> System.out.println(<span class="string">"CodeSnippets:func2:"</span> + text);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * for region test</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">func3</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// @start region="test"</span></span><br><span class="line"> CodeSnippets.func2(<span class="string">"hihihi"</span>); <span class="comment">// @replace regex='".*"' replacement="..."</span></span><br><span class="line"> <span class="comment">// @end</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="部分注释说明"><a href="#部分注释说明" class="headerlink" title="部分注释说明"></a>部分注释说明</h4><ul><li>@highlight substring=”func”<br>是将当前行的注释java代码中的func高亮</li><li>// @replace regex=’”.*”‘ replacement=”…”<br> 当前行在作为注释代码时,通过正则匹配,将“hihihi”替换为…</li></ul><h3 id="javadoc脚本"><a href="#javadoc脚本" class="headerlink" title="javadoc脚本"></a>javadoc脚本</h3> <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">javadoc -private -d .../Downloads/test --source-path ".../JavaFeature/JDK18/src/main/java:.../JavaFeature/JDK18/src/main/resources" ".../JavaFeature/JDK18/src/main/java/module-info.java" ".../JavaFeature/JDK18/src/main/java/com/janwarlen/feature/CodeSnippets.java" ".../JavaFeature/JDK18/src/main/java/com/janwarlen/Demo.java" --snippet-path ".../JavaFeature/JDK18/src/main/java/com/janwarlen/feature/"</span><br></pre></td></tr></table></figure><h4 id="参数说明"><a href="#参数说明" class="headerlink" title="参数说明"></a>参数说明</h4><ul><li>–source-path 指明有哪些路径需要参与本次生成</li><li>–snippet-path 标注在使用@snippet 的file文件范围</li><li>-d doc生成文件存放目录,文件较多,建议单独创建空目录</li><li>-private 扫描所有文件</li></ul><h3 id="结果简单展示"><a href="#结果简单展示" class="headerlink" title="结果简单展示"></a>结果简单展示</h3><p><img src="http://img.janwarlen.com/blog/Java-snippet-res.png"></p><h1 id="使用Method-Handles重新实现反射核心内容"><a href="#使用Method-Handles重新实现反射核心内容" class="headerlink" title="使用Method Handles重新实现反射核心内容"></a>使用Method Handles重新实现反射核心内容</h1><p>Method Handles 作为反射的底层机制,重新实现了 java.lang.reflect.Method、Constructor、Field 组件,将大大降低反射 API 的维护和开发成本。</p><blockquote><p>This benefits Project Loom by reducing the use of native stack frames.</p></blockquote><h1 id="Vector-API-第三次孵化"><a href="#Vector-API-第三次孵化" class="headerlink" title="Vector API (第三次孵化)"></a>Vector API (第三次孵化)</h1><p>变动内容:</p><ul><li>支持ARM 标量矢量扩展 (SVE) 平台。</li><li>在支持硬件掩码的架构上提高接受掩码的向量操作的性能。</li></ul><h1 id="互联网地址解析-SPI"><a href="#互联网地址解析-SPI" class="headerlink" title="互联网地址解析 SPI"></a>互联网地址解析 SPI</h1><p>为主机名和地址解析定义服务提供者接口 (SPI),以便java.net.InetAddress可以使用平台内置解析器以外的解析器。<br>在尝试自定义解析器的过程中发现一个难以解决的问题,<code>InetAddress</code>是通过<code>ServiceLoader.load(InetAddressResolverProvider.class)</code>去找所有实现的解析器,但是用户自定义的解析器所在的<code>module</code>不在<code>java.base</code>的<code>requires</code>/<code>uses</code>中,根据模块化的隔离,此时<code>java.base</code>是看不到自定义实现的解析器的。。。<br>原因在于如果类在声明了module的工程中,就跳过不load了。。。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">java.util.ServiceLoader.LazyClassPathLookupIterator#hasNextService</span><br><span class="line"> <span class="title function_">if</span> <span class="params">(clazz.getModule()</span>.isNamed()) {</span><br><span class="line"> <span class="comment">// ignore class if in named module</span></span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>当我删除<code>module-info.java</code>后,自定义的解析器就可以被识别了。</p><h2 id="案例-1"><a href="#案例-1" class="headerlink" title="案例"></a>案例</h2><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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"><span class="keyword">import</span> java.net.Inet4Address;</span><br><span class="line"><span class="keyword">import</span> java.net.InetAddress;</span><br><span class="line"><span class="keyword">import</span> java.net.UnknownHostException;</span><br><span class="line"><span class="keyword">import</span> java.net.spi.InetAddressResolver;</span><br><span class="line"><span class="keyword">import</span> java.util.Arrays;</span><br><span class="line"><span class="keyword">import</span> java.util.stream.Stream;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AddressResolver</span> <span class="keyword">implements</span> <span class="title class_">InetAddressResolver</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Stream<InetAddress> <span class="title function_">lookupByName</span><span class="params">(String host, LookupPolicy lookupPolicy)</span> <span class="keyword">throws</span> UnknownHostException {</span><br><span class="line"> <span class="comment">// 自定义根据hostname生成InetAddress流</span></span><br><span class="line"> <span class="comment">// 可查找DNS 或者本地的 /etc/hosts</span></span><br><span class="line"> System.out.println(<span class="string">"AddressResolver:lookupByName"</span>);</span><br><span class="line"> <span class="type">InetAddress</span> <span class="variable">localHost</span> <span class="operator">=</span> Inet4Address.getLoopbackAddress();</span><br><span class="line"> <span class="keyword">return</span> Arrays.stream(<span class="keyword">new</span> <span class="title class_">InetAddress</span>[]{localHost});</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">lookupByAddress</span><span class="params">(<span class="type">byte</span>[] addr)</span> <span class="keyword">throws</span> UnknownHostException {</span><br><span class="line"> <span class="comment">// 根据ip找hostname</span></span><br><span class="line"> System.out.println(<span class="string">"AddressResolver:lookupByAddress"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"null"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.net.spi.InetAddressResolver;</span><br><span class="line"><span class="keyword">import</span> java.net.spi.InetAddressResolverProvider;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResolverProvider</span> <span class="keyword">extends</span> <span class="title class_">InetAddressResolverProvider</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> {</span><br><span class="line"> System.out.println(<span class="string">"classLoader load class:ResolverProvider"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> InetAddressResolver <span class="title function_">get</span><span class="params">(Configuration configuration)</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AddressResolver</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">name</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"自定义"</span>;</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><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="type">InetAddress</span> <span class="variable">ids</span> <span class="operator">=</span> InetAddress.getByName(<span class="string">"null"</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (UnknownHostException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="输出结果"><a href="#输出结果" class="headerlink" title="输出结果"></a>输出结果</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">classLoader load class:ResolverProvider</span><br><span class="line">AddressResolver:lookupByName</span><br><span class="line">WARNING: Unknown <span class="keyword">module</span>: JDK18 specified to --add-<span class="keyword">exports</span></span><br></pre></td></tr></table></figure><h1 id="调用本地方法和操作堆外内存(第二次孵化)"><a href="#调用本地方法和操作堆外内存(第二次孵化)" class="headerlink" title="调用本地方法和操作堆外内存(第二次孵化)"></a>调用本地方法和操作堆外内存(第二次孵化)</h1><p>本次变更内容:</p><ul><li>内存访问var句柄中支持更多的载体,如booleanand MemoryAddress;</li><li>一个更通用的解引用 API,在MemorySegment和MemoryAddress接口中都可用;</li><li>一个更简单的 API 来获取下调用方法句柄,MethodType不再需要传递参数;</li><li>一个更简单的 API 来管理资源范围之间的时间依赖关系;和</li><li>用于将 Java 数组复制到内存段和从内存段复制的新 API。</li></ul><p>挖个坑,后续补上代码demo;</p><h1 id="switch匹配模式增强(第二次预调整)"><a href="#switch匹配模式增强(第二次预调整)" class="headerlink" title="switch匹配模式增强(第二次预调整)"></a>switch匹配模式增强(第二次预调整)</h1><p>变更内容:</p><ul><li>同类型的case必须所有case都有类型声明(我的理解应该产生了偏差),在该案例中,<code>CharSequence</code>的覆盖范围比<code>String</code>大,因此此处就是有问题的。该变动应该是增强了case之间的条件覆盖范围检查。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">error</span><span class="params">(Object o)</span> {</span><br><span class="line"> <span class="keyword">switch</span>(o) {</span><br><span class="line"> <span class="keyword">case</span> CharSequence cs -></span><br><span class="line"> System.out.println(<span class="string">"A sequence of length "</span> + cs.length());</span><br><span class="line"> <span class="keyword">case</span> String s -> <span class="comment">// Error - pattern is dominated by previous pattern</span></span><br><span class="line"> System.out.println(<span class="string">"A string: "</span> + s);</span><br><span class="line"> <span class="keyword">default</span> -> {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li>对<code>sealed</code>进行了拓展支持,当case中出现<code>sealed</code>相关的类时,会检测其他类是否都已覆盖<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">sealed</span> <span class="keyword">interface</span> <span class="title class_">S</span> permits A, B, C {}</span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">A</span> <span class="keyword">implements</span> <span class="title class_">S</span> {}</span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">B</span> <span class="keyword">implements</span> <span class="title class_">S</span> {}</span><br><span class="line"><span class="keyword">record</span> <span class="title class_">C</span><span class="params">(<span class="type">int</span> i)</span> <span class="keyword">implements</span> <span class="title class_">S</span> {} <span class="comment">// Implicitly final</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="title function_">testSealedExhaustive</span><span class="params">(S s)</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">switch</span> (s) {</span><br><span class="line"> <span class="keyword">case</span> A a -> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">case</span> B b -> <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">case</span> C c -> <span class="number">3</span>;</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">sealed</span> <span class="keyword">interface</span> <span class="title class_">I</span><T> permits A, B {}</span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">A</span><X> <span class="keyword">implements</span> <span class="title class_">I</span><String> {}</span><br><span class="line"><span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">B</span><Y> <span class="keyword">implements</span> <span class="title class_">I</span><Y> {}</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="title function_">testGenericSealedExhaustive</span><span class="params">(I<Integer> i)</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">switch</span> (i) {</span><br><span class="line"> <span class="comment">// Exhaustive as no A case possible! </span></span><br><span class="line"> <span class="keyword">case</span> B<Integer> bi -> <span class="number">42</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h1 id="弃用-Finalization"><a href="#弃用-Finalization" class="headerlink" title="弃用 Finalization"></a>弃用 Finalization</h1><p>可以先查看<a href="https://www.jdon.com/60407">迁移到JDK18为何写一个空的finalize()方法?</a><br>官方文档共4个角度决定放弃</p><ul><li>安全漏洞(资源泄漏可通过<code>try-with-resources</code> 和<code>Cleaners</code>方式避免)</li><li>性能</li><li>不可靠的执行</li><li>困难的编程模型(编码成本较高)</li></ul><p>目前仍默认启用,可通过<code>--finalization=disabled</code>禁用</p>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://openjdk.org/projects/jdk/18/">jdk18特性列表</a></li>
<li><a href="https://www.jdon.com/60407">迁移到JDK18为何写一个空的finalize()方法?</a></li>
<li><a href="https://www.oracle.com/java/technologies/javase/seccodeguide.html">Java-SE 安全编码指南</a></li>
<li><a href="https://www.hboehm.info/misc_slides/java_finalizers.pdf">Finalization对GC的影响</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>反射核心重新实现,从API的维护成本和性能都有所提升</li>
<li>互联网地址解析器可自定义</li>
<li><code>switch</code>针对<code>case</code>覆盖范围和<code>sealed</code>类的检测进行了加强</li>
<li><code>Finalization</code>标注弃用,因目前仍默认启用,因此迁移成本暂时无变化,后期该特性正式删除后,迁移的成本估计需要仔细评估。</summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK18" scheme="http://janwarlen.com/tags/JDK18/"/>
</entry>
<entry>
<title>Java版本特性-JDK17</title>
<link href="http://janwarlen.com/2022/06/22/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK17/"/>
<id>http://janwarlen.com/2022/06/22/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK17/</id>
<published>2022-06-21T21:23:33.000Z</published>
<updated>2022-09-02T02:49:26.602Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://openjdk.org/projects/jdk/17/">jdk17特性列表</a></li><li><a href="https://segmentfault.com/a/1190000041252650">Java 随机数相关 API 的演进与思考(上)</a></li><li><a href="https://segmentfault.com/a/1190000041262013">Java 随机数相关 API 的演进与思考(下)</a></li><li><a href="https://cloud.tencent.com/developer/article/1890732">支持上下文的序列化过滤器,又一次给序列化打补丁</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>浮点数计算将跨系统平台结果统一,不过float这个坑已经几乎所有人都知道,这个改动即便知道大部分也会遵循之前的安全习惯</li><li>switch的匹配模式增强虽然还没开放,但可以直观的看出对简化代码的帮助很大</li><li>密封类<code>sealed</code>将会让多态环境更好控制,避免代码的无序扩张</li><li>反序列化过滤器没有想到使用场景,但是从介绍到demo可以直观的感受到它的重要性,一个是安全性,一个是反序列化过程的可控性</li><li>调用本地方法和操作堆外内存是非常重要的点,只是平时接触和使用的机会太少,这个特性未来能够正式使用一定能绽放精彩<span id="more"></span></li></ol><h1 id="float跨平台值统一"><a href="#float跨平台值统一" class="headerlink" title="float跨平台值统一"></a>float跨平台值统一</h1><p>使浮点运算始终保持严格。所有的浮点数计算都遵循<code>strictfp</code><br>该特性令我比较疑惑。疑惑的点是这个遵循的是所有系统平台执行的结果一致还是没有了数据波动。</p><h2 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StrictFloat</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">float</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0.1231231f</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">0.23456d</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">c</span> <span class="operator">=</span> a + b;</span><br><span class="line"> System.out.println(c);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">strictfp</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func2</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">float</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0.1231231f</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">0.23456d</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">c</span> <span class="operator">=</span> a + b;</span><br><span class="line"> System.out.println(c);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// JDK8 中执行结果</span></span><br><span class="line"><span class="comment">// 0.3576831018900871</span></span><br><span class="line"><span class="comment">// 0.3576831018900871</span></span><br><span class="line"><span class="comment">// JDK17 执行 func 结果</span></span><br><span class="line"><span class="comment">// 0.3576831018900871</span></span><br></pre></td></tr></table></figure><h2 id="疑惑"><a href="#疑惑" class="headerlink" title="疑惑"></a>疑惑</h2><p>按照个人理解的<code>strictfp</code>,执行结果应该是 <code>0.3576831</code>。<br>但是依然有不稳定的波动值存在,因此只能理解为跨平台系统的执行结果统一了。。。。<br>上方案例代码在JDK8反编译汇编后,两个函数指令集一样,只有函数2有<code>strictfp</code>修饰符一个区别</p><h2 id="JDK17不需要使用strictfp"><a href="#JDK17不需要使用strictfp" class="headerlink" title="JDK17不需要使用strictfp"></a>JDK17不需要使用<code>strictfp</code></h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Modifier 'strictfp' is redundant on Java 17 and later</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">strictfp</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func2</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">float</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0.1231231f</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">0.23456d</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">c</span> <span class="operator">=</span> a + b;</span><br><span class="line"> System.out.println(c);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h1 id="增强PRNG(伪随机数生成器)"><a href="#增强PRNG(伪随机数生成器)" class="headerlink" title="增强PRNG(伪随机数生成器)"></a>增强PRNG(伪随机数生成器)</h1><p>提供新的接口类型和实现,包括可跳转 PRNG 和另一类可拆分 PRNG 算法 (LXM)。<br>具体详细内容和关于随机数的可以浏览博文<a href="https://segmentfault.com/a/1190000041252650">Java 随机数相关 API 的演进与思考(上)</a>/<a href="https://segmentfault.com/a/1190000041262013">Java 随机数相关 API 的演进与思考(下)</a>,博主写的非常详尽</p><h1 id="macOS新的渲染管道"><a href="#macOS新的渲染管道" class="headerlink" title="macOS新的渲染管道"></a>macOS新的渲染管道</h1><p>使用 Apple Metal API 为 macOS 实现 Java 2D 内部渲染管道,以替代使用已弃用的 Apple OpenGL API 的现有管道。</p><h1 id="系统平台架构兼容:-macOS-x2F-AArch64"><a href="#系统平台架构兼容:-macOS-x2F-AArch64" class="headerlink" title="系统平台架构兼容: macOS/AArch64"></a>系统平台架构兼容: macOS/AArch64</h1><h1 id="弃用-Applet-API"><a href="#弃用-Applet-API" class="headerlink" title="弃用 Applet API"></a>弃用 Applet API</h1><blockquote><p>It is essentially irrelevant since all web-browser vendors have either removed support for Java browser plug-ins or announced plans to do so.</p></blockquote><p>市面主流浏览器已经不再支持Java的浏览器插件,这个API已经没有作用了。</p><h1 id="JDK内部代码继续加强-强封装"><a href="#JDK内部代码继续加强-强封装" class="headerlink" title="JDK内部代码继续加强 强封装"></a>JDK内部代码继续加强 强封装</h1><p>除了部分<a href="https://openjdk.org/jeps/260#Description">关键代码</a>,对其他所有JDK内部的Java类内部元素进行强封装,以提高JDK的安全性和可维护性。</p><h1 id="switch模式匹配增强(预调整)"><a href="#switch模式匹配增强(预调整)" class="headerlink" title="switch模式匹配增强(预调整)"></a>switch模式匹配增强(预调整)</h1><p>因还未开放使用,因此使用官方文档案例说明:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> String <span class="title function_">formatterPatternSwitch</span><span class="params">(Object o)</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">switch</span> (o) {</span><br><span class="line"> <span class="keyword">case</span> <span class="literal">null</span> -> System.out.println(<span class="string">"Oops"</span>);</span><br><span class="line"> <span class="keyword">case</span> Integer i -> String.format(<span class="string">"int %d"</span>, i);</span><br><span class="line"> <span class="keyword">case</span> Long l -> String.format(<span class="string">"long %d"</span>, l);</span><br><span class="line"> <span class="keyword">case</span> Double d -> String.format(<span class="string">"double %f"</span>, d);</span><br><span class="line"> <span class="keyword">case</span> String s && (s.length() > <span class="number">100</span>) -> String.format(<span class="string">"String %s"</span>, s);</span><br><span class="line"> <span class="keyword">default</span> -> o.toString();</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>可跨类型匹配,带匹配对象无类型限制(从文档看起来是这样)</li><li>可匹配null</li><li>可在匹配条件添加判断条件</li></ul><h1 id="删除-RMI-Activation"><a href="#删除-RMI-Activation" class="headerlink" title="删除 RMI Activation"></a>删除 RMI Activation</h1><p> JDK15标注弃用,JDK17删除。<br>删除远程方法调用 (RMI) 激活机制,同时保留 RMI 的其余部分。</p><h1 id="密封类-开放使用"><a href="#密封类-开放使用" class="headerlink" title="密封类-开放使用"></a>密封类-开放使用</h1><p> 与JDK16相比,JDK17没有新增变更。</p><ul><li><code>sealed</code>修饰的类必须有子类</li><li><code>non-sealed</code>修饰的子类被其他类继承不受限制</li><li><code>sealed</code>修饰的类的子类在不同包时,所有类必须在同一个模块下(同一个<code>module-info.java</code>)</li></ul><h1 id="删除实验性-AOT-和-JIT-编译器"><a href="#删除实验性-AOT-和-JIT-编译器" class="headerlink" title="删除实验性 AOT 和 JIT 编译器"></a>删除实验性 AOT 和 JIT 编译器</h1><p>官方发现没啥人用,就给删了。(维护工作量大)<br>保留了实验性的JVMCI。</p><h1 id="弃用安全管理器(Security-Manager)"><a href="#弃用安全管理器(Security-Manager)" class="headerlink" title="弃用安全管理器(Security Manager)"></a>弃用安全管理器(Security Manager)</h1><p>根据官方文档的说法,安全管理器使用率较低,有一部分框架甚至避开JDK内置策略实现自定义安全管理器。加上官方认定的性能不佳/权限模型脆弱/编码困难,因此决定弃用。<br>但是没有提到替代方案,只是在未来工作列表中表示会在多个方面增强安全性。</p><h1 id="本地方法调用和操作堆外内存-API(孵化器)"><a href="#本地方法调用和操作堆外内存-API(孵化器)" class="headerlink" title="本地方法调用和操作堆外内存 API(孵化器)"></a>本地方法调用和操作堆外内存 API(孵化器)</h1><p>JDK14/15/16已经进行过孵化,这次是在之前基础之上的演变。<br>官方文档没说和之前相比的变动内容,挖个坑,等以后填。。。。。</p><h1 id="Vector-API(第二次孵化器)"><a href="#Vector-API(第二次孵化器)" class="headerlink" title="Vector API(第二次孵化器)"></a>Vector API(第二次孵化器)</h1><ul><li>增强 API 以支持字符操作,例如 UTF-8 字符解码。short具体来说,我们添加了用于在向量和数组之间复制字符的方法char,以及用于与整数向量进行无符号比较的新向量比较运算符。</li><li>byte用于将向量转换为数组和从boolean 数组转换为 API 的增强功能。</li><li>使用英特尔的短向量数学库 (SVML)对 x64 上的超越和三角通道运算的内在支持。</li><li>对 Intel x64 和 ARM NEON 实施的一般性能增强。</li></ul><h1 id="特定于上下文的反序列化过滤器"><a href="#特定于上下文的反序列化过滤器" class="headerlink" title="特定于上下文的反序列化过滤器"></a>特定于上下文的反序列化过滤器</h1><p>结合官方文档和<a href="https://cloud.tencent.com/developer/article/1890732">支持上下文的序列化过滤器,又一次给序列化打补丁</a>大致明白了这个是干啥的。<br>简单的总结就是在所有使用<code>ObjectInputStream</code>的时候,都会经过反序列化过滤器,如果不符合所有过滤器,则无法反序列化。</p><h2 id="案例-1"><a href="#案例-1" class="headerlink" title="案例"></a>案例</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DeserializationFilter</span> {</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"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 该过滤器将只允许java.base路径的类反序列化,其他所有反序列化操作均拒绝</span></span><br><span class="line"> <span class="type">ObjectInputFilter</span> <span class="variable">filter</span> <span class="operator">=</span> ObjectInputFilter.Config.createFilter(<span class="string">"java.base/*;!*"</span>);</span><br><span class="line"> <span class="type">ObjectInputFilter</span> <span class="variable">serialFilter</span> <span class="operator">=</span> ObjectInputFilter.Config.getSerialFilter();</span><br><span class="line"> ObjectInputFilter.Config.setSerialFilter(filter);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">serial</span><span class="params">(Object obj)</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="type">FileOutputStream</span> <span class="variable">fileOutputStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileOutputStream</span>(<span class="string">"test"</span>);</span><br><span class="line"> <span class="type">ObjectOutputStream</span> <span class="variable">objectOutputStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(fileOutputStream);</span><br><span class="line"> objectOutputStream.writeObject(obj);</span><br><span class="line"> objectOutputStream.close();</span><br><span class="line"> fileOutputStream.close();</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">deserial</span> <span class="params">()</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="type">FileInputStream</span> <span class="variable">fileInputStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FileInputStream</span>(<span class="string">"test"</span>);</span><br><span class="line"> <span class="type">ObjectInputStream</span> <span class="variable">objectInputStream</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectInputStream</span>(fileInputStream);</span><br><span class="line"> <span class="type">Object</span> <span class="variable">o</span> <span class="operator">=</span> objectInputStream.readObject();</span><br><span class="line"> <span class="keyword">if</span> (o <span class="keyword">instanceof</span> Shape s) {</span><br><span class="line"> System.out.println(<span class="string">"deserial:"</span>);</span><br><span class="line"> s.func();</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (IOException | ClassNotFoundException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="不设置过滤器"><a href="#不设置过滤器" class="headerlink" title="不设置过滤器"></a>不设置过滤器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="comment">// DeserializationFilter.func();</span></span><br><span class="line"> DeserializationFilter.serial(cirle);</span><br><span class="line"> DeserializationFilter.deserial();</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 反序列化正常</span></span><br><span class="line"><span class="comment">// deserial:</span></span><br><span class="line"><span class="comment">// com.janwarlen.feature.Circle</span></span><br></pre></td></tr></table></figure><h3 id="设置过滤器"><a href="#设置过滤器" class="headerlink" title="设置过滤器"></a>设置过滤器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> DeserializationFilter.func();</span><br><span class="line"> DeserializationFilter.serial(cirle);</span><br><span class="line"> DeserializationFilter.deserial();</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 反序列化失败</span></span><br><span class="line">Exception in thread <span class="string">"main"</span> java.lang.RuntimeException: java.io.InvalidClassException: filter status: REJECTED</span><br><span class="line">at JDK17/com.janwarlen.feature.DeserializationFilter.deserial(DeserializationFilter.java:<span class="number">38</span>)</span><br><span class="line">at JDK17/com.janwarlen.Demo.main(Demo.java:<span class="number">26</span>)</span><br><span class="line">Caused by: java.io.InvalidClassException: filter status: REJECTED</span><br><span class="line">at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:<span class="number">1414</span>)</span><br><span class="line">at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:<span class="number">2055</span>)</span><br><span class="line">at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:<span class="number">1909</span>)</span><br><span class="line">at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:<span class="number">2235</span>)</span><br><span class="line">at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:<span class="number">1744</span>)</span><br><span class="line">at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:<span class="number">514</span>)</span><br><span class="line">at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:<span class="number">472</span>)</span><br><span class="line">at JDK17/com.janwarlen.feature.DeserializationFilter.deserial(DeserializationFilter.java:<span class="number">32</span>)</span><br><span class="line">... <span class="number">1</span> more</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://openjdk.org/projects/jdk/17/">jdk17特性列表</a></li>
<li><a href="https://segmentfault.com/a/1190000041252650">Java 随机数相关 API 的演进与思考(上)</a></li>
<li><a href="https://segmentfault.com/a/1190000041262013">Java 随机数相关 API 的演进与思考(下)</a></li>
<li><a href="https://cloud.tencent.com/developer/article/1890732">支持上下文的序列化过滤器,又一次给序列化打补丁</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>浮点数计算将跨系统平台结果统一,不过float这个坑已经几乎所有人都知道,这个改动即便知道大部分也会遵循之前的安全习惯</li>
<li>switch的匹配模式增强虽然还没开放,但可以直观的看出对简化代码的帮助很大</li>
<li>密封类<code>sealed</code>将会让多态环境更好控制,避免代码的无序扩张</li>
<li>反序列化过滤器没有想到使用场景,但是从介绍到demo可以直观的感受到它的重要性,一个是安全性,一个是反序列化过程的可控性</li>
<li>调用本地方法和操作堆外内存是非常重要的点,只是平时接触和使用的机会太少,这个特性未来能够正式使用一定能绽放精彩</summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK17" scheme="http://janwarlen.com/tags/JDK17/"/>
</entry>
<entry>
<title>Java版本特性-JDK16</title>
<link href="http://janwarlen.com/2022/06/21/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK16/"/>
<id>http://janwarlen.com/2022/06/21/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK16/</id>
<published>2022-06-21T08:12:33.000Z</published>
<updated>2022-09-02T02:49:26.576Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://openjdk.org/projects/jdk/16/">jdk16特性列表</a></li><li><a href="https://cr.openjdk.java.net/~stuefe/JEP-Improve-Metaspace-Allocator/review-guide/review-guide-1.0.html">JEP-Improve-Metaspace-Allocator</a></li><li><a href="https://my.oschina.net/benhaile/blog/214159">元空间(MetaSpace)一种新的内存空间诞生</a></li><li><a href="https://cloud.tencent.com/developer/article/1933656">Java 16 中对于 Project Valhalla 的铺垫</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li><code>record</code>的开放使用对于一些简单的数据建模场景非常友好,能简化很多重复的编码工作</li><li><code>instanceof</code>正式放出,可以简化代码,提高代码可读性<span id="more"></span></li></ol><h1 id="矢量API"><a href="#矢量API" class="headerlink" title="矢量API"></a>矢量API</h1><p>经历了一顿能干三顿饭的四年,已经不知道矢量计算的相关公示和理论了。有需求的可以看官方给出的案例简单了解下。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">scalarComputation</span><span class="params">(<span class="type">float</span>[] a, <span class="type">float</span>[] b, <span class="type">float</span>[] c)</span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < a.length; i++) {</span><br><span class="line"> c[i] = (a[i] * a[i] + b[i] * b[i]) * -<span class="number">1.0f</span>;</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><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 class="keyword">static</span> <span class="keyword">final</span> VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;</span><br><span class="line"></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">vectorComputation</span><span class="params">(<span class="type">float</span>[] a, <span class="type">float</span>[] b, <span class="type">float</span>[] c)</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < a.length; i += SPECIES.length()) {</span><br><span class="line"> <span class="type">var</span> <span class="variable">m</span> <span class="operator">=</span> SPECIES.indexInRange(i, a.length);</span><br><span class="line"><span class="comment">// FloatVector va, vb, vc;</span></span><br><span class="line"> <span class="type">var</span> <span class="variable">va</span> <span class="operator">=</span> FloatVector.fromArray(SPECIES, a, i, m);</span><br><span class="line"> <span class="type">var</span> <span class="variable">vb</span> <span class="operator">=</span> FloatVector.fromArray(SPECIES, b, i, m);</span><br><span class="line"> <span class="type">var</span> <span class="variable">vc</span> <span class="operator">=</span> va.mul(va).</span><br><span class="line"> add(vb.mul(vb)).</span><br><span class="line"> neg();</span><br><span class="line"> vc.intoArray(c, i, m);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="可使用-C-14"><a href="#可使用-C-14" class="headerlink" title="可使用 C++14"></a>可使用 C++14</h1><p>Java有相当一部分代码底层实现是通过调用本地C++库实现,并且也支持开发人员自定义C++本地实现调用,本次版本是升级了C++的支持范围。</p><h1 id="JDK-源码迁移至git"><a href="#JDK-源码迁移至git" class="headerlink" title="JDK 源码迁移至git"></a>JDK 源码迁移至git</h1><p><a href="https://github.com/openjdk/">https://github.com/openjdk/</a></p><h1 id="ZGC增强:并发线程堆栈处理"><a href="#ZGC增强:并发线程堆栈处理" class="headerlink" title="ZGC增强:并发线程堆栈处理"></a>ZGC增强:并发线程堆栈处理</h1><p>将 ZGC 线程堆栈处理从安全点移至并发阶段。减少ZGC在safepoint上的暂停时间,官方文档说是基本控制在1ms内。</p><h1 id="Unix-Domain-Socket-Channels"><a href="#Unix-Domain-Socket-Channels" class="headerlink" title="Unix-Domain Socket Channels"></a>Unix-Domain Socket Channels</h1><p>对于本地、进程间通信,Unix 域套接字比 TCP/IP 环回连接更安全、更高效。</p><ul><li>Unix 域套接字严格用于同一系统上的进程之间的通信。不打算接受远程连接的应用程序可以通过使用 Unix 域套接字来提高安全性。</li><li>Unix 域套接字进一步受到操作系统强制、基于文件系统的访问控制的保护。</li><li>与 TCP/IP 环回连接相比,Unix 域套接字具有更快的设置时间和更高的数据吞吐量。</li><li>对于需要在同一系统上的容器之间进行通信的容器环境,Unix 域套接字可能是比 TCP/IP 套接字更好的解决方案。这可以使用位于共享卷中的套接字来实现。<br>Unix 域套接字长期以来一直是大多数 Unix 平台的一项功能,现在在 Windows 10 和 Windows Server 2019 中得到支持</li></ul><h1 id="兼容新系统:Alpine-Linux"><a href="#兼容新系统:Alpine-Linux" class="headerlink" title="兼容新系统:Alpine Linux"></a>兼容新系统:Alpine Linux</h1><p>以及其他使用 musl 作为主要 C 库的 Linux 发行版,机器是x64和AArch64架构的</p><h1 id="弹性元空间"><a href="#弹性元空间" class="headerlink" title="弹性元空间"></a>弹性元空间</h1><p>更及时地将未使用的 HotSpot 类元数据(即元空间)内存返回给操作系统,减少元空间占用,并简化元空间代码以降低维护成本。<br><code>-XX:MetaspaceReclaimPolicy=(balanced|aggressive|none)</code></p><ul><li>balanced:大多数应用程序应该看到元空间内存占用有所改善,而内存回收的负面影响应该是微不足道的。此模式是默认模式,旨在向后兼容。</li><li>‘aggressive’:以增加虚拟内存碎片为代价提供更高的内存回收率。</li><li>‘none’:完全禁用内存回收。<br>新算法说明文档:<a href="https://cr.openjdk.java.net/~stuefe/JEP-Improve-Metaspace-Allocator/review-guide/review-guide-1.0.html">https://cr.openjdk.java.net/~stuefe/JEP-Improve-Metaspace-Allocator/review-guide/review-guide-1.0.html</a></li></ul><h1 id="兼容Windows-x2F-AArch64"><a href="#兼容Windows-x2F-AArch64" class="headerlink" title="兼容Windows/AArch64"></a>兼容Windows/AArch64</h1><p>AArch64架构的兼容越来越多了,这次针对Windows的兼容应该是针对桌面软件领域。</p><h1 id="开放新API调用本地代码"><a href="#开放新API调用本地代码" class="headerlink" title="开放新API调用本地代码"></a>开放新API调用本地代码</h1><p>还在孵化阶段,目标应该是替换JNI,提供一种更高校更安全的调用本地方法的方式。</p><h1 id="针对值类型的告警信息"><a href="#针对值类型的告警信息" class="headerlink" title="针对值类型的告警信息"></a>针对值类型的告警信息</h1><p>可以先浏览<a href="https://cloud.tencent.com/developer/article/1933656">Java 16 中对于 Project Valhalla 的铺垫</a>,这篇文章对于值类型讲解和demo展示比较到位。</p><h1 id="新的打包工具(正式开放使用)"><a href="#新的打包工具(正式开放使用)" class="headerlink" title="新的打包工具(正式开放使用)"></a>新的打包工具(正式开放使用)</h1><p>JDK14开始孵化的打包工具,用以给各个系统打包各自平台的应用包:exe/pkg/dmg/deb/rpm</p><h1 id="外部内存访问-API(第三次孵化)"><a href="#外部内存访问-API(第三次孵化)" class="headerlink" title="外部内存访问 API(第三次孵化)"></a>外部内存访问 API(第三次孵化)</h1><ul><li><code>MemorySegment</code> 和 <code>MemoryAddress</code>之间的职责区别更清晰</li><li>提供新的接口<code>MemoryAccess</code>,在简单的使用场景降低<code>VarHandle API</code>的使用需求,即简化普通场景的使用</li><li>支持 <code>shared segments</code>(多线程场景)</li><li>可以通过<code>Cleaner</code>注册<code>segments</code>(API 保证段的清理操作最多会被调用一次——要么显式地(通过客户端代码),要么隐式地(通过清理器)。)<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">MemorySegment</span> <span class="variable">segment</span> <span class="operator">=</span> MemorySegment.allocateNative(<span class="number">100</span>);</span><br><span class="line"><span class="type">Cleaner</span> <span class="variable">cleaner</span> <span class="operator">=</span> Cleaner.create();</span><br><span class="line">segment.registerCleaner(cleaner);</span><br><span class="line"><span class="comment">// do some work</span></span><br><span class="line">segment = <span class="literal">null</span>; <span class="comment">// Cleaner might reclaim the segment memory now</span></span><br></pre></td></tr></table></figure></li></ul><h1 id="instanceof增强开放使用"><a href="#instanceof增强开放使用" class="headerlink" title="instanceof增强开放使用"></a><code>instanceof</code>增强开放使用</h1><p>需要注意变量的声明,避免重复,重复不会影响使用,会影响代码可读性。</p><h2 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Model</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Model</span><span class="params">(String name)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> name;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Instanceof</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">Model</span> <span class="variable">m</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Model</span>(<span class="string">"private"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func</span><span class="params">(Object obj)</span> {</span><br><span class="line"> <span class="keyword">if</span> (obj <span class="keyword">instanceof</span> String s) {</span><br><span class="line"> System.out.println(s);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (obj <span class="keyword">instanceof</span> Model m) {</span><br><span class="line"> System.out.println(<span class="string">"instanceof:"</span> + m.getName());</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"out:instanceof:"</span> + m.getName());</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> Instanceof.func(<span class="string">"sedg3"</span>);</span><br><span class="line"> Instanceof.func(<span class="keyword">new</span> <span class="title class_">Model</span>(<span class="string">"demo"</span>));</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></pre></td><td class="code"><pre><span class="line">sedg3</span><br><span class="line">out:<span class="keyword">instanceof</span>:<span class="keyword">private</span></span><br><span class="line"><span class="keyword">instanceof</span>:demo</span><br><span class="line">out:<span class="keyword">instanceof</span>:<span class="keyword">private</span></span><br></pre></td></tr></table></figure><h1 id="record-开放使用"><a href="#record-开放使用" class="headerlink" title="record 开放使用"></a>record 开放使用</h1><p>经过JDK14与JDK15的两次调整,在JDK16中开放使用,对于只需要数据建模的场景比较有用,需要额外注意record的限制规则:</p><ul><li>无法继承其他类,与<code>Enum</code>类似,父类限制为<code>java.lang.Record</code></li><li><code>record</code>修饰的类自动包含<code>final</code>规则限制</li><li>类的内部属性默认都是<code>final</code>的</li><li><code>record</code>修饰的类不能在内部显示声明变量<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"> <span class="keyword">public</span> <span class="keyword">record</span> <span class="title class_">RecordPoint</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span> {</span><br><span class="line"><span class="comment">// Instance field is not allowed in record</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> z;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li><li>不能声明<code>native</code>方法</li></ul><h1 id="强封装JDK"><a href="#强封装JDK" class="headerlink" title="强封装JDK"></a>强封装JDK</h1><p>建议开发人员通过标准API使用JDK内部类的内部元素,而不是直接使用。<br>提高JDK的安全性和可维护性。<br>影响的类涉足领域较多,如果迁移版本后编译失败,可以通过<a href="https://openjdk.org/jeps/396">JEP-396</a>查看替代方案</p><h1 id="密封类(第二次预调整)"><a href="#密封类(第二次预调整)" class="headerlink" title="密封类(第二次预调整)"></a>密封类(第二次预调整)</h1><p>添加了<code>non-sealed</code>,当<code>sealed</code>类的子类使用<code>non-sealed</code>修饰时,该子类可以被其他类继承。</p>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://openjdk.org/projects/jdk/16/">jdk16特性列表</a></li>
<li><a href="https://cr.openjdk.java.net/~stuefe/JEP-Improve-Metaspace-Allocator/review-guide/review-guide-1.0.html">JEP-Improve-Metaspace-Allocator</a></li>
<li><a href="https://my.oschina.net/benhaile/blog/214159">元空间(MetaSpace)一种新的内存空间诞生</a></li>
<li><a href="https://cloud.tencent.com/developer/article/1933656">Java 16 中对于 Project Valhalla 的铺垫</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li><code>record</code>的开放使用对于一些简单的数据建模场景非常友好,能简化很多重复的编码工作</li>
<li><code>instanceof</code>正式放出,可以简化代码,提高代码可读性</summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK16" scheme="http://janwarlen.com/tags/JDK16/"/>
</entry>
<entry>
<title>Java版本特性-JDK15</title>
<link href="http://janwarlen.com/2022/06/21/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK15/"/>
<id>http://janwarlen.com/2022/06/21/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK15/</id>
<published>2022-06-20T18:04:11.000Z</published>
<updated>2022-09-02T03:30:53.812Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://openjdk.org/projects/jdk/15/">jdk15特性列表</a></li><li><a href="https://blog.csdn.net/JAVA88866/article/details/124673213">隐藏类使用案例</a></li><li><a href="http://c.biancheng.net/view/1203.html">UDP通信:DatagramSocket类和DatagramPacket类</a></li><li><a href="http://t.zoukankan.com/yif0118-p-15315745.html">Java17新特性初探</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>ZGC和Shenandoah 的可正式投产比较重要</li><li>隐藏类的使用场景不多,但是对于agent和平台框架开发人员来说比较重要</li><li>文本块的正式开放使用,喜大普奔,相较于以前的大文本操作用起来是真的舒服很多<span id="more"></span></li></ol><h1 id="新签名方案:EdDSA-爱德华兹曲线数字签名算法"><a href="#新签名方案:EdDSA-爱德华兹曲线数字签名算法" class="headerlink" title="新签名方案:EdDSA(爱德华兹曲线数字签名算法)"></a>新签名方案:EdDSA(爱德华兹曲线数字签名算法)</h1><p>新增的签名方案,相比于现有的其他签名方案,它更快并且不降低安全性。可在TLS 1.3 中使用。</p><h1 id="密封类-预调整"><a href="#密封类-预调整" class="headerlink" title="密封类(预调整)"></a>密封类(预调整)</h1><p>使用<code>sealed</code>修饰类后,通过<code>permits</code>限制可以继承当前类的子类,避免无限制的继承扩张。<br>接口也是同样的使用规则。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">sealed</span> <span class="keyword">class</span> <span class="title class_">Shape</span> </span><br><span class="line"> permits com.example.polar.Circle,</span><br><span class="line"> com.example.quad.Rectangle,</span><br><span class="line"> com.example.quad.simple.Square {...}</span><br></pre></td></tr></table></figure><p>子类必须是 <code>final</code> 或者 <code>sealed</code></p><h1 id="隐藏类"><a href="#隐藏类" class="headerlink" title="隐藏类"></a>隐藏类</h1><p>官方文档看的比较晦涩,可以浏览<a href="https://blog.csdn.net/JAVA88866/article/details/124673213">隐藏类使用案例</a>进行初步的了解,该特性对于平台、框架、agent开发人员比较友好,正常的业务场景不会使用到该特性。</p><h1 id="删除Nashorn-JavaScript-引擎"><a href="#删除Nashorn-JavaScript-引擎" class="headerlink" title="删除Nashorn JavaScript 引擎"></a>删除Nashorn JavaScript 引擎</h1><p>删除 Nashorn JavaScript 脚本引擎和 API 以及该jjs 工具。JDK11标注弃用,JDK15删除。</p><h1 id="重新实现-DatagramSocket-API"><a href="#重新实现-DatagramSocket-API" class="headerlink" title="重新实现 DatagramSocket API"></a>重新实现 DatagramSocket API</h1><p>用易于维护和调试的更简单、更现代的实现替换java.net.DatagramSocket和API的底层实现。<br>笔者未使用过,因此通过其他博主分享的文章了解<code>DatagramSocket</code>是UDP通信的相关类。网络编程相关需要重点关注下。</p><h1 id="弃用和禁用偏向锁"><a href="#弃用和禁用偏向锁" class="headerlink" title="弃用和禁用偏向锁"></a>弃用和禁用偏向锁</h1><p>根据官方文档的说明</p><ol><li>应用不会通过偏向锁获得明显的性能优化</li><li>偏向锁的维护成本较高,并且侵入了其他的HotSpot组件<br>因此,官方决定禁用并且弃用偏向锁。<br>实际的编码中,不会直接涉及偏向锁,主要是JVM内部实现涉及。</li></ol><h1 id="instanceof增强-第二次预调整"><a href="#instanceof增强-第二次预调整" class="headerlink" title="instanceof增强(第二次预调整)"></a>instanceof增强(第二次预调整)</h1><blockquote><p>This JEP proposes to re-preview the feature in JDK 15, with no changes relative to the preview in JDK 14, in order to gather additional feedback.</p></blockquote><p>没变动,纯粹属于提个醒,想收集更多的反馈…..</p><h1 id="ZGC可正式投产"><a href="#ZGC可正式投产" class="headerlink" title="ZGC可正式投产"></a>ZGC可正式投产</h1><p>默认仍是G1,通过启动参数<code>-XX:+UnlockExperimentalVMOptions -XX:+UseZGC</code>启用</p><h1 id="文本块"><a href="#文本块" class="headerlink" title="文本块"></a>文本块</h1><p>无新增调整,正式开放使用。</p><h1 id="Shenandoah-可正式投产"><a href="#Shenandoah-可正式投产" class="headerlink" title="Shenandoah 可正式投产"></a>Shenandoah 可正式投产</h1><p>默认仍是G1,通过启动参数<code>-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC</code>启用。<br>本次无新增功能,并且Red Hat 8u与Red Hat 11u同步可正式投产使用。</p><h1 id="删除-Solaris-和-SPARC-端口"><a href="#删除-Solaris-和-SPARC-端口" class="headerlink" title="删除 Solaris 和 SPARC 端口"></a>删除 Solaris 和 SPARC 端口</h1><p>JDK14标记弃用,本次迭代彻底删除。</p><ul><li>删除所有特定于 Solaris 操作系统的源代码</li><li>删除特定于 SPARC 体系结构的所有源代码</li></ul><h1 id="外部内存访问-API(更新调整)"><a href="#外部内存访问-API(更新调整)" class="headerlink" title="外部内存访问 API(更新调整)"></a>外部内存访问 API(更新调整)</h1><p>本次更新内容:</p><ul><li>丰富的VarHandle组合器 API,用于自定义内存访问 var 句柄;</li><li>通过接口有针对性地支持内存段的并行处理Spliterator;</li><li>增强了对映射内存段的支持(例如,MappedMemorySegment::force);</li><li>支持串行限制的安全 API 点(例如,在两个线程之间转移线程所有权);和</li><li>不安全的 API 指向操作和取消引用来自例如本地调用的地址,或者将这些地址包装到合成内存段中</li></ul><p>因为使用经验较少,对于更新内容无法作出总结…</p><h1 id="新的数据建模方式-Records-第二次预调整"><a href="#新的数据建模方式-Records-第二次预调整" class="headerlink" title="新的数据建模方式 Records(第二次预调整)"></a>新的数据建模方式 Records(第二次预调整)</h1><ul><li>适配了<code>sealed</code><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.expression;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">sealed</span> <span class="keyword">interface</span> <span class="title class_">Expr</span></span><br><span class="line"> permits ConstantExpr, PlusExpr, TimesExpr, NegExpr {...}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">record</span> <span class="title class_">ConstantExpr</span><span class="params">(<span class="type">int</span> i)</span> <span class="keyword">implements</span> <span class="title class_">Expr</span> {...}</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">record</span> <span class="title class_">PlusExpr</span><span class="params">(Expr a, Expr b)</span> <span class="keyword">implements</span> <span class="title class_">Expr</span> {...}</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">record</span> <span class="title class_">TimesExpr</span><span class="params">(Expr a, Expr b)</span> <span class="keyword">implements</span> <span class="title class_">Expr</span> {...}</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">record</span> <span class="title class_">NegExpr</span><span class="params">(Expr e)</span> <span class="keyword">implements</span> <span class="title class_">Expr</span> {...}</span><br></pre></td></tr></table></figure></li><li>可局部数据建模<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">List<Merchant> <span class="title function_">findTopMerchants</span><span class="params">(List<Merchant> merchants, <span class="type">int</span> month)</span> {</span><br><span class="line"> <span class="comment">// Local record</span></span><br><span class="line"> <span class="keyword">record</span> <span class="title class_">MerchantSales</span><span class="params">(Merchant merchant, <span class="type">double</span> sales)</span> {}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> merchants.stream()</span><br><span class="line"> .map(merchant -> <span class="keyword">new</span> <span class="title class_">MerchantSales</span>(merchant, computeSales(merchant, month)))</span><br><span class="line"> .sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))</span><br><span class="line"> .map(MerchantSales::merchant)</span><br><span class="line"> .collect(toList());</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h1 id="弃用-RMI-Remote-Method-Invocation-Activation"><a href="#弃用-RMI-Remote-Method-Invocation-Activation" class="headerlink" title="弃用 RMI(Remote Method Invocation) Activation"></a>弃用 RMI(Remote Method Invocation) Activation</h1><blockquote><p>RMI 即 Remote Method Invocation ,其为 Java 提供了远程方法调用的能力,不过,比较鸡肋,基本不会用到。RMI Activation 为激活分布式对象提供支持,不过,一般也不会用到。</p></blockquote>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://openjdk.org/projects/jdk/15/">jdk15特性列表</a></li>
<li><a href="https://blog.csdn.net/JAVA88866/article/details/124673213">隐藏类使用案例</a></li>
<li><a href="http://c.biancheng.net/view/1203.html">UDP通信:DatagramSocket类和DatagramPacket类</a></li>
<li><a href="http://t.zoukankan.com/yif0118-p-15315745.html">Java17新特性初探</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>ZGC和Shenandoah 的可正式投产比较重要</li>
<li>隐藏类的使用场景不多,但是对于agent和平台框架开发人员来说比较重要</li>
<li>文本块的正式开放使用,喜大普奔,相较于以前的大文本操作用起来是真的舒服很多</summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK15" scheme="http://janwarlen.com/tags/JDK15/"/>
</entry>
<entry>
<title>Java版本特性-JDK14</title>
<link href="http://janwarlen.com/2022/06/20/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK14/"/>
<id>http://janwarlen.com/2022/06/20/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK14/</id>
<published>2022-06-20T12:22:33.000Z</published>
<updated>2022-09-02T02:49:26.526Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://openjdk.org/projects/jdk/14/">jdk14特性列表</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>switch已经可以正式使用,用起来很爽</li><li>官方开放了API可以访问堆外的外部内存,这个特性看起来在大数据领域可以发挥很大的作用</li><li>空指针异常信息的细节增强非常方便开发人员的本地开发过程,大大的简化了本地调试的工作<span id="more"></span></li></ol><h1 id="instanceof增强-预调整"><a href="#instanceof增强-预调整" class="headerlink" title="instanceof增强(预调整)"></a><code>instanceof</code>增强(预调整)</h1><p>目前<code>instanceof</code>大多数使用方式如下所示:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (obj <span class="keyword">instanceof</span> String) {</span><br><span class="line"> <span class="type">String</span> <span class="variable">s</span> <span class="operator">=</span> (String) obj;</span><br><span class="line"> <span class="comment">// use s</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li>先判断是否是String类型</li><li>类型强转并赋值新变量</li><li>变量使用<br>增强后,将1-2步骤合并,如下所示:<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (obj <span class="keyword">instanceof</span> String s) {</span><br><span class="line"> <span class="comment">// can use s here</span></span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// can't use s here</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>该特性属于代码简化,增加代码可读性。<br>目前JDK14未开放使用。</li></ol><h1 id="新增一个打包工具"><a href="#新增一个打包工具" class="headerlink" title="新增一个打包工具"></a>新增一个打包工具</h1><p>基于JavaFX的打包工具,能够将Java应用程序打包成各系统对应的应用类型,如exe、dmg、dep等。应该是针对客户端软件的更新。</p><h1 id="G1增强:NUMA-Aware-内存分配"><a href="#G1增强:NUMA-Aware-内存分配" class="headerlink" title="G1增强:NUMA-Aware 内存分配"></a>G1增强:NUMA-Aware 内存分配</h1><p>随着使用非统一内存访问架构(NUMA)的机器越来越多,G1将针对NUMA场景处理增强</p><ol><li>年轻代将保证在同一NUMA节点分配内存,如果内存不够将触发垃圾回收</li><li>老年代不需要保证在同一NUMA节点分配内存</li><li>Humongous区域不适用该策略<br>通过启动参数<code>+XX:+UseNUMA</code>开启</li></ol><h1 id="JFR增强:事件流"><a href="#JFR增强:事件流" class="headerlink" title="JFR增强:事件流"></a>JFR增强:事件流</h1><p>在JDK14之前,JFR都是通过命令手动开启录制,转储文件,再进行分析,现在开放了API,可以通过Java代码进行持续的监控,接下来举例两种使用方式(还有其他使用方式)。<code>agent</code>领域的开发同学狂喜。</p><h2 id="RecordingStream"><a href="#RecordingStream" class="headerlink" title="RecordingStream"></a>RecordingStream</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> (<span class="type">var</span> <span class="variable">rs</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RecordingStream</span>()) {</span><br><span class="line"> rs.enable(<span class="string">"jdk.CPULoad"</span>).withPeriod(Duration.ofSeconds(<span class="number">1</span>));</span><br><span class="line"> rs.enable(<span class="string">"jdk.JavaMonitorEnter"</span>).withThreshold(Duration.ofMillis(<span class="number">10</span>));</span><br><span class="line"> rs.onEvent(<span class="string">"jdk.CPULoad"</span>, event -> {</span><br><span class="line"> System.out.println(<span class="string">"func1:jdk.CPULoad:"</span> + event.getFloat(<span class="string">"machineTotal"</span>));</span><br><span class="line"> });</span><br><span class="line"> rs.onEvent(<span class="string">"jdk.JavaMonitorEnter"</span>, event -> {</span><br><span class="line"> System.out.println(<span class="string">"func1:jdk.JavaMonitorEnter:"</span> + event.getClass(<span class="string">"monitorClass"</span>));</span><br><span class="line"> });</span><br><span class="line"> rs.start();</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>该方法可以直接进行指定的参数录制,并在对应的事件中触发,可由自定义代码收集</p><h2 id="EventStream-openRepository"><a href="#EventStream-openRepository" class="headerlink" title="EventStream::openRepository()"></a>EventStream::openRepository()</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="type">EventStream</span> <span class="variable">eventStream</span> <span class="operator">=</span> EventStream.openRepository();</span><br><span class="line"> <span class="keyword">try</span> (eventStream) {</span><br><span class="line"> eventStream.onEvent(recordedEvent -> {</span><br><span class="line"> System.out.println(<span class="string">"func2:getEventType:"</span> + recordedEvent.getEventType().getName());</span><br><span class="line"> });</span><br><span class="line"> eventStream.start();</span><br><span class="line"> }</span><br><span class="line">} <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>该方式需要在进程外通过命令行或其他方式开启JFR才能够开始录制。</p><h1 id="硬件适配增强:Non-Volatile-Mapped-Byte-Buffers"><a href="#硬件适配增强:Non-Volatile-Mapped-Byte-Buffers" class="headerlink" title="硬件适配增强:Non-Volatile Mapped Byte Buffers"></a>硬件适配增强:Non-Volatile Mapped Byte Buffers</h1><p>针对 Linux/x64 和 Linux/AArch64 的非易失性存储器 (NVM)场景下,应用程序可以有效且连贯的访问和更新NVM。<br>官方文档说</p><blockquote><p>NVM offers the opportunity for application programmers to create and update program state across program runs without incurring the significant copying and/or translation costs that output to and input from a persistent medium normally implies. This is particularly significant for transactional programs, where regular persistence of in-doubt state is required to enable crash recovery.</p></blockquote><p>跨程序的数据交互场景,笔者想不到能够容易理解的业务场景。这中特定的硬件组合,和JDK10提出的<code>Heap Allocation on Alternative Memory Devices</code>很类似,应该在大数据集群处理的业务场景可能会有用处?</p><h1 id="空指针异常信息增强"><a href="#空指针异常信息增强" class="headerlink" title="空指针异常信息增强"></a>空指针异常信息增强</h1><p>以前的空指针异常信息只显示到某一行,如果某一行的变量比较多,是没办法直接定位哪个变量为null的,这时候基本上都需要本地再运行debug才行,而JDK14中,通过启动参数<code>-XX:+ShowCodeDetailsInExceptionMessages</code>可以开启更详细的空指针异常信息.<br>需要注意的是,官方文档提出这种详细信息是经过了额外的计算的,因此性能方面需要考虑自己的应用空指针异常是否频繁,避免带来额外的性能开销。</p><h2 id="异常示例代码"><a href="#异常示例代码" class="headerlink" title="异常示例代码"></a>异常示例代码</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NPE</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">C</span> <span class="variable">c</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">C</span>();</span><br><span class="line"> c.b = <span class="keyword">new</span> <span class="title class_">B</span>();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> c.b.a.name = <span class="string">"null"</span>;</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> B.test().arr[<span class="number">1</span>][<span class="number">2</span>] = <span class="number">3</span>;</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">A</span> {</span><br><span class="line"> <span class="keyword">public</span> String name;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span>[][] arr;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">B</span> {</span><br><span class="line"> <span class="keyword">public</span> A a;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> A <span class="title function_">test</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">A</span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">C</span> {</span><br><span class="line"> <span class="keyword">public</span> B b;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="异常信息"><a href="#异常信息" class="headerlink" title="异常信息"></a>异常信息</h2><h3 id="开启前"><a href="#开启前" class="headerlink" title="开启前"></a>开启前</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">java.lang.NullPointerException</span><br><span class="line">at com.janwarlen.feature.NPE.func(NPE.java:<span class="number">9</span>)</span><br><span class="line">at com.janwarlen.Demo.main(Demo.java:<span class="number">15</span>)</span><br><span class="line">java.lang.NullPointerException</span><br><span class="line">at com.janwarlen.feature.NPE.func(NPE.java:<span class="number">14</span>)</span><br><span class="line">at com.janwarlen.Demo.main(Demo.java:<span class="number">15</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><h3 id="开启后"><a href="#开启后" class="headerlink" title="开启后"></a>开启后</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">java.lang.NullPointerException: Cannot assign field <span class="string">"name"</span> because <span class="string">"c.b.a"</span> is <span class="literal">null</span></span><br><span class="line">at com.janwarlen.feature.NPE.func(NPE.java:<span class="number">9</span>)</span><br><span class="line">at com.janwarlen.Demo.main(Demo.java:<span class="number">15</span>)</span><br><span class="line">java.lang.NullPointerException: Cannot load from object array because <span class="string">"com.janwarlen.feature.NPE$B.test().arr"</span> is <span class="literal">null</span></span><br><span class="line">at com.janwarlen.feature.NPE.func(NPE.java:<span class="number">14</span>)</span><br><span class="line">at com.janwarlen.Demo.main(Demo.java:<span class="number">15</span>)</span><br></pre></td></tr></table></figure><h1 id="新的数据建模方式-预调整"><a href="#新的数据建模方式-预调整" class="headerlink" title="新的数据建模方式(预调整)"></a>新的数据建模方式(预调整)</h1><p>目前想要封装一个数据模型类,有很多方法需要重复的编码,如<code>equals</code>、<code>hashcode</code>、<code>toString</code>等。因此JDK14中预添加了该技术特性(添加但未开放使用),以便开发人员更简单的创建数据模型类。因未开放使用,因此只能从官方文档上简单的阅读,从表面上看是简化了数据模型的类的创建,但是看起来实际使用效果并不会太好,因为数据模型类的创建,那些重复编码的函数并不是非常重要的部分,继承多态、工程结构才是。使用该特性是有额外的限制的,比如:不能继承于其他的类,不能为抽象类,并且是隐式的<code>final</code>等等。综合对比,似乎该特性即便开放使用,也只能在有限的场景里发挥作用。</p><h1 id="Switch增强-正式开发使用"><a href="#Switch增强-正式开发使用" class="headerlink" title="Switch增强(正式开发使用)"></a>Switch增强(正式开发使用)</h1><p>前两次JDK的预调整终于开放使用,放Demo代码</p><h2 id="增强前"><a href="#增强前" class="headerlink" title="增强前"></a>增强前</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">func1</span><span class="params">(String arg)</span> {</span><br><span class="line"> <span class="keyword">switch</span> (arg) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"1"</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"2"</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"3"</span>:</span><br><span class="line"> System.out.println(<span class="string">"small"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"4"</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"5"</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"6"</span>:</span><br><span class="line"> System.out.println(<span class="string">"big"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> System.out.println(<span class="string">"illegal"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">func2</span><span class="params">(<span class="type">int</span> e)</span> {</span><br><span class="line"> <span class="type">int</span> x;</span><br><span class="line"> <span class="keyword">switch</span> (e) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> x = <span class="number">111</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> x = <span class="number">222</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line"> x = <span class="number">233</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> x = <span class="number">666</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> System.out.println(x);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="增强后"><a href="#增强后" class="headerlink" title="增强后"></a>增强后</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><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"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">func1Enhanced</span><span class="params">(String arg)</span> {</span><br><span class="line"> <span class="keyword">switch</span> (arg) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"1"</span>, <span class="string">"2"</span>, <span class="string">"3"</span> -> System.out.println(<span class="string">"small"</span>);</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"4"</span>, <span class="string">"5"</span>, <span class="string">"6"</span> -> System.out.println(<span class="string">"big"</span>);</span><br><span class="line"> <span class="keyword">default</span> -> System.out.println(<span class="string">"illegal"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">func2Enhanced</span><span class="params">(<span class="type">int</span> e)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="keyword">switch</span> (e) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span> -> <span class="number">111</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span> -> <span class="number">222</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">3</span> -> <span class="number">233</span>;</span><br><span class="line"> <span class="keyword">default</span> -> <span class="number">666</span>;</span><br><span class="line"> };</span><br><span class="line"> System.out.println(x);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="yield"><a href="#yield" class="headerlink" title="yield"></a>yield</h2><p>官方文档没有明确说明yield的作用,从示例代码来看(和之前JDK版本的说明),该关键字使用场景为case中执行语句有多条,但是又必须返回值的场景。</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">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func3</span><span class="params">(String yield)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">d</span> <span class="operator">=</span> <span class="keyword">switch</span> (yield) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"MONDAY"</span> -> {</span><br><span class="line"> System.out.println(<span class="string">"monday"</span>);</span><br><span class="line"> yield <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"TUESDAY"</span> -> {</span><br><span class="line"> System.out.println(<span class="string">"TUESDAY"</span>);</span><br><span class="line"> yield <span class="number">2</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">default</span> -> {</span><br><span class="line"> System.out.println(<span class="string">"not monday"</span>);</span><br><span class="line"> yield <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> System.out.println(<span class="string">"func3:"</span> + d);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h1 id="弃用-Solaris-and-SPARC-Ports"><a href="#弃用-Solaris-and-SPARC-Ports" class="headerlink" title="弃用 Solaris and SPARC Ports"></a>弃用 Solaris and SPARC Ports</h1><p>不太明白这俩是干嘛的,也没有搜到有博主分享关于这两个的文档,根据官方文档的这段话</p><blockquote><p>Dropping support for these ports will enable contributors in the OpenJDK Community to accelerate the development of new features that will move the platform forward.</p></blockquote><p>看着像是针对特殊的系统平台类的兼容,该弃用应该也是为了更方便的迭代和扩展。</p><h1 id="删除垃圾回收器-CMS-Concurrent-Mark-Sweep"><a href="#删除垃圾回收器-CMS-Concurrent-Mark-Sweep" class="headerlink" title="删除垃圾回收器: CMS(Concurrent Mark Sweep)"></a>删除垃圾回收器: CMS(Concurrent Mark Sweep)</h1><p>G1已经可以完全替代CMS,从性能和效率各个方面,JDK9标注废弃,等到这个版本才正式删除。</p><h1 id="ZGC移植:macOS、Windows-实验性"><a href="#ZGC移植:macOS、Windows-实验性" class="headerlink" title="ZGC移植:macOS、Windows(实验性)"></a>ZGC移植:macOS、Windows(实验性)</h1><p>方便开发人员在本地调试,也针对开发桌面客户端软件的用户能够使用ZGC。</p><h1 id="垃圾回收算法弃用:ParallelScavenge-SerialOld"><a href="#垃圾回收算法弃用:ParallelScavenge-SerialOld" class="headerlink" title="垃圾回收算法弃用:ParallelScavenge + SerialOld"></a>垃圾回收算法弃用:ParallelScavenge + SerialOld</h1><p>官方假设使用这种算法组合的人较少才进行弃用,如果后续反馈的人数较多,可能会重新启用。<br>官方认为这种组合仅适用于具有非常大的年轻一代和非常小的老一代的部署。<br>涉及启动参数:<code>-XX:+UseParallelGC -XX:-UseParallelOldGC -XX:UseParallelOldGC</code></p><h1 id="删除Pack200及其API"><a href="#删除Pack200及其API" class="headerlink" title="删除Pack200及其API"></a>删除Pack200及其API</h1><p>JDK11弃用的,在本此JDK迭代中彻底删除<br>该针对jar包的压缩方案已经不再适用于现在的各种场景。</p><h1 id="文本块-第二次预调整"><a href="#文本块-第二次预调整" class="headerlink" title="文本块(第二次预调整)"></a>文本块(第二次预调整)</h1><p>为了更好地控制换行符和空格的处理,我们引入了两个新的转义序列。</p><h2 id="当前行末尾添加-表示不换行"><a href="#当前行末尾添加-表示不换行" class="headerlink" title="当前行末尾添加\表示不换行"></a>当前行末尾添加\表示不换行</h2><p>该转义仅在文本块中生效。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">literal</span> <span class="operator">=</span> <span class="string">"Lorem ipsum dolor sit amet, consectetur adipiscing "</span> +</span><br><span class="line"> <span class="string">"elit, sed do eiusmod tempor incididunt ut labore "</span> +</span><br><span class="line"> <span class="string">"et dolore magna aliqua."</span>;</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></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">text</span> <span class="operator">=</span> <span class="string">"""</span></span><br><span class="line"><span class="string"> Lorem ipsum dolor sit amet, consectetur adipiscing \</span></span><br><span class="line"><span class="string"> elit, sed do eiusmod tempor incididunt ut labore \</span></span><br><span class="line"><span class="string"> et dolore magna aliqua.\</span></span><br><span class="line"><span class="string"> """</span>;</span><br></pre></td></tr></table></figure><h2 id="s"><a href="#s" class="headerlink" title="\s"></a>\s</h2><p>等效于’ ‘。该转义在String中都可以使用。<br>应该是针对String::stripIndent()和trim()提出的,<code>Escape sequences aren't translated until after incident space stripping</code>,使用\s防止文本块中行末的空格被清空。</p><h1 id="添加API以便Java可以访问堆外外部内存"><a href="#添加API以便Java可以访问堆外外部内存" class="headerlink" title="添加API以便Java可以访问堆外外部内存"></a>添加API以便Java可以访问堆外外部内存</h1><ol><li>避开垃圾回收器维护大量的缓存</li><li>跨进程通信</li><li>映射文件到内存<br>等等场景皆可使用,不过官方仅是给出API可以访问堆外的外部内存,具体的功能实现还需要用户自己去封装。<br>核心三个类<code>MemorySegment/MemoryAddress/MemoryLayout</code></li></ol>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://openjdk.org/projects/jdk/14/">jdk14特性列表</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>switch已经可以正式使用,用起来很爽</li>
<li>官方开放了API可以访问堆外的外部内存,这个特性看起来在大数据领域可以发挥很大的作用</li>
<li>空指针异常信息的细节增强非常方便开发人员的本地开发过程,大大的简化了本地调试的工作</summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK14" scheme="http://janwarlen.com/tags/JDK14/"/>
</entry>
<entry>
<title>Java版本特性-JDK13</title>
<link href="http://janwarlen.com/2022/06/20/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK13/"/>
<id>http://janwarlen.com/2022/06/20/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK13/</id>
<published>2022-06-20T08:12:33.000Z</published>
<updated>2022-09-02T02:49:26.499Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://openjdk.org/projects/jdk/13/">jdk13特性列表</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>本次版本没有对用户代码直接影响特性</li><li>AppCDS的增强对于使用AppCDS的用户来说是降低了日常的使用成本</li><li>对于大多数开发人员来说,我想<code>文本块</code>的提出是非常令人舒畅的,日常的大文本的使用的确是一直为人诟病的,这个虽然只是小细节的优化调整,甚至都未开放使用,但是却令笔者对Java的后续更新迭代增加了很多期望<span id="more"></span></li></ol><h1 id="AppCDS增强"><a href="#AppCDS增强" class="headerlink" title="AppCDS增强"></a>AppCDS增强</h1><p>应用程序退出时将动态创建共享的归档,该归档是在JDK12中新增的默认归档基础上创建,因此启动参数不可指定<code>-Xshare:off</code>(默认为on),该特性提高了AppCDS的可用性,JDK10 中需要先运行几次应用来创建类列表,再转储为归档文件,再使用归档文件启动。<br>应用程序退出时动态创建归档需要使用启动参数<code>-XX:ArchiveClassesAtExit</code>开启。</p><h1 id="ZGC增强:允许释放未使用堆内存回操作系统"><a href="#ZGC增强:允许释放未使用堆内存回操作系统" class="headerlink" title="ZGC增强:允许释放未使用堆内存回操作系统"></a>ZGC增强:允许释放未使用堆内存回操作系统</h1><p>该功能与JDK10 的G1增强一致,都是为了尽可能减少应用程序的资源消耗。<br>不会将内存减少到低于<code>-Xms</code>。</p><h1 id="替换-Socket-API-底层实现"><a href="#替换-Socket-API-底层实现" class="headerlink" title="替换 Socket API 底层实现"></a>替换 Socket API 底层实现</h1><p>将 java.net.Socket 和 java.net.ServerSocket API 使用的底层实现替换为更简单、更现代且易于维护和调试的实现。</p><h1 id="switch表达式第二次预调整"><a href="#switch表达式第二次预调整" class="headerlink" title="switch表达式第二次预调整"></a>switch表达式第二次预调整</h1><p>对比于JDK12的调整,本次是取消了break 返回值,使用<code>yield</code>返回值进行替换。<br>JDK13未正式开放使用该特性。</p><h1 id="文本块-预调整"><a href="#文本块-预调整" class="headerlink" title="文本块(预调整)"></a>文本块(预调整)</h1><p>目前对于大量文本大多用<code>+</code>和换行进行拼接</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">html</span> <span class="operator">=</span> <span class="string">"<html>\n"</span> +</span><br><span class="line"> <span class="string">" <body>\n"</span> +</span><br><span class="line"> <span class="string">" <p>Hello, world</p>\n"</span> +</span><br><span class="line"> <span class="string">" </body>\n"</span> +</span><br><span class="line"> <span class="string">"</html>\n"</span>;</span><br></pre></td></tr></table></figure><p>新的文本块特性将允许<code>"""</code>标记文本区域,从而简化文本块的使用</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">html</span> <span class="operator">=</span> <span class="string">"""</span></span><br><span class="line"><span class="string"> <html></span></span><br><span class="line"><span class="string"> <body></span></span><br><span class="line"><span class="string"> <p>Hello, world</p></span></span><br><span class="line"><span class="string"> </body></span></span><br><span class="line"><span class="string"> </html></span></span><br><span class="line"><span class="string"> """</span>;</span><br></pre></td></tr></table></figure><p>JDK13未正式开放使用该特性。</p>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://openjdk.org/projects/jdk/13/">jdk13特性列表</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>本次版本没有对用户代码直接影响特性</li>
<li>AppCDS的增强对于使用AppCDS的用户来说是降低了日常的使用成本</li>
<li>对于大多数开发人员来说,我想<code>文本块</code>的提出是非常令人舒畅的,日常的大文本的使用的确是一直为人诟病的,这个虽然只是小细节的优化调整,甚至都未开放使用,但是却令笔者对Java的后续更新迭代增加了很多期望</summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK13" scheme="http://janwarlen.com/tags/JDK13/"/>
</entry>
<entry>
<title>Java版本特性-JDK12</title>
<link href="http://janwarlen.com/2022/06/19/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK12/"/>
<id>http://janwarlen.com/2022/06/19/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK12/</id>
<published>2022-06-19T12:12:33.000Z</published>
<updated>2022-09-02T02:49:26.475Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://openjdk.org/projects/jdk/12/">jdk12特性列表</a></li><li><a href="https://www.researchgate.net/publication/306112816_Shenandoah_An_open-source_concurrent_compacting_garbage_collector_for_OpenJDK">Shenandoah算法论文</a></li><li><a href="https://github.com/openjdk/jmh-jdk-microbenchmarks">jmh Git仓库</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>JDK12对用户代码无直接影响</li><li>主要内容是新增GC(实验阶段)和对已有G1的增强<span id="more"></span></li></ol><h1 id="新GC:Shenandoah-实验"><a href="#新GC:Shenandoah-实验" class="headerlink" title="新GC:Shenandoah(实验)"></a>新GC:Shenandoah(实验)</h1><p>GC时间不受堆内存大小影响,200M与200G 停顿时间一致,通过启动参数<code>-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC</code>开启。</p><h1 id="基准测试组件"><a href="#基准测试组件" class="headerlink" title="基准测试组件"></a>基准测试组件</h1><p>添加了基础的基准测试组件,方便开发人员(测试方向)执行现有的基准测试和创建新的基准测试。<br>实际尝试了,从JDK12-18在maven未引入<code>jmh</code>相关jar包是无法使用相关注解的。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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 class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.openjdk.jmh<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>jmh-core<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.19<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.openjdk.jmh<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>jmh-generator-annprocess<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.19<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure><p>但是官方文档明确说了将基础的组件添加到了jdk的源码中,因此只能推测是只开放给JDK开发人员使用。通过官方github下载的<code>jdk12</code>源码,目录结构也与文档一致,相关代码都在test目录中,因此该特性是针对JDK开发人员来说的….</p><h1 id="Switch表达式优化-JDK12暂未开放"><a href="#Switch表达式优化-JDK12暂未开放" class="headerlink" title="Switch表达式优化(JDK12暂未开放)"></a>Switch表达式优化(JDK12暂未开放)</h1><h2 id="简化case分支"><a href="#简化case分支" class="headerlink" title="简化case分支"></a>简化case分支</h2><p>但在JDK12中还不能使用,会提示<code>Enhanced 'switch' blocks are not supported at language level '12'</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">switch</span> (day) {</span><br><span class="line"> <span class="keyword">case</span> MONDAY:</span><br><span class="line"> <span class="keyword">case</span> FRIDAY:</span><br><span class="line"> <span class="keyword">case</span> SUNDAY:</span><br><span class="line"> System.out.println(<span class="number">6</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> TUESDAY:</span><br><span class="line"> System.out.println(<span class="number">7</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> THURSDAY:</span><br><span class="line"> <span class="keyword">case</span> SATURDAY:</span><br><span class="line"> System.out.println(<span class="number">8</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> WEDNESDAY:</span><br><span class="line"> System.out.println(<span class="number">9</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">switch</span> (day) {</span><br><span class="line"> <span class="keyword">case</span> MONDAY, FRIDAY, SUNDAY -> System.out.println(<span class="number">6</span>);</span><br><span class="line"> <span class="keyword">case</span> TUESDAY -> System.out.println(<span class="number">7</span>);</span><br><span class="line"> <span class="keyword">case</span> THURSDAY, SATURDAY -> System.out.println(<span class="number">8</span>);</span><br><span class="line"> <span class="keyword">case</span> WEDNESDAY -> System.out.println(<span class="number">9</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="switch代码块可当作函数返回值"><a href="#switch代码块可当作函数返回值" class="headerlink" title="switch代码块可当作函数返回值"></a>switch代码块可当作函数返回值</h2><p>实际不确定这样写会不会影响代码的可读性,尤其是switch的case情况较多的场景,还是建议封装为单独的函数,避免switch代码块庞大影响代码可读性。</p><h3 id="模板"><a href="#模板" class="headerlink" title="模板"></a>模板</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">T</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">switch</span> (arg) {</span><br><span class="line"> <span class="keyword">case</span> L1 -> e1;</span><br><span class="line"> <span class="keyword">case</span> L2 -> e2;</span><br><span class="line"> <span class="keyword">default</span> -> e3;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">switch</span> (s) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"Foo"</span>: </span><br><span class="line"> <span class="keyword">break</span> <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"Bar"</span>:</span><br><span class="line"> <span class="keyword">break</span> <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> System.out.println(<span class="string">"Neither Foo nor Bar, hmmm..."</span>);</span><br><span class="line"> <span class="keyword">break</span> <span class="number">0</span>;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h1 id="JVM常量API"><a href="#JVM常量API" class="headerlink" title="JVM常量API"></a>JVM常量API</h1><blockquote><p>Introduce an API to model nominal descriptions of key class-file and run-time artifacts, in particular constants that are loadable from the constant pool.</p></blockquote><p>引入一个 API 来模拟关键类文件和运行时工件的名义描述,特别是可从常量池加载的常量。<br>没有搜到有博主分享使用该特性的场景,应该也是JDK或JVM内部的开发工作优化。</p><h1 id="AArch64兼容代码优化"><a href="#AArch64兼容代码优化" class="headerlink" title="AArch64兼容代码优化"></a>AArch64兼容代码优化</h1><p>属于代码重构,简化后续JDK工作。</p><h1 id="默认-CDS"><a href="#默认-CDS" class="headerlink" title="默认 CDS"></a>默认 CDS</h1><p>该特性仅针对64位的JDK,相当于默认开启JDK的CDS,如果不需要CDS,需要使用启动参数<code>-Xshare:off</code>停止。</p><h1 id="可中断的Mixed-G1"><a href="#可中断的Mixed-G1" class="headerlink" title="可中断的Mixed G1"></a>可中断的Mixed G1</h1><p>当G1预计回收的区域过多时,将会切换到Mixed模式,将目标切割为必须回收与可选回收,在目标时间内,有限回收必须回收部分,如果时间有剩余,才会处理可选回收部分,并且时间结束就立即停止,无论可选回收部分是否全部回收结束。<br>有效的避免G1的回收时间超过用户指定时间(<code>-XX:MaxGCPauseMillis</code>默认200ms)</p><h1 id="空闲时G1也可释放内存回操作系统"><a href="#空闲时G1也可释放内存回操作系统" class="headerlink" title="空闲时G1也可释放内存回操作系统"></a>空闲时G1也可释放内存回操作系统</h1><p>增强G1(定期触发),使得G1在应用程序不活动期间,也可以进行内存回收行动,减少资源消耗。<br>触发条件有两个,需同时满足:</p><ol><li>G1PeriodicGCInterval时间内无任何垃圾回收行为(包括并发标记周期)</li><li>一分钟内,系统负载(通过<code>getloadavg()</code>获取)低于<code>G1PeriodicGCSystemLoadThreshold</code>(该值为0则此条件忽略)</li></ol>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://openjdk.org/projects/jdk/12/">jdk12特性列表</a></li>
<li><a href="https://www.researchgate.net/publication/306112816_Shenandoah_An_open-source_concurrent_compacting_garbage_collector_for_OpenJDK">Shenandoah算法论文</a></li>
<li><a href="https://github.com/openjdk/jmh-jdk-microbenchmarks">jmh Git仓库</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>JDK12对用户代码无直接影响</li>
<li>主要内容是新增GC(实验阶段)和对已有G1的增强</summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK12" scheme="http://janwarlen.com/tags/JDK12/"/>
</entry>
<entry>
<title>Java版本特性-JDK11</title>
<link href="http://janwarlen.com/2022/06/17/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK11/"/>
<id>http://janwarlen.com/2022/06/17/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK11/</id>
<published>2022-06-17T08:38:41.000Z</published>
<updated>2022-09-02T02:49:26.449Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://openjdk.org/projects/jdk/11/">jdk11特性列表</a></li><li><a href="https://www.bbsmax.com/A/QW5Ybg23dm/">nest class和inner class的区别</a></li><li><a href="https://cloud.tencent.com/developer/article/1165256">JDK11中增加了一个常量池类型:CONSTANT_Dynamic</a></li><li><a href="https://www.techwell.com/techwell-insights/2021/08/new-jvm-features-jdk-11">new-jvm-features-jdk-11</a></li><li><a href="https://www.javacodegeeks.com/2018/08/hands-on-java-constantdynamic.html">hands-on-java-constantdynamic</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>直接影响编码的功能几乎没有,大多数都是幕后工作的更新</li><li>ZGC是一个核心内容,不过JDK11中是实验版</li><li>JFR的加入方便了性能监控,建议上手实际操作几次,非常好用</li><li>加密算法的几个新增实现未进行深入了解(未涉足领域,功力不足)<span id="more"></span></li></ol><h1 id="内部类-嵌套类-的权限控制-Nest-Based-Access-Control"><a href="#内部类-嵌套类-的权限控制-Nest-Based-Access-Control" class="headerlink" title="内部类(嵌套类)的权限控制(Nest-Based Access Control)"></a>内部类(嵌套类)的权限控制(Nest-Based Access Control)</h1><blockquote><p>Introduce nests, an access-control context that aligns with the existing notion of nested types in the Java programming language. Nests allow classes that are logically part of the same code entity, but which are compiled to distinct class files, to access each other’s private members without the need for compilers to insert accessibility-broadening bridge methods.</p></blockquote><p> <a href="https://openjdk.org/jeps/181">原文</a>的开头介绍说自该版本起,内部类之间访问私有属性不用再通过编译器生成<code>accessibility-broadening bridge methods</code>。然后我阅读整篇文档都没有举例说明这个是啥,并且又引入了新的概念<code>InnerClasses</code>和<code>EnclosingMethod</code>,越看越迷糊,百度也没有比较好的文章去说明这个特性,不过还好搜到了<code>nest class和inner class的区别</code>,但是光明白这个也没用,还好之前看<code>《深入理解Java虚拟机》</code>时了解到了java的class文件反编译汇编的概念,于是尝试了一下</p><h2 id="Demo类"><a href="#Demo类" class="headerlink" title="Demo类"></a>Demo类</h2> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">//https://openjdk.org/jeps/181</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NestClass</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">NestClass_1</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">func</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">NestClass_2</span> <span class="variable">n2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NestClass_2</span>();</span><br><span class="line"> System.out.println(<span class="string">"NestClass_1:func:NestClass_2:name:"</span> + n2.name);</span><br><span class="line"> <span class="type">NestClass</span> <span class="variable">nestClass</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NestClass</span>();</span><br><span class="line"> <span class="type">InnerClass</span> <span class="variable">i1</span> <span class="operator">=</span> nestClass.<span class="keyword">new</span> <span class="title class_">InnerClass</span>();</span><br><span class="line"> System.out.println(<span class="string">"NestClass_1:func:InnerClass:name:"</span> + i1.name);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">NestClass_2</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">"NestClass_2"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">InnerClass</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">"InnerClass"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 该类通过JDK8和JDK11分别得到两套class文件,使用<code>javap -c class文件名</code>命令去得到反编译结果,可直接查看<a href="#%E5%AF%B9%E6%AF%94%E8%A7%A3%E8%AF%BB">对比解读</a></p><h2 id="反编译结果"><a href="#反编译结果" class="headerlink" title="反编译结果"></a>反编译结果</h2><h3 id="JDK8"><a href="#JDK8" class="headerlink" title="JDK8"></a>JDK8</h3> <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><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></pre></td><td class="code"><pre><span class="line"> jan<span class="meta">@JanWarlendeMacBook</span>-Pro feature % javap -c NestClass.<span class="keyword">class</span> </span><br><span class="line"><span class="title class_">Compiled</span> from <span class="string">"NestClass.java"</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">com</span>.janwarlen.feature.NestClass {</span><br><span class="line"> <span class="keyword">public</span> com.janwarlen.feature.NestClass();</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: invokespecial #<span class="number">1</span> <span class="comment">// Method java/lang/Object."<init>":()V</span></span><br><span class="line"> <span class="number">4</span>: <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line">jan<span class="meta">@JanWarlendeMacBook</span>-Pro feature % javap -c NestClass\$NestClass_1.<span class="keyword">class</span> </span><br><span class="line"><span class="title class_">Compiled</span> from <span class="string">"NestClass.java"</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">com</span>.janwarlen.feature.NestClass$NestClass_1 {</span><br><span class="line"> <span class="keyword">public</span> com.janwarlen.feature.NestClass$NestClass_1();</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: invokespecial #<span class="number">1</span> <span class="comment">// Method java/lang/Object."<init>":()V</span></span><br><span class="line"> <span class="number">4</span>: <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">func</span><span class="params">()</span>;</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: <span class="keyword">new</span> #<span class="number">2</span> <span class="comment">// class com/janwarlen/feature/NestClass$NestClass_2</span></span><br><span class="line"> <span class="number">3</span>: dup</span><br><span class="line"> <span class="number">4</span>: invokespecial #<span class="number">3</span> <span class="comment">// Method com/janwarlen/feature/NestClass$NestClass_2."<init>":()V</span></span><br><span class="line"> <span class="number">7</span>: astore_1</span><br><span class="line"> <span class="number">8</span>: getstatic #<span class="number">4</span> <span class="comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span></span><br><span class="line"> <span class="number">11</span>: <span class="keyword">new</span> #<span class="number">5</span> <span class="comment">// class java/lang/StringBuilder</span></span><br><span class="line"> <span class="number">14</span>: dup</span><br><span class="line"> <span class="number">15</span>: invokespecial #<span class="number">6</span> <span class="comment">// Method java/lang/StringBuilder."<init>":()V</span></span><br><span class="line"> <span class="number">18</span>: ldc #<span class="number">7</span> <span class="comment">// String NestClass_1:func:NestClass_2:name:</span></span><br><span class="line"> <span class="number">20</span>: invokevirtual #<span class="number">8</span> <span class="comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span></span><br><span class="line"> <span class="number">23</span>: aload_1</span><br><span class="line"> <span class="number">24</span>: invokestatic #<span class="number">9</span> <span class="comment">// Method com/janwarlen/feature/NestClass$NestClass_2.access$000:(Lcom/janwarlen/feature/NestClass$NestClass_2;)Ljava/lang/String;</span></span><br><span class="line"> <span class="number">27</span>: invokevirtual #<span class="number">8</span> <span class="comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span></span><br><span class="line"> <span class="number">30</span>: invokevirtual #<span class="number">10</span> <span class="comment">// Method java/lang/StringBuilder.toString:()Ljava/lang/String;</span></span><br><span class="line"> <span class="number">33</span>: invokevirtual #<span class="number">11</span> <span class="comment">// Method java/io/PrintStream.println:(Ljava/lang/String;)V</span></span><br><span class="line"> <span class="number">36</span>: <span class="keyword">new</span> #<span class="number">12</span> <span class="comment">// class com/janwarlen/feature/NestClass</span></span><br><span class="line"> <span class="number">39</span>: dup</span><br><span class="line"> <span class="number">40</span>: invokespecial #<span class="number">13</span> <span class="comment">// Method com/janwarlen/feature/NestClass."<init>":()V</span></span><br><span class="line"> <span class="number">43</span>: astore_2</span><br><span class="line"> <span class="number">44</span>: <span class="keyword">new</span> #<span class="number">14</span> <span class="comment">// class com/janwarlen/feature/NestClass$InnerClass</span></span><br><span class="line"> <span class="number">47</span>: dup</span><br><span class="line"> <span class="number">48</span>: aload_2</span><br><span class="line"> <span class="number">49</span>: dup</span><br><span class="line"> <span class="number">50</span>: invokevirtual #<span class="number">15</span> <span class="comment">// Method java/lang/Object.getClass:()Ljava/lang/Class;</span></span><br><span class="line"> <span class="number">53</span>: pop</span><br><span class="line"> <span class="number">54</span>: invokespecial #<span class="number">16</span> <span class="comment">// Method com/janwarlen/feature/NestClass$InnerClass."<init>":(Lcom/janwarlen/feature/NestClass;)V</span></span><br><span class="line"> <span class="number">57</span>: astore_3</span><br><span class="line"> <span class="number">58</span>: getstatic #<span class="number">4</span> <span class="comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span></span><br><span class="line"> <span class="number">61</span>: <span class="keyword">new</span> #<span class="number">5</span> <span class="comment">// class java/lang/StringBuilder</span></span><br><span class="line"> <span class="number">64</span>: dup</span><br><span class="line"> <span class="number">65</span>: invokespecial #<span class="number">6</span> <span class="comment">// Method java/lang/StringBuilder."<init>":()V</span></span><br><span class="line"> <span class="number">68</span>: ldc #<span class="number">17</span> <span class="comment">// String NestClass_1:func:InnerClass:name:</span></span><br><span class="line"> <span class="number">70</span>: invokevirtual #<span class="number">8</span> <span class="comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span></span><br><span class="line"> <span class="number">73</span>: aload_3</span><br><span class="line"> <span class="number">74</span>: invokestatic #<span class="number">18</span> <span class="comment">// Method com/janwarlen/feature/NestClass$InnerClass.access$100:(Lcom/janwarlen/feature/NestClass$InnerClass;)Ljava/lang/String;</span></span><br><span class="line"> <span class="number">77</span>: invokevirtual #<span class="number">8</span> <span class="comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span></span><br><span class="line"> <span class="number">80</span>: invokevirtual #<span class="number">10</span> <span class="comment">// Method java/lang/StringBuilder.toString:()Ljava/lang/String;</span></span><br><span class="line"> <span class="number">83</span>: invokevirtual #<span class="number">11</span> <span class="comment">// Method java/io/PrintStream.println:(Ljava/lang/String;)V</span></span><br><span class="line"> <span class="number">86</span>: <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line">jan<span class="meta">@JanWarlendeMacBook</span>-Pro feature % javap -c NestClass\$NestClass_2.<span class="keyword">class</span></span><br><span class="line"><span class="title class_">Compiled</span> from <span class="string">"NestClass.java"</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">com</span>.janwarlen.feature.NestClass$NestClass_2 {</span><br><span class="line"> <span class="keyword">public</span> com.janwarlen.feature.NestClass$NestClass_2();</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: invokespecial #<span class="number">2</span> <span class="comment">// Method java/lang/Object."<init>":()V</span></span><br><span class="line"> <span class="number">4</span>: aload_0</span><br><span class="line"> <span class="number">5</span>: ldc #<span class="number">3</span> <span class="comment">// String NestClass_2</span></span><br><span class="line"> <span class="number">7</span>: putfield #<span class="number">1</span> <span class="comment">// Field name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">10</span>: <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> java.lang.String access$<span class="number">000</span>(com.janwarlen.feature.NestClass$NestClass_2);</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: getfield #<span class="number">1</span> <span class="comment">// Field name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">4</span>: areturn</span><br><span class="line">}</span><br><span class="line">jan<span class="meta">@JanWarlendeMacBook</span>-Pro feature % javap -c NestClass\$InnerClass.<span class="keyword">class</span> </span><br><span class="line"><span class="title class_">Compiled</span> from <span class="string">"NestClass.java"</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">com</span>.janwarlen.feature.NestClass$InnerClass {</span><br><span class="line"> <span class="keyword">final</span> com.janwarlen.feature.NestClass <span class="built_in">this</span>$<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> com.janwarlen.feature.NestClass$InnerClass(com.janwarlen.feature.NestClass);</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: aload_1</span><br><span class="line"> <span class="number">2</span>: putfield #<span class="number">2</span> <span class="comment">// Field this$0:Lcom/janwarlen/feature/NestClass;</span></span><br><span class="line"> <span class="number">5</span>: aload_0</span><br><span class="line"> <span class="number">6</span>: invokespecial #<span class="number">3</span> <span class="comment">// Method java/lang/Object."<init>":()V</span></span><br><span class="line"> <span class="number">9</span>: aload_0</span><br><span class="line"> <span class="number">10</span>: ldc #<span class="number">4</span> <span class="comment">// String InnerClass</span></span><br><span class="line"> <span class="number">12</span>: putfield #<span class="number">1</span> <span class="comment">// Field name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">15</span>: <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> java.lang.String access$<span class="number">100</span>(com.janwarlen.feature.NestClass$InnerClass);</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: getfield #<span class="number">1</span> <span class="comment">// Field name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">4</span>: areturn</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="JDK11"><a href="#JDK11" class="headerlink" title="JDK11"></a>JDK11</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><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></pre></td><td class="code"><pre><span class="line">jan<span class="meta">@JanWarlendeMacBook</span>-Pro feature % javap -c NestClass.<span class="keyword">class</span> </span><br><span class="line"><span class="title class_">Compiled</span> from <span class="string">"NestClass.java"</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">com</span>.janwarlen.feature.NestClass {</span><br><span class="line"> <span class="keyword">public</span> com.janwarlen.feature.NestClass();</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: invokespecial #<span class="number">1</span> <span class="comment">// Method java/lang/Object."<init>":()V</span></span><br><span class="line"> <span class="number">4</span>: <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line">jan<span class="meta">@JanWarlendeMacBook</span>-Pro feature % javap -c NestClass\$NestClass_1.<span class="keyword">class</span> </span><br><span class="line"><span class="title class_">Compiled</span> from <span class="string">"NestClass.java"</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">com</span>.janwarlen.feature.NestClass$NestClass_1 {</span><br><span class="line"> <span class="keyword">public</span> com.janwarlen.feature.NestClass$NestClass_1();</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: invokespecial #<span class="number">1</span> <span class="comment">// Method java/lang/Object."<init>":()V</span></span><br><span class="line"> <span class="number">4</span>: <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">func</span><span class="params">()</span>;</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: <span class="keyword">new</span> #<span class="number">2</span> <span class="comment">// class com/janwarlen/feature/NestClass$NestClass_2</span></span><br><span class="line"> <span class="number">3</span>: dup</span><br><span class="line"> <span class="number">4</span>: invokespecial #<span class="number">3</span> <span class="comment">// Method com/janwarlen/feature/NestClass$NestClass_2."<init>":()V</span></span><br><span class="line"> <span class="number">7</span>: astore_1</span><br><span class="line"> <span class="number">8</span>: getstatic #<span class="number">4</span> <span class="comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span></span><br><span class="line"> <span class="number">11</span>: aload_1</span><br><span class="line"> <span class="number">12</span>: getfield #<span class="number">5</span> <span class="comment">// Field com/janwarlen/feature/NestClass$NestClass_2.name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">15</span>: invokedynamic #<span class="number">6</span>, <span class="number">0</span> <span class="comment">// InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;</span></span><br><span class="line"> <span class="number">20</span>: invokevirtual #<span class="number">7</span> <span class="comment">// Method java/io/PrintStream.println:(Ljava/lang/String;)V</span></span><br><span class="line"> <span class="number">23</span>: <span class="keyword">new</span> #<span class="number">8</span> <span class="comment">// class com/janwarlen/feature/NestClass</span></span><br><span class="line"> <span class="number">26</span>: dup</span><br><span class="line"> <span class="number">27</span>: invokespecial #<span class="number">9</span> <span class="comment">// Method com/janwarlen/feature/NestClass."<init>":()V</span></span><br><span class="line"> <span class="number">30</span>: astore_2</span><br><span class="line"> <span class="number">31</span>: <span class="keyword">new</span> #<span class="number">10</span> <span class="comment">// class com/janwarlen/feature/NestClass$InnerClass</span></span><br><span class="line"> <span class="number">34</span>: dup</span><br><span class="line"> <span class="number">35</span>: aload_2</span><br><span class="line"> <span class="number">36</span>: dup</span><br><span class="line"> <span class="number">37</span>: invokestatic #<span class="number">11</span> <span class="comment">// Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;</span></span><br><span class="line"> <span class="number">40</span>: pop</span><br><span class="line"> <span class="number">41</span>: invokespecial #<span class="number">12</span> <span class="comment">// Method com/janwarlen/feature/NestClass$InnerClass."<init>":(Lcom/janwarlen/feature/NestClass;)V</span></span><br><span class="line"> <span class="number">44</span>: astore_3</span><br><span class="line"> <span class="number">45</span>: getstatic #<span class="number">4</span> <span class="comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span></span><br><span class="line"> <span class="number">48</span>: aload_3</span><br><span class="line"> <span class="number">49</span>: getfield #<span class="number">13</span> <span class="comment">// Field com/janwarlen/feature/NestClass$InnerClass.name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">52</span>: invokedynamic #<span class="number">14</span>, <span class="number">0</span> <span class="comment">// InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;</span></span><br><span class="line"> <span class="number">57</span>: invokevirtual #<span class="number">7</span> <span class="comment">// Method java/io/PrintStream.println:(Ljava/lang/String;)V</span></span><br><span class="line"> <span class="number">60</span>: <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line">jan<span class="meta">@JanWarlendeMacBook</span>-Pro feature % javap -c NestClass\$NestClass_2.<span class="keyword">class</span></span><br><span class="line"><span class="title class_">Compiled</span> from <span class="string">"NestClass.java"</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">com</span>.janwarlen.feature.NestClass$NestClass_2 {</span><br><span class="line"> <span class="keyword">public</span> com.janwarlen.feature.NestClass$NestClass_2();</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: invokespecial #<span class="number">1</span> <span class="comment">// Method java/lang/Object."<init>":()V</span></span><br><span class="line"> <span class="number">4</span>: aload_0</span><br><span class="line"> <span class="number">5</span>: ldc #<span class="number">2</span> <span class="comment">// String NestClass_2</span></span><br><span class="line"> <span class="number">7</span>: putfield #<span class="number">3</span> <span class="comment">// Field name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">10</span>: <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line">jan<span class="meta">@JanWarlendeMacBook</span>-Pro feature % javap -c NestClass\$InnerClass.<span class="keyword">class</span> </span><br><span class="line"><span class="title class_">Compiled</span> from <span class="string">"NestClass.java"</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">com</span>.janwarlen.feature.NestClass$InnerClass {</span><br><span class="line"> <span class="keyword">final</span> com.janwarlen.feature.NestClass <span class="built_in">this</span>$<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> com.janwarlen.feature.NestClass$InnerClass(com.janwarlen.feature.NestClass);</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: aload_1</span><br><span class="line"> <span class="number">2</span>: putfield #<span class="number">1</span> <span class="comment">// Field this$0:Lcom/janwarlen/feature/NestClass;</span></span><br><span class="line"> <span class="number">5</span>: aload_0</span><br><span class="line"> <span class="number">6</span>: invokespecial #<span class="number">2</span> <span class="comment">// Method java/lang/Object."<init>":()V</span></span><br><span class="line"> <span class="number">9</span>: aload_0</span><br><span class="line"> <span class="number">10</span>: ldc #<span class="number">3</span> <span class="comment">// String InnerClass</span></span><br><span class="line"> <span class="number">12</span>: putfield #<span class="number">4</span> <span class="comment">// Field name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">15</span>: <span class="keyword">return</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="对比解读"><a href="#对比解读" class="headerlink" title="对比解读"></a><span id="对比解读">对比解读</span></h3><h4 id="EnclosingMethod"><a href="#EnclosingMethod" class="headerlink" title="EnclosingMethod"></a><code>EnclosingMethod</code></h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> com.janwarlen.feature.NestClass$InnerClass(com.janwarlen.feature.NestClass);</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: aload_1</span><br><span class="line"> <span class="number">2</span>: putfield #<span class="number">2</span> <span class="comment">// Field this$0:Lcom/janwarlen/feature/NestClass;</span></span><br><span class="line"> <span class="number">5</span>: aload_0</span><br><span class="line"> <span class="number">6</span>: invokespecial #<span class="number">3</span> <span class="comment">// Method java/lang/Object."<init>":()V</span></span><br><span class="line"> <span class="number">9</span>: aload_0</span><br><span class="line"> <span class="number">10</span>: ldc #<span class="number">4</span> <span class="comment">// String InnerClass</span></span><br><span class="line"> <span class="number">12</span>: putfield #<span class="number">1</span> <span class="comment">// Field name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">15</span>: <span class="keyword">return</span></span><br></pre></td></tr></table></figure><p>JDK8和JDK11反编译结果中均有此代码,但是源码中只有一个属性,因此我推测这个就是所谓的<code>EnclosingMethod</code>(笔者此时还不了解闭包),而内部类的特殊创建方式估计也是与此有关。需要注意的是<code>Nest Class</code>是没有该代码的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">NestClass</span> <span class="variable">nestClass</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NestClass</span>();</span><br><span class="line"><span class="type">InnerClass</span> <span class="variable">i1</span> <span class="operator">=</span> nestClass.<span class="keyword">new</span> <span class="title class_">InnerClass</span>();</span><br></pre></td></tr></table></figure><h4 id="accessibility-broadening-bridge-methods"><a href="#accessibility-broadening-bridge-methods" class="headerlink" title="accessibility-broadening bridge methods"></a><code>accessibility-broadening bridge methods</code></h4><p>因为在<code>nest class</code> <code>NestClass_1</code>中我们使用了<code>inner class</code>的私有属性,因此,在反编译的结果中,我们看到了JDK8和JDK11的不同之处</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> java.lang.String access$<span class="number">100</span>(com.janwarlen.feature.NestClass$InnerClass);</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: aload_0</span><br><span class="line"> <span class="number">1</span>: getfield #<span class="number">1</span> <span class="comment">// Field name:Ljava/lang/String;</span></span><br><span class="line"> <span class="number">4</span>: areturn</span><br></pre></td></tr></table></figure><p>上述汇编代码在<code>JDK11</code>的反编译结果中不存在,需要明白这个编码的作用我们需要再看调用方的反编译汇编码。</p><h5 id="调用源码"><a href="#调用源码" class="headerlink" title="调用源码"></a>调用源码</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"NestClass_1:func:InnerClass:name:"</span> + i1.name);</span><br></pre></td></tr></table></figure><h5 id="JDK8调用汇编码"><a href="#JDK8调用汇编码" class="headerlink" title="JDK8调用汇编码"></a>JDK8调用汇编码</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 部分节选</span></span><br><span class="line"> <span class="number">58</span>: getstatic #<span class="number">4</span> <span class="comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span></span><br><span class="line"> <span class="number">61</span>: <span class="keyword">new</span> #<span class="number">5</span> <span class="comment">// class java/lang/StringBuilder</span></span><br><span class="line"> <span class="number">64</span>: dup</span><br><span class="line"> <span class="number">65</span>: invokespecial #<span class="number">6</span> <span class="comment">// Method java/lang/StringBuilder."<init>":()V</span></span><br><span class="line"> <span class="number">68</span>: ldc #<span class="number">17</span> <span class="comment">// String NestClass_1:func:InnerClass:name:</span></span><br><span class="line"> <span class="number">70</span>: invokevirtual #<span class="number">8</span> <span class="comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span></span><br><span class="line"> <span class="number">73</span>: aload_3</span><br><span class="line"> <span class="number">74</span>: invokestatic #<span class="number">18</span> <span class="comment">// Method com/janwarlen/feature/NestClass$InnerClass.access$100:(Lcom/janwarlen/feature/NestClass$InnerClass;)Ljava/lang/String;</span></span><br><span class="line"> <span class="number">77</span>: invokevirtual #<span class="number">8</span> <span class="comment">// Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;</span></span><br><span class="line"> <span class="number">80</span>: invokevirtual #<span class="number">10</span> <span class="comment">// Method java/lang/StringBuilder.toString:()Ljava/lang/String;</span></span><br><span class="line"> <span class="number">83</span>: invokevirtual #<span class="number">11</span> <span class="comment">// Method java/io/PrintStream.println:(Ljava/lang/String;)V</span></span><br></pre></td></tr></table></figure><h5 id="JDK11调用汇编码"><a href="#JDK11调用汇编码" class="headerlink" title="JDK11调用汇编码"></a>JDK11调用汇编码</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">45</span>: getstatic #<span class="number">4</span> <span class="comment">// Field java/lang/System.out:Ljava/io/PrintStream;</span></span><br><span class="line"><span class="number">48</span>: aload_3</span><br><span class="line"><span class="number">49</span>: getfield #<span class="number">13</span> <span class="comment">// Field com/janwarlen/feature/NestClass$InnerClass.name:Ljava/lang/String;</span></span><br><span class="line"><span class="number">52</span>: invokedynamic #<span class="number">14</span>, <span class="number">0</span> <span class="comment">// InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;</span></span><br><span class="line"><span class="number">57</span>: invokevirtual #<span class="number">7</span> <span class="comment">// Method java/io/PrintStream.println:(Ljava/lang/String;)V</span></span><br></pre></td></tr></table></figure><h4 id="accessibility-broadening-bridge-methods续"><a href="#accessibility-broadening-bridge-methods续" class="headerlink" title="accessibility-broadening bridge methods续"></a><code>accessibility-broadening bridge methods</code>续</h4><p>观察上方两者汇编码,我们可以看出在JDK11中字符串的拼接也进行了优化,此处先不赘述,我们单独看变量使用,JDK8是通过<code>Method com/janwarlen/feature/NestClass$InnerClass.access$100</code>去获得私有属性的值,而JDK11则是直接去获取了属性,这样,突然就明白了为什么说是<code>bridge methods</code>了,并且这样对比,发现汇编的指令码也少了很多,这样对于JVM的性能提升也是有帮助的。</p><h3 id="挖坑"><a href="#挖坑" class="headerlink" title="挖坑"></a>挖坑</h3><p>期待后续有机会回填,以下的优化点实在是难以通过代码直观查看,因此只好暂时先跳过,也许以后实力见长,可以去深入JVM内部去查看对比,届时再返回填土….</p><ol><li>JVM Access Control for Nestmates</li><li>Nest Membership Validation</li></ol><h1 id="常量池添加新类型CONSTANT-Dynamic"><a href="#常量池添加新类型CONSTANT-Dynamic" class="headerlink" title="常量池添加新类型CONSTANT_Dynamic"></a>常量池添加新类型<code>CONSTANT_Dynamic</code></h1><p>官方的JEP原文看的头痛欲裂,和上一个一样,概念性居多,整篇看下来感觉添加这个是因为本次JDK更新的一些内容和指令<code>invokedynamic</code>经常起冲突导致工作量增多而进行的调整,也算是为后续的工作减少工作量<code>improving program performance and simplifying compiler logic</code>。<br>浏览<a href="https://www.techwell.com/techwell-insights/2021/08/new-jvm-features-jdk-11">new-jvm-features-jdk-11</a>和<a href="https://www.javacodegeeks.com/2018/08/hands-on-java-constantdynamic.html">hands-on-java-constantdynamic</a>之后,感觉<code>java agent</code>领域的同学可能需要更多的关注这个,可能需要深入研究,尤其是<code>hands-on-java-constantdynamic</code>中有这么一段话(虽然目前并不能想到使用场景以及使用方式)</p><blockquote><p>The constantdynamic feature can also be useful to Java agents that often need to enhance existing classes with additional information. Java agents cannot normally alter a classes by for example adding static fields as this can both interfere with reflection-based frameworks and since class format changes are forbidden on most JVMs when redefining an already loaded class. Neither restriction does however apply to dynamic constants that are added during runtime where a Java agent can now easily tag classes with additional information.</p></blockquote><h1 id="针对AArch64处理器优化部分函数"><a href="#针对AArch64处理器优化部分函数" class="headerlink" title="针对AArch64处理器优化部分函数"></a>针对AArch64处理器优化部分函数</h1><p>该特性是硬件适配内容,如果使用的服务器有涉及AArch64处理器,建议增加额外的性能测试,避免未知问题,因为原文中有如下一段话,大致意思就是官方性能测试并未覆盖所有<code>AArch64</code>架构,相当一部分还是依靠OpenJDK社区反馈,因此需要额外注意。</p><blockquote><p>It is not possible to perform testing and performance measurements on all AArch64 hardware variants. We will rely on the OpenJDK Community to perform testing on hardware we currently do not have in-house should they find it necessary when patches are submitted for review.</p></blockquote><h1 id="新GC:Epsilon-GC(实验性)"><a href="#新GC:Epsilon-GC(实验性)" class="headerlink" title="新GC:Epsilon GC(实验性)"></a>新GC:Epsilon GC(实验性)</h1><blockquote><p>Develop a GC that handles memory allocation but does not implement any actual memory reclamation mechanism. Once the available Java heap is exhausted, the JVM will shut down.</p></blockquote><p>官方文档原文明确指出该GC只处理内存分配,不进行任何内存回收动作(那还叫啥GC…),并且一旦堆内存耗尽,JVM将GG。<br>使用场景官方文档给出了几个用途:</p><ol><li>做其他GC的性能测试背景板(对比)</li><li>内存压力测试,应该是一些特殊应用可以用到,测试出内存使用的上限,方便启动参数进行内存分配</li><li>声明周期极短的应用,一些应用可以通过进程的消亡快速的释放资源,该场景下GC的内存回收则显得多此一举(不确定理解的是否正确)</li><li>…</li></ol><h1 id="删除-Java-EE-和-CORBA-模块"><a href="#删除-Java-EE-和-CORBA-模块" class="headerlink" title="删除 Java EE 和 CORBA 模块"></a>删除 Java EE 和 CORBA 模块</h1><p>JDK9中声明过期的模块,本次进行了大范围的删除。如果有相关代码,版本迁移需要额外注意。</p><h1 id="对-HTTP-客户端-API-进行标准化"><a href="#对-HTTP-客户端-API-进行标准化" class="headerlink" title="对 HTTP 客户端 API 进行标准化"></a>对 HTTP 客户端 API 进行标准化</h1><p>有部分类和方法存在删除,版本迁移需要额外注意。</p><h1 id="Lambda-参数的局部变量语法"><a href="#Lambda-参数的局部变量语法" class="headerlink" title="Lambda 参数的局部变量语法"></a>Lambda 参数的局部变量语法</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(<span class="keyword">var</span> x, <span class="keyword">var</span> y) -> x.process(y)</span><br><span class="line">(x, y) -> x.process(y)</span><br></pre></td></tr></table></figure><p>em…可能是为了补充之前lambda不用声明类型的”漏洞”?不使用<code>var</code>也没有什么问题,使用<code>var</code>还必须都使用,不能有入参无类型声明或指明类型,以下为错误用法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">(<span class="keyword">var</span> x, y) -> x.process(y) <span class="comment">// Cannot mix 'var' and 'no var' in implicitly typed lambda expression</span></span><br><span class="line">(<span class="keyword">var</span> x, <span class="type">int</span> y) -> x.process(y) <span class="comment">// Cannot mix 'var' and manifest types in explicitly typed lambda expression</span></span><br></pre></td></tr></table></figure><h1 id="拓展两个加密算法实现-Curve25519-和-Curve448"><a href="#拓展两个加密算法实现-Curve25519-和-Curve448" class="headerlink" title="拓展两个加密算法实现(Curve25519 和 Curve448)"></a>拓展两个加密算法实现(Curve25519 和 Curve448)</h1><p>笔者未涉足过加密算法领域,不太清楚这两个拓展的具体含义,但是根据官方的文档和其他博主的分享,应该是更安全更高效的算法。<br>官方示例代码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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 class="type">KeyPairGenerator</span> <span class="variable">kpg</span> <span class="operator">=</span> KeyPairGenerator.getInstance(<span class="string">"XDH"</span>);</span><br><span class="line"><span class="type">NamedParameterSpec</span> <span class="variable">paramSpec</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NamedParameterSpec</span>(<span class="string">"X25519"</span>);</span><br><span class="line">kpg.initialize(paramSpec); <span class="comment">// equivalent to kpg.initialize(255)</span></span><br><span class="line"><span class="comment">// alternatively: kpg = KeyPairGenerator.getInstance("X25519")</span></span><br><span class="line"><span class="type">KeyPair</span> <span class="variable">kp</span> <span class="operator">=</span> kpg.generateKeyPair();</span><br><span class="line"></span><br><span class="line"><span class="type">KeyFactory</span> <span class="variable">kf</span> <span class="operator">=</span> KeyFactory.getInstance(<span class="string">"XDH"</span>);</span><br><span class="line"><span class="type">BigInteger</span> <span class="variable">u</span> <span class="operator">=</span> ...</span><br><span class="line"><span class="type">XECPublicKeySpec</span> <span class="variable">pubSpec</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XECPublicKeySpec</span>(paramSpec, u);</span><br><span class="line"><span class="type">PublicKey</span> <span class="variable">pubKey</span> <span class="operator">=</span> kf.generatePublic(pubSpec);</span><br><span class="line"></span><br><span class="line"><span class="type">KeyAgreement</span> <span class="variable">ka</span> <span class="operator">=</span> KeyAgreement.getInstance(<span class="string">"XDH"</span>);</span><br><span class="line">ka.init(kp.getPrivate());</span><br><span class="line">ka.doPhase(pubKey, <span class="literal">true</span>);</span><br><span class="line"><span class="type">byte</span>[] secret = ka.generateSecret();</span><br></pre></td></tr></table></figure><h1 id="Unicode-10持续更新"><a href="#Unicode-10持续更新" class="headerlink" title="Unicode 10持续更新"></a>Unicode 10持续更新</h1><p>支持到JDK11发布时的Unicode 10,下列四个未涉及:</p><ol><li>UTS #10,Unicode 排序算法</li><li>UTS #39,Unicode 安全机制</li><li>UTS #46,Unicode IDNA 兼容性处理</li><li>UTS #51,Unicode 表情符号</li></ol><h1 id="Flight-Recorder-飞行记录仪"><a href="#Flight-Recorder-飞行记录仪" class="headerlink" title="Flight Recorder(飞行记录仪)"></a>Flight Recorder(飞行记录仪)</h1><p>好用的性能分析工具,官方说不会超过1%的性能消耗,看起来是可以在生产环境中使用的,但实际还是慎重,除非特殊情况,临时需要该工具进行问题分析,否则不建议常驻。<code>-XX:StartFlightRecording</code>启动参数开启。<br>读取jmr文件工具:<a href="https://github.com/openjdk/jmc">jmc</a></p><h1 id="拓展新的加密算法实现-ChaCha20-和-Poly1305"><a href="#拓展新的加密算法实现-ChaCha20-和-Poly1305" class="headerlink" title="拓展新的加密算法实现(ChaCha20 和 Poly1305)"></a>拓展新的加密算法实现(ChaCha20 和 Poly1305)</h1><p>这两个加密算法已经是广泛使用,JDK11的该更新算是”紧跟潮流”。</p><h1 id="启动单文件源代码程序"><a href="#启动单文件源代码程序" class="headerlink" title="启动单文件源代码程序"></a>启动单文件源代码程序</h1><p>看起来和JShell一样,降低部分场景下的Java使用成本。除了一些特殊的小型应用使用场景,不建议使用该功能,哪怕是在学习Java的前期。</p><h1 id="低开销堆内存分配管理"><a href="#低开销堆内存分配管理" class="headerlink" title="低开销堆内存分配管理"></a>低开销堆内存分配管理</h1><p>对于堆内存管理的一个优化方案,甚至可以查看Java对象的存活信息。</p><h1 id="传输层安全性-TLS-升至1-3"><a href="#传输层安全性-TLS-升至1-3" class="headerlink" title="传输层安全性 (TLS)升至1.3"></a>传输层安全性 (TLS)升至1.3</h1><p>对之前的版本如1.2存在兼容性风险,建议全局统一升级。</p><h1 id="新GC:ZGC(实验性)"><a href="#新GC:ZGC(实验性)" class="headerlink" title="新GC:ZGC(实验性)"></a>新GC:ZGC(实验性)</h1><ol><li>GC停顿时间优化效果明显</li><li>GC停顿时间不会因堆大小有明显变化</li><li>JDK11不可以在生产环境中使用</li></ol><h1 id="弃用-Nashorn-JavaScript-引擎"><a href="#弃用-Nashorn-JavaScript-引擎" class="headerlink" title="弃用 Nashorn JavaScript 引擎"></a>弃用 Nashorn JavaScript 引擎</h1><p>未删除,仅通过<code>@Deprecated(forRemoval=true)</code>标记,未来版本可能会删除。</p><h1 id="弃用-Pack200-工具和-API"><a href="#弃用-Pack200-工具和-API" class="headerlink" title="弃用 Pack200 工具和 API"></a>弃用 Pack200 工具和 API</h1><p>和Java应用程序打包有关,属于压缩工具。一句话总结就是跟不上时代的脚步而被抛弃,JDK11仅标记<code>@Deprecated(forRemoval=true)</code>。</p>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://openjdk.org/projects/jdk/11/">jdk11特性列表</a></li>
<li><a href="https://www.bbsmax.com/A/QW5Ybg23dm/">nest class和inner class的区别</a></li>
<li><a href="https://cloud.tencent.com/developer/article/1165256">JDK11中增加了一个常量池类型:CONSTANT_Dynamic</a></li>
<li><a href="https://www.techwell.com/techwell-insights/2021/08/new-jvm-features-jdk-11">new-jvm-features-jdk-11</a></li>
<li><a href="https://www.javacodegeeks.com/2018/08/hands-on-java-constantdynamic.html">hands-on-java-constantdynamic</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>直接影响编码的功能几乎没有,大多数都是幕后工作的更新</li>
<li>ZGC是一个核心内容,不过JDK11中是实验版</li>
<li>JFR的加入方便了性能监控,建议上手实际操作几次,非常好用</li>
<li>加密算法的几个新增实现未进行深入了解(未涉足领域,功力不足)</summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK11" scheme="http://janwarlen.com/tags/JDK11/"/>
</entry>
<entry>
<title>Java版本特性-JDK10</title>
<link href="http://janwarlen.com/2022/06/16/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK10/"/>
<id>http://janwarlen.com/2022/06/16/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK10/</id>
<published>2022-06-15T19:38:41.000Z</published>
<updated>2022-09-02T02:49:26.410Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://openjdk.org/projects/jdk/10/">JDK10特性列表</a></li><li><a href="https://shipilev.net/jvm/anatomy-quarks/16-megamorphic-virtual-calls/">megamorphic-virtual-calls</a></li><li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">class-data-sharing</a></li><li><a href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javah.html">javah</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>JDK10的更新内容不多,核心就是<code>局部变量类型推断</code></li><li><code>Heap Allocation on Alternative Memory Devices</code>可能在未来会发挥相当重要的作用,不过是在运维层面了,是和机器的物理架构相结合的技术</li><li>有一些面对运行性能的优化,比如<code>G1的全并行</code>、<code>Thread-Local Handshakes</code>和<code>基于 Java 的 JIT 编译器</code>,分别针对垃圾回收、多线程场景、代码编译,并且这几项技术启用需要做额外的性能测试,以确认是否在生产环境使用</li></ol><span id="more"></span><h1 id="局部变量类型推断"><a href="#局部变量类型推断" class="headerlink" title="局部变量类型推断"></a>局部变量类型推断</h1><p>简单来说就是在部分场景里,降低了对类型的关注度,减轻编码负担</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">var</span> <span class="variable">list</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span><String>(); <span class="comment">// infers ArrayList<String></span></span><br><span class="line"><span class="type">var</span> <span class="variable">stream</span> <span class="operator">=</span> list.stream(); <span class="comment">// infers Stream<String></span></span><br></pre></td></tr></table></figure><h2 id="局限"><a href="#局限" class="headerlink" title="局限"></a>局限</h2><ol><li>var声明对象必须初始化,初始化也不可以为null<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"><span class="comment">// Cannot infer type: 'var' on variable without initializer</span></span><br><span class="line"><span class="keyword">var</span> a;</span><br><span class="line"><span class="comment">// Cannot infer type: variable initializer is 'null'</span></span><br><span class="line"><span class="type">var</span> <span class="variable">b</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br></pre></td></tr></table></figure></li><li>var声明对象不可接收lambda表达式<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Cannot infer type: lambda expression requires an explicit target type</span></span><br><span class="line"><span class="type">var</span> <span class="variable">c</span> <span class="operator">=</span> () -> <span class="number">1</span>;</span><br></pre></td></tr></table></figure></li><li>var声明对象初始化数组时必须指定数组类型<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"><span class="comment">// 编译器会直接异常提示</span></span><br><span class="line"><span class="comment">// var k = { 1 , 2 };</span></span><br><span class="line"><span class="type">var</span> <span class="variable">k</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">int</span>[]{<span class="number">1</span>, <span class="number">2</span>};</span><br></pre></td></tr></table></figure></li></ol><h1 id="JDK源码整合为一个工程"><a href="#JDK源码整合为一个工程" class="headerlink" title="JDK源码整合为一个工程"></a>JDK源码整合为一个工程</h1><p>从<a href="https://openjdk.org/jeps/296">特性详情页</a>来看,该特性主要是为了bug修复方便,因为多工程模式下,存在部分bug涉及多工程,这样bug的修复提交就不是原子性的了,工程合并之后,bug修复提交就是原子性操作。</p><figure class="highlight plaintext"><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">$ROOT/jdk/src/java.base</span><br><span class="line">...</span><br><span class="line">$ROOT/langtools/src/java.compiler</span><br><span class="line">...</span><br><span class="line">变更为</span><br><span class="line">$ROOT/src/java.base</span><br><span class="line">$ROOT/src/java.compiler</span><br><span class="line">...</span><br></pre></td></tr></table></figure><h1 id="垃圾收集器接口"><a href="#垃圾收集器接口" class="headerlink" title="垃圾收集器接口"></a>垃圾收集器接口</h1><p>虚拟机内部的代码重构,对外部不影响,方便JDK的内部GC开发人员扩展垃圾收集器和虚拟机开发人员更容易理解垃圾收集器相关代码(容易找到,之前的代码是散落的,甚至部分由if-else维护),不过以下这段原文可能需要额外注意</p><blockquote><p>There is a risk that performance could be harmed, for example if additional virtual calls would be introduced. This risk can be mitigated by continuous performance testing.</p></blockquote><p>大致意思是性能可能会受到影响,并用<code>virtual calls</code>的额外调用举例。因此JDK10+版本需要多做性能测试,以避免该影响。关于<code>virtual call</code>可参考<a href="https://shipilev.net/jvm/anatomy-quarks/16-megamorphic-virtual-calls/">megamorphic-virtual-calls</a>,大体场景可以理解为父类声明变量接收子类对象,调用函数时,因是在运行时确定,该情况可以称作<code>virtual call</code>。(主要还是看编译后的字节码,反编译为汇编时,看到指令<code>invokevirtual</code>基本就是了)</p><h1 id="G1的GC全并行"><a href="#G1的GC全并行" class="headerlink" title="G1的GC全并行"></a>G1的GC全并行</h1><p>万恶的JDK9将G1设置为默认垃圾收集器的尾声,在JDK10之前,<code>mark-sweep-compact</code>的算法实现(<code>full GC</code>场景)采取的单线程,在JDK10中,该实现修改为多线程实现,线程数与年轻代和混合代(?Mixed)收集线程数一致,并且该线程数可通过启动参数<code>-XX:ParallelGCThreads</code>指定。<br>但是原文中有这么一句话</p><blockquote><p>The fact that G1 uses regions will most likely lead to more wasted space after a parallel full GC than for a single threaded one.</p></blockquote><p>具体没说是怎么样的浪费空间,内存是一定回收了的,我推测是并行可能会导致空间碎片比较多,影响后续内存使用</p><h1 id="CDS-Class-Data-Sharing-的应用级拓展"><a href="#CDS-Class-Data-Sharing-的应用级拓展" class="headerlink" title="CDS(Class-Data Sharing)的应用级拓展"></a>CDS(Class-Data Sharing)的应用级拓展</h1><p>CDS(Class-Data Sharing)在JDK5中引入(感兴趣可以研究<a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">class-data-sharing</a>原文)<br>在JDK10中将JDK内部的CDS拓展到应用级别,可以指定类到归档文件(<code>archive file</code>),可以有效的减少应用启动时间和内存消耗。但是根据原文描述,个人理解该技术仅限于多个Java应用程序在同一个系统镜像内运行,才可以有效的发挥作用,对于目前大多数公司的实际运行环境(一个应用一个镜像)来说,该技术没有什么实际意义。<br>JDK8/JDK9已经有APPCDS的实现,因此在长期的测试中,基本不需要考虑风险和稳定性问题….</p><h1 id="Thread-Local-Handshakes"><a href="#Thread-Local-Handshakes" class="headerlink" title="Thread-Local Handshakes"></a>Thread-Local Handshakes</h1><p>实在不知道怎么翻译,用了原文,该技术是想通过减少全局安全点从而降低JVM的延迟,尤其是在GC领域。<br>没说具体实现,但在偏向锁、堆栈追踪、G1有良好的优化功能</p><h1 id="删除了javah"><a href="#删除了javah" class="headerlink" title="删除了javah"></a>删除了<code>javah</code></h1><p>功能被JDK8的javac的拓展功能所取代,在JDK10中才删除<br><a href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javah.html"><code>javah</code></a>就是当java代码调用native方法(C/C++本地实现)时,生成对应的文件头,方便JVM运行时调用<br>应该是安卓场景会用到</p><h1 id="Unicode拓展"><a href="#Unicode拓展" class="headerlink" title="Unicode拓展"></a>Unicode拓展</h1><p>增强了类<code>java.util.Locale</code>及相关API拓展支持<a href="https://www.rfc-editor.org/rfc/bcp/bcp47.txt">bcp47</a>.<br>JDK7就已经在日期和数字上针对<code>bcp 47</code>进行了补充实现,在JDK10中进行了更完整的拓展实现。</p><blockquote><p>An IETF BCP 47 language tag is a standardized code or tag that is used to identify human languages in the Internet.<br> ——-wiki</p></blockquote><p>从wiki的这段话来理解就是各国语言的标识,在国际化场景中会有重要的作用,例如安卓…..</p><h1 id="Heap-Allocation-on-Alternative-Memory-Devices"><a href="#Heap-Allocation-on-Alternative-Memory-Devices" class="headerlink" title="Heap Allocation on Alternative Memory Devices"></a>Heap Allocation on Alternative Memory Devices</h1><p>痛苦,没办法精确的翻译,还没确定<code>Alternative Memory Devices</code>指代的是什么,从原文的理解来说,应该是一台物理机器有多个内存系统,尤其是针对<code>non-DRAM</code>内存系统,可以通过指令<code>-XX:AllocateHeapAt=<path></code>去辅助JVM在这种类型的内存上进行堆分配。<br>这应该是一项未来将会发挥很大作用的功能,尤其是在简单了解<code>NV-DIMM</code>之后。这算是硬件适配。</p><h1 id="基于-Java-的-JIT-编译器"><a href="#基于-Java-的-JIT-编译器" class="headerlink" title="基于 Java 的 JIT 编译器"></a>基于 Java 的 JIT 编译器</h1><p>可以预先把Java代码编译为本地代码,以提高运行效率。通过<code>-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler</code>开启。<br>存在以下风险:</p><ol><li>启动会变慢</li><li>堆的使用将会难以评估<br>需要额外的性能测试指标去覆盖,以确认是否需要开启该功能。</li></ol><h1 id="根证书"><a href="#根证书" class="headerlink" title="根证书"></a>根证书</h1><p>为了推广<code>OpenJDK</code>和降低<code>OpenJDK</code>与<code>Oracle JDK</code>差异的。</p><h1 id="基于时间的发布版本控制"><a href="#基于时间的发布版本控制" class="headerlink" title="基于时间的发布版本控制"></a>基于时间的发布版本控制</h1><p>新的JDK版本号规则,影响不大,直观影响就是<code>java -v</code>显示信息会和以往不同…</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$FEATURE.$INTERIM.$UPDATE.$PATCH</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://openjdk.org/projects/jdk/10/">JDK10特性列表</a></li>
<li><a href="https://shipilev.net/jvm/anatomy-quarks/16-megamorphic-virtual-calls/">megamorphic-virtual-calls</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html">class-data-sharing</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javah.html">javah</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>JDK10的更新内容不多,核心就是<code>局部变量类型推断</code></li>
<li><code>Heap Allocation on Alternative Memory Devices</code>可能在未来会发挥相当重要的作用,不过是在运维层面了,是和机器的物理架构相结合的技术</li>
<li>有一些面对运行性能的优化,比如<code>G1的全并行</code>、<code>Thread-Local Handshakes</code>和<code>基于 Java 的 JIT 编译器</code>,分别针对垃圾回收、多线程场景、代码编译,并且这几项技术启用需要做额外的性能测试,以确认是否在生产环境使用</li>
</ol></summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK10" scheme="http://janwarlen.com/tags/JDK10/"/>
</entry>
<entry>
<title>Java版本特性-JDK9</title>
<link href="http://janwarlen.com/2022/06/11/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK9/"/>
<id>http://janwarlen.com/2022/06/11/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-JDK9/</id>
<published>2022-06-11T10:05:32.000Z</published>
<updated>2022-09-02T02:49:26.659Z</updated>
<content type="html"><![CDATA[<h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol><li><a href="https://docs.oracle.com/javase/9/">Oracle Java9官方文档</a></li><li><a href="https://www.oracle.com/java/technologies/javase/9-relnotes.html">JDK 9 Release Notes</a></li><li><a href="http://openjdk.java.net/projects/jigsaw/quick-start">Module System Quick-Start Guide</a></li><li><a href="https://docs.oracle.com/javase/9/migrate/toc.htm#JSMIG-GUID-7744EF96-5899-4FB2-B34E-86D49B2E89B6">JDK9迁移指南</a></li><li><a href="http://openjdk.java.net/jeps/213">Milling Project Coin</a></li><li><a href="http://openjdk.java.net/jeps/282">jlink: The Java Linker</a></li><li><a href="http://openjdk.java.net/jeps/222">The Java Shell</a></li></ol><h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol><li>本文仅涉及部分JDK9变更,完整可查看官方文档<a href="https://docs.oracle.com/javase/9/whatsnew/toc.htm">Standard Edition What’s New in Oracle JDK 9</a></li><li>除了模块化,其他改动对日常开发影响不多,可以着重关注模块化和接口私有函数</li><li>在后续的学习中发现,在模块化后,类似于<code>ServiceLoader.load</code>的类加载的方法都有变动,大多数都和模块之间的类可视化有关,如果是从低版本迁移,需要额外注意</li></ol><span id="more"></span><h1 id="模块化系统"><a href="#模块化系统" class="headerlink" title="模块化系统"></a>模块化系统</h1><h2 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h2><h3 id="ModularDemo"><a href="#ModularDemo" class="headerlink" title="ModularDemo"></a><a href="https://github.com/JanWarlen/ModularDemo">ModularDemo</a></h3><p>因目前Oracle开放的JDK下载版本仅有8/11/17/18,因此该项目通过JDK18编译JDK9(Project language level设置为9)模拟</p><h4 id="工程结构"><a href="#工程结构" class="headerlink" title="工程结构"></a>工程结构</h4><p><img src="http://img.janwarlen.com/blog/Modular-%E5%B7%A5%E7%A8%8B%E7%BB%93%E6%9E%84.png" alt="Modular"></p><h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><ol><li><code>common</code>与<code>service</code>是在 Maven 层面上的子模块,JDK9的模块化主要通过类<code>module-info.java</code></li><li>每个工程(模块)仅可有一个<code>module-info.java</code></li><li>对外开放的最小粒度是<code>package</code>,无法以类作为最小单位,如果有类不愿对外开放,建议迁移到单独的包中</li><li>打包后,虽然<code>common</code>没有开放<code>intern</code>包,但是最终common.jar中还是会有<code>intern</code>中的类</li><li>如果类不惜那个对外开放,但是其中部分功能还需要对外使用,可以单独在对外开放的包中新建一个类,作为转发调用</li><li>从初步的使用感觉,目前仅在权限控制方面有明显的作用</li><li><code>maven-compiler-plugin</code>仅在<code>3.8.0+</code>才支持<code>module</code>(可查看<code>ModularDemo/pom.xml</code>)</li></ol><h1 id="jlink"><a href="#jlink" class="headerlink" title="jlink"></a>jlink</h1><p>可以自定义Java程序运行时的JRE环境</p><h2 id="命令使用"><a href="#命令使用" class="headerlink" title="命令使用"></a>命令使用</h2><figure class="highlight shell"><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">jlink --module-path <modulepath> --add-modules <modules> --limit-modules <modules> --output <path></span><br><span class="line">module-path: 指的是你自定义的JRE环境所有需要的module所处的路径</span><br><span class="line">add-modules: 在 module-path 中,你需要那些module</span><br><span class="line">limit-modules: 根据module名称限制搜寻范围</span><br><span class="line">output: 自定义JRE的输出路径</span><br><span class="line"></span><br><span class="line">jlink --output service/src/main/resources/jre --module-path "service/src/main/resources" --add-modules common</span><br><span class="line">我将入门案例中的common的jar包拷贝到service/src/main/resources中,因此 module-path 就是 "service/src/main/resources",而因 service 仅需要 common 一个模块,因此我只添加了 common</span><br></pre></td></tr></table></figure><h2 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h2><ol><li>自定义的JRE环境没有无关的jar包,因此启动内存消耗会少<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">System.out.println(<span class="string">"totalMemory:"</span> + Runtime.getRuntime().totalMemory()/ (<span class="number">1024</span> * <span class="number">1024</span>));</span><br><span class="line">System.out.println(<span class="string">"freeMemory:"</span> + Runtime.getRuntime().freeMemory()/ (<span class="number">1024</span> * <span class="number">1024</span>));</span><br><span class="line">System.out.println(<span class="string">"maxMemory:"</span> + Runtime.getRuntime().maxMemory()/ (<span class="number">1024</span> * <span class="number">1024</span>));</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><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">(base) MacBook-Pro ModularDemo % ./service/src/main/resources/jre/bin/java -jar service/target/service-1.0-SNAPSHOT.jar</span><br><span class="line">hello</span><br><span class="line">this is private class.</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">false</span><br><span class="line">totalMemory:512</span><br><span class="line">freeMemory:510</span><br><span class="line">maxMemory:8192</span><br><span class="line">(base) MacBook-Pro ModularDemo % java -jar service/target/service-1.0-SNAPSHOT.jar </span><br><span class="line">hello</span><br><span class="line">this is private class.</span><br><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">false</span><br><span class="line">totalMemory:520</span><br><span class="line">freeMemory:514</span><br><span class="line">maxMemory:8192</span><br></pre></td></tr></table></figure></li><li>可以根据应用自定义不同的JRE,而不用将所有 module 对全部应用开放</li></ol><h2 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h2><ol><li>初步感受,该功能在嵌入式领域可以发挥非常重要的作用,在服务器端程序,作用可能没有那么明显</li><li>最好可以搭配<a href="http://openjdk.java.net/jeps/200">The Modular JDK</a>、<a href="http://openjdk.java.net/jeps/201">Modular Source Code</a>、<a href="http://openjdk.java.net/jeps/220">Modular Run-Time Images</a>一起使用,效果更佳(此处埋坑,以后有时间再回头来研究这三个)</li></ol><h1 id="正则-调整作用范围"><a href="#正则-调整作用范围" class="headerlink" title="正则^调整作用范围"></a>正则<code>^</code>调整作用范围</h1><p>将作用到整个表达式,而不是第一个group</p><h2 id="案例-1"><a href="#案例-1" class="headerlink" title="案例"></a>案例</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Pattern</span> <span class="variable">compile</span> <span class="operator">=</span> Pattern.compile(<span class="string">"[^a-b[c-d]e-f]"</span>);</span><br><span class="line"><span class="type">Matcher</span> <span class="variable">a</span> <span class="operator">=</span> compile.matcher(<span class="string">"a"</span>);</span><br><span class="line"><span class="type">Matcher</span> <span class="variable">c</span> <span class="operator">=</span> compile.matcher(<span class="string">"c"</span>);</span><br><span class="line"><span class="comment">// JDK8 false JDK9 false</span></span><br><span class="line">System.out.println(a.matches());</span><br><span class="line"><span class="comment">// JDK8 true JDK9 false</span></span><br><span class="line">System.out.println(c.matches());</span><br></pre></td></tr></table></figure><h1 id="try-with-resources"><a href="#try-with-resources" class="headerlink" title="try-with-resources"></a>try-with-resources</h1><p>可以在try外部声明变量,将变量名放在try中即可,但是该变量受<code>final or effectively final</code>规则限制,即在初始化完成后不可再次进行赋值操作;</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">InputStream</span> <span class="variable">in</span> <span class="operator">=</span> Files.newInputStream(Paths.get(<span class="string">"test.txt"</span>));</span><br><span class="line"><span class="keyword">try</span> (in) {</span><br><span class="line"> <span class="comment">// JDK8中该用法会有以下异常提示</span></span><br><span class="line"> <span class="comment">// Resource references are not supported at language level '8'</span></span><br><span class="line"> ...</span><br><span class="line">} <span class="keyword">catch</span> (IOException e) {</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><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">InferredType</span> <T>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(T t)</span>;</span><br><span class="line">}</span><br><span class="line">...</span><br><span class="line"> <span class="comment">// JDK8 中会有如下异常提示</span></span><br><span class="line"> <span class="comment">// Class 'Anonymous class derived from InferredType' must either be declared abstract or implement abstract method 'test(T)' in 'InferredType'</span></span><br><span class="line"> InferredType<String> inferredType = <span class="keyword">new</span> <span class="title class_">InferredType</span><>() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(String o)</span> {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="允许接口定义private方法"><a href="#允许接口定义private方法" class="headerlink" title="允许接口定义private方法"></a>允许接口定义private方法</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">PrivateFunc</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">func1</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">default</span> <span class="keyword">void</span> <span class="title function_">func2</span><span class="params">()</span> {</span><br><span class="line"> func3();</span><br><span class="line"> <span class="comment">// JDK8+</span></span><br><span class="line"> System.out.println(<span class="string">"默认实现"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">func3</span><span class="params">()</span> {</span><br><span class="line"> func4();</span><br><span class="line"> System.out.println(<span class="string">"私有默认实现"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">func4</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"静态私有实现"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol><li>可以起到优化重复代码的帮助</li></ol><h1 id="不可作为变量名"><a href="#不可作为变量名" class="headerlink" title="_不可作为变量名"></a><code>_</code>不可作为变量名</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// As of Java 9, '_' is a keyword, and may not be used as an identifier</span></span><br><span class="line"><span class="comment">// JDK8 是可以正常使用的,影响不大,几乎很少有场景会单独使用 _ 作为变量名</span></span><br><span class="line"><span class="type">String</span> <span class="variable">_</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>();</span><br></pre></td></tr></table></figure><h1 id="SafeVarargs"><a href="#SafeVarargs" class="headerlink" title="@SafeVarargs"></a>@SafeVarargs</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SafeVarargs</span></span><br><span class="line"><span class="comment">// 不用该注解,仅会导致编译器提示,不会异常</span></span><br><span class="line"><span class="comment">// 此注解用于泛型可变入参函数,JDK9是拓展至可声明private函数</span></span><br><span class="line"><span class="comment">// Possible heap pollution from parameterized vararg type</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <T> <span class="keyword">void</span> <span class="title function_">saveVarargs</span><span class="params">(T... params)</span> {</span><br><span class="line"> Arrays.stream(params).forEach(System.out::println);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="Class文件版本号变动至53"><a href="#Class文件版本号变动至53" class="headerlink" title="Class文件版本号变动至53"></a>Class文件版本号变动至53</h1><h1 id="编译旧版本范围变动"><a href="#编译旧版本范围变动" class="headerlink" title="编译旧版本范围变动"></a>编译旧版本范围变动</h1><blockquote><p>The javac command no longer supports -source or-target values for releases before 6/1.6. However, older class files are still readable by javac. Source code for an older release can be ported to a newer source level. To generate class files usable by releases older than JDK 6, a javac from a JDK 6, 7, or 8 release family can be used.<br><a href="https://openjdk.java.net/jeps/182">JEP 182</a> documents the policy for retiring old -source and -target options. </p></blockquote><p>JDK9在使用javac编译java文件时,指定java的版本仅支持*1.6+*,不再支持编译到1.5以及更早期的版本</p><h1 id="JShell"><a href="#JShell" class="headerlink" title="JShell"></a>JShell</h1><p>引用官方原文</p><blockquote><p>Immediate feedback is important when learning a programming language and its APIs. The number one reason schools cite for moving away from Java as a teaching language is that other languages have a “REPL” and have far lower bars to an initial “Hello, world!” program. A Read-Eval-Print Loop (REPL) is an interactive programming tool which loops, continually reading user input, evaluating the input, and printing the value of the input or a description of the state change the input caused. Scala, Ruby, JavaScript, Haskell, Clojure, and Python all have REPLs and all allow small initial programs. JShell adds REPL functionality to the Java platform.<br>Exploration of coding options is also important for developers prototyping code or investigating a new API. Interactive evaluation is vastly more efficient in this regard than edit/compile/execute and System.out.println.<br>Without the ceremony of class Foo { public static void main(String[] args) { … } }, learning and exploration is streamlined.</p></blockquote><p>大致含义是降低Java学习的入门门槛,可以更方便的测试API<br>吐槽:</p><ol><li>对于习惯了ide的代码提示和代码模板(<code>code templates</code>)来说,突然使用<code>jshell</code>会有些难受</li></ol>]]></content>
<summary type="html"><h1 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h1><ol>
<li><a href="https://docs.oracle.com/javase/9/">Oracle Java9官方文档</a></li>
<li><a href="https://www.oracle.com/java/technologies/javase/9-relnotes.html">JDK 9 Release Notes</a></li>
<li><a href="http://openjdk.java.net/projects/jigsaw/quick-start">Module System Quick-Start Guide</a></li>
<li><a href="https://docs.oracle.com/javase/9/migrate/toc.htm#JSMIG-GUID-7744EF96-5899-4FB2-B34E-86D49B2E89B6">JDK9迁移指南</a></li>
<li><a href="http://openjdk.java.net/jeps/213">Milling Project Coin</a></li>
<li><a href="http://openjdk.java.net/jeps/282">jlink: The Java Linker</a></li>
<li><a href="http://openjdk.java.net/jeps/222">The Java Shell</a></li>
</ol>
<h1 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h1><ol>
<li>本文仅涉及部分JDK9变更,完整可查看官方文档<a href="https://docs.oracle.com/javase/9/whatsnew/toc.htm">Standard Edition What’s New in Oracle JDK 9</a></li>
<li>除了模块化,其他改动对日常开发影响不多,可以着重关注模块化和接口私有函数</li>
<li>在后续的学习中发现,在模块化后,类似于<code>ServiceLoader.load</code>的类加载的方法都有变动,大多数都和模块之间的类可视化有关,如果是从低版本迁移,需要额外注意</li>
</ol></summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="模块系统" scheme="http://janwarlen.com/tags/%E6%A8%A1%E5%9D%97%E7%B3%BB%E7%BB%9F/"/>
<category term="REPL (JShell)" scheme="http://janwarlen.com/tags/REPL-JShell/"/>
<category term="JDK9" scheme="http://janwarlen.com/tags/JDK9/"/>
</entry>
<entry>
<title>Java版本特性-StreamAPI</title>
<link href="http://janwarlen.com/2022/06/08/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-StreamAPI/"/>
<id>http://janwarlen.com/2022/06/08/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-StreamAPI/</id>
<published>2022-06-08T12:13:11.000Z</published>
<updated>2022-09-02T02:49:26.722Z</updated>
<content type="html"><![CDATA[<h3 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h3><ol><li><a href="https://dev.java/learn/the-stream-api/">Java官方学习手册-Stream</a></li><li><a href="https://github.com/CarpenterLee/JavaLambdaInternals">GitHub-CarpenterLee-JavaLambdaInternals</a></li><li><a href="https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html">Oracle关于parallelism官方文档</a></li><li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#package.description">Oracle关于Stream包官方描述文档</a><span id="more"></span></li></ol><h2 id="StreamAPI"><a href="#StreamAPI" class="headerlink" title="StreamAPI"></a>StreamAPI</h2><h3 id="入门"><a href="#入门" class="headerlink" title="入门"></a>入门</h3><ol><li>Stream API 是关于向 JDK 提供众所周知的 map-filter-reduce 算法的实现</li><li><code>map</code>只会变更对象的类型,不会改变对象集合的数量</li><li><code>filter</code>只会改变对象集合的数量,而不会改变其中对象的类型</li><li><code>reduce</code>允许针对数据流构建任何你想构建的数据结构</li></ol><h4 id="案例-统计所有人口超过10w的城市人口总和"><a href="#案例-统计所有人口超过10w的城市人口总和" class="headerlink" title="案例 - 统计所有人口超过10w的城市人口总和"></a>案例 - 统计所有人口超过10w的城市人口总和</h4><h5 id="城市类定义"><a href="#城市类定义" class="headerlink" title="城市类定义"></a>城市类定义</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">City</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">City</span><span class="params">(String name, <span class="type">int</span> population)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> <span class="built_in">this</span>.population = population;</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"> */</span></span><br><span class="line"> <span class="keyword">private</span> String name;</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"> */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> population;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getPopulation</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> population;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setPopulation</span><span class="params">(<span class="type">int</span> population)</span> {</span><br><span class="line"> <span class="built_in">this</span>.population = population;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"City{"</span> +</span><br><span class="line"> <span class="string">"name='"</span> + name + <span class="string">'\''</span> +</span><br><span class="line"> <span class="string">", population="</span> + population +</span><br><span class="line"> <span class="string">'}'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="数据初始化"><a href="#数据初始化" class="headerlink" title="数据初始化"></a>数据初始化</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">List<City> cities = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"><span class="type">City</span> <span class="variable">city1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"1"</span>, <span class="number">50_000</span>);</span><br><span class="line"><span class="type">City</span> <span class="variable">city2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"2"</span>, <span class="number">2_100_000</span>);</span><br><span class="line"><span class="type">City</span> <span class="variable">city3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"3"</span>, <span class="number">90_000</span>);</span><br><span class="line"><span class="type">City</span> <span class="variable">city4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"4"</span>, <span class="number">130_000</span>);</span><br><span class="line"><span class="type">City</span> <span class="variable">city5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"5"</span>, <span class="number">1_000_000</span>);</span><br><span class="line">cities.add(city1);</span><br><span class="line">cities.add(city2);</span><br><span class="line">cities.add(city3);</span><br><span class="line">cities.add(city4);</span><br><span class="line">cities.add(city5);</span><br></pre></td></tr></table></figure><h5 id="普通写法"><a href="#普通写法" class="headerlink" title="普通写法"></a>普通写法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">for</span> (City city : cities) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">population</span> <span class="operator">=</span> city.getPopulation();</span><br><span class="line"> <span class="keyword">if</span> (population > <span class="number">100_000</span>) {</span><br><span class="line"> sum += population;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">System.out.println(<span class="string">"Sum = "</span> + sum);</span><br></pre></td></tr></table></figure><h5 id="StreamApi写法"><a href="#StreamApi写法" class="headerlink" title="StreamApi写法"></a>StreamApi写法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 第一步先创建流</span></span><br><span class="line"><span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> cities.stream()</span><br><span class="line"><span class="comment">// 第二步 将城市集合转换为人口集合</span></span><br><span class="line"> .mapToInt(City::getPopulation)</span><br><span class="line"><span class="comment">// 第三步 过滤人口超过 10w 的城市人口</span></span><br><span class="line"> .filter(population -> population > <span class="number">100_000</span>)</span><br><span class="line"><span class="comment">// 求和</span></span><br><span class="line"> .sum();</span><br><span class="line">System.out.println(<span class="string">"Stream Sum = "</span> + sum);</span><br></pre></td></tr></table></figure><h3 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h3><h4 id="流的创建"><a href="#流的创建" class="headerlink" title="流的创建"></a>流的创建</h4><h5 id="创建一个空流"><a href="#创建一个空流" class="headerlink" title="创建一个空流"></a>创建一个空流</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Stream.empty()</span><br></pre></td></tr></table></figure><p>一般场景不会用到,通常与<code>flatMap</code>结合使用,过滤不需要的元素</p><h5 id="从可变参数或数组创建流"><a href="#从可变参数或数组创建流" class="headerlink" title="从可变参数或数组创建流"></a>从可变参数或数组创建流</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Stream.of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>)</span><br><span class="line">Arrays.stream(array)</span><br></pre></td></tr></table></figure><h5 id="给定规则创建流并持续添加元素"><a href="#给定规则创建流并持续添加元素" class="headerlink" title="给定规则创建流并持续添加元素"></a>给定规则创建流并持续添加元素</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Stream.generate(Supplier<T> s)</span><br><span class="line"></span><br><span class="line">Stream.generate(() -> <span class="string">"test"</span>)</span><br></pre></td></tr></table></figure><p>需注意的是,该方法将会无限制的添加元素进入流中,需要搭配<code>limit</code>使用,避免<code>OOM</code></p><h5 id="创建符合一定规则的流"><a href="#创建符合一定规则的流" class="headerlink" title="创建符合一定规则的流"></a>创建符合一定规则的流</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// seed是第一个元素, UnaryOperator则是所有后续元素将要遵守的规则</span></span><br><span class="line">Stream.iterate(<span class="keyword">final</span> T seed, <span class="keyword">final</span> UnaryOperator<T> f)</span><br><span class="line"><span class="comment">// 举例</span></span><br><span class="line">Stream.iterate(<span class="string">"+"</span>, s -> s + <span class="string">"+"</span>)</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">// +++</span></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">// ...</span></span><br></pre></td></tr></table></figure><p>通常需要搭配<code>limit</code>使用,<code>Java9</code>该方法添加了重载,添加了入参,可自定义停止创建规则,当规则符合false则停止创建流</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Stream.iterate(<span class="string">"+"</span>, s -> s.length() <= <span class="number">5</span>, s -> s + <span class="string">"+"</span>)</span><br></pre></td></tr></table></figure><h5 id="创建一个在一定范围的数字流"><a href="#创建一个在一定范围的数字流" class="headerlink" title="创建一个在一定范围的数字流"></a>创建一个在一定范围的数字流</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 0 1 2 3 4 5 6 7 8 9</span></span><br><span class="line">IntStream.range(<span class="number">0</span>,<span class="number">10</span>);</span><br><span class="line"><span class="comment">// 0 1 2 3 4 5 6 7 8 9 10</span></span><br><span class="line">IntStream.rangeClosed(<span class="number">0</span>,<span class="number">10</span>)</span><br></pre></td></tr></table></figure><h5 id="创建随机数流"><a href="#创建随机数流" class="headerlink" title="创建随机数流"></a>创建随机数流</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Random</span> <span class="variable">random</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>(<span class="number">10L</span>);</span><br><span class="line">random.ints();</span><br><span class="line">random.ints(<span class="number">10</span>);</span><br><span class="line"><span class="comment">// [0, 7)</span></span><br><span class="line">random.ints(<span class="number">0</span>, <span class="number">7</span>);</span><br><span class="line"><span class="comment">// [2,9)</span></span><br><span class="line">random.ints(<span class="number">10</span>, <span class="number">2</span>, <span class="number">9</span>);</span><br></pre></td></tr></table></figure><p>相对应的,还有<code>long</code>与<code>double</code></p><h5 id="通过String创建char字符流"><a href="#通过String创建char字符流" class="headerlink" title="通过String创建char字符流"></a>通过String创建char字符流</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">test</span> <span class="operator">=</span> <span class="string">"test"</span>;</span><br><span class="line"><span class="type">IntStream</span> <span class="variable">chars</span> <span class="operator">=</span> test.chars();</span><br></pre></td></tr></table></figure><h5 id="读取文件创建流"><a href="#读取文件创建流" class="headerlink" title="读取文件创建流"></a>读取文件创建流</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Path</span> <span class="variable">log</span> <span class="operator">=</span> Path.of(<span class="string">"/tmp/debug.log"</span>);</span><br><span class="line"><span class="keyword">try</span> (Stream<String> lines = Files.lines(log)) {</span><br><span class="line"> </span><br><span class="line"> <span class="type">long</span> <span class="variable">warnings</span> <span class="operator">=</span> </span><br><span class="line"> lines.filter(line -> line.contains(<span class="string">"WARNING"</span>))</span><br><span class="line"> .count();</span><br><span class="line"> System.out.println(<span class="string">"Number of warnings = "</span> + warnings);</span><br><span class="line"> </span><br><span class="line">} <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> <span class="comment">// do something with the exception</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="通过正则创建流"><a href="#通过正则创建流" class="headerlink" title="通过正则创建流"></a>通过正则创建流</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">sentence</span> <span class="operator">=</span> <span class="string">"For there is good news yet to hear and fine things to be seen"</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">Pattern</span> <span class="variable">pattern</span> <span class="operator">=</span> Pattern.compile(<span class="string">" "</span>);</span><br><span class="line">Stream<String> stream = pattern.splitAsStream(sentence);</span><br></pre></td></tr></table></figure><h5 id="通过Builder创建流"><a href="#通过Builder创建流" class="headerlink" title="通过Builder创建流"></a>通过Builder创建流</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Stream.Builder<String> builder = Stream.<String>builder();</span><br><span class="line"></span><br><span class="line">builder.add(<span class="string">"one"</span>)</span><br><span class="line"> .add(<span class="string">"two"</span>)</span><br><span class="line"> .add(<span class="string">"three"</span>)</span><br><span class="line"> .add(<span class="string">"four"</span>);</span><br><span class="line"></span><br><span class="line">Stream<String> stream = builder.build();</span><br></pre></td></tr></table></figure><h4 id="常用方法及功能描述"><a href="#常用方法及功能描述" class="headerlink" title="常用方法及功能描述"></a>常用方法及功能描述</h4><h5 id="Stream"><a href="#Stream" class="headerlink" title="Stream"></a>Stream</h5><table><thead><tr><th>方法</th><th>功能</th></tr></thead><tbody><tr><td>filter</td><td>根据用户自定义条件过滤Stream中所有数据,符合条件的通过</td></tr><tr><td>map</td><td>使用用户自定义的功能代码应用到每一个元素,常规用于类型转换</td></tr><tr><td>mapToInt</td><td>通过用户自定义的代码将当前Stream转换为IntStream(通常是为了调用IntStream额外的接口,如<code>sum</code>)</td></tr><tr><td>mapToLong</td><td>通过用户自定义代码将当前Stream转换为LongStream</td></tr><tr><td>mapToDouble</td><td>通过用户自定义代码将当前Stream转换为DoublegStream</td></tr><tr><td>flatMap</td><td>将Stream中的多维元素降一维,如<code>Stream<List<City>></code>转换为<code>Stream<City></code></td></tr><tr><td>flatMapToInt</td><td>效果同<code>flatMap</code>,仅可用于<code>int</code>类型,如 <code>Stream<int[]></code> 转换为 <code>Stream<int></code></td></tr><tr><td>flatMapToLong</td><td>效果同 <code>flatMapToInt</code> ,类型限制为<code>long</code></td></tr><tr><td>flatMapToDouble</td><td>效果同<code>flatMapToInt</code>,类型限制为<code>double</code></td></tr><tr><td>distinct</td><td>对数据流进行去重,重复判定由<code>Object.equals(Object)</code>确认</td></tr><tr><td>sorted</td><td>将流中元素根据用户实现<code>Comparable</code>内容进行自然排序,若类型未实现该接口,则抛出异常<code>java.lang.ClassCastException</code></td></tr><tr><td>sorted(Comparator<? super T> comparator)</td><td>重载函数,根据入参的自定义规则进行自然排序</td></tr><tr><td>peek</td><td>使用用户自定义操作应用到流中每一个元素</td></tr><tr><td>limit</td><td>根据用户自定义长度,对流进行截断</td></tr><tr><td>skip</td><td>从流开头跳过用户给定长度,如果流元素数不够,则流将会被清空</td></tr><tr><td>forEach</td><td>将用户自定义操作应用到流中每一个元素</td></tr><tr><td>forEachOrdered</td><td>通常在<code>parallel stream</code>中使用,对流按照给定顺序进行元素操作</td></tr><tr><td>toArray</td><td>将流中元素组成数组返回,返回<code>Object[]</code></td></tr><tr><td>toArray(IntFunction<A[]> generator)</td><td>重载函数,可由用户指定数组类型,使用方式<code>toArray(String[]::new)</code></td></tr><tr><td>min/max</td><td>根据自定义的<code>Comparator</code>实现,寻找流中最小/最大元素</td></tr><tr><td>count</td><td>统计流中元素数量</td></tr><tr><td>anyMatch</td><td>根据用户自定义判断条件,判断流中是否有任意符合元素</td></tr><tr><td>allMatch</td><td>判断流中元素是否全部符合自定义条件</td></tr><tr><td>noneMatch</td><td>判断流中元素是否全部不符合自定义条件</td></tr><tr><td>findFirst</td><td>返回流中第一个元素,<code>parallelStream</code>中将无法保证根据给定顺序确认第一个</td></tr><tr><td>findAny</td><td>返回流中任意一个元素,<code>Stream</code>中始终返回第一个,<code>parallelStream</code>中将会任意返回</td></tr><tr><td>concat</td><td>将两个流合并为一个</td></tr></tbody></table><h5 id="IntStream-x2F-LongStream-x2F-DoubleStream"><a href="#IntStream-x2F-LongStream-x2F-DoubleStream" class="headerlink" title="IntStream/LongStream/DoubleStream"></a>IntStream/LongStream/DoubleStream</h5><p>相对比常规Stream,此三种流额外实现几种方法</p><table><thead><tr><th>方法</th><th>功能</th></tr></thead><tbody><tr><td>sum</td><td>将流中元素求和</td></tr><tr><td>average</td><td>求平均值</td></tr><tr><td>min</td><td>最小值(无需提供<code>Comparator</code>实现)</td></tr><tr><td>max</td><td>最大值(无需提供<code>Comparator</code>实现)</td></tr><tr><td>summaryStatistics</td><td>返回统计结果,包含元素个数、总和、最大值、最小值</td></tr></tbody></table><h3 id="提升"><a href="#提升" class="headerlink" title="提升"></a>提升</h3><h4 id="reduce"><a href="#reduce" class="headerlink" title="reduce"></a>reduce</h4><p><code>reduce</code>操作一般是处理一组数据生成一个值,上文中提及的<code>sum</code>/<code>min</code>/<code>max</code>/<code>count</code>都是<code>reduce</code>操作,因这些方法使用频繁,因此被单独设置一个方法以方便使用</p><h5 id="找最大值"><a href="#找最大值" class="headerlink" title="找最大值"></a>找最大值</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Stream<Integer> ints = Stream.of(<span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">3</span>);</span><br><span class="line">Optional<Integer> optional = ints.reduce((i1, i2) -> i1 > i2 ? i1: i2);</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (optional.isPresent()) {</span><br><span class="line"> System.out.println(<span class="string">"result = "</span> + optional.get());</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> System.out.println(<span class="string">"No result could be computed"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上方代码中,<code>reduce</code>操作通过用户自定义的规则<code>(i1, i2) -> i1 > i2 ? i1: i2</code>对整个流中数据进行规约(reduce),最终求得最大值</p><h5 id="重载"><a href="#重载" class="headerlink" title="重载"></a>重载</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Optional<T> <span class="title function_">reduce</span><span class="params">(BinaryOperator<T> accumulator)</span>;</span><br><span class="line">T <span class="title function_">reduce</span><span class="params">(T identity, BinaryOperator<T> accumulator)</span>;</span><br><span class="line"><U> U <span class="title function_">reduce</span><span class="params">(U identity, BiFunction<U, ? <span class="built_in">super</span> T, U> accumulator, BinaryOperator<U> combiner)</span>;</span><br></pre></td></tr></table></figure><p>其中,入参<code>BinaryOperator<T> accumulator</code>是由用户提供的函数,用于对所有元素进行操作,返回值会作为新元素添加到流中加入后续的规则应用,而该接口待实现的方法入参有两个,分别代表了当前元素与下一个元素,返回值会作为下一轮的当前元素;<br>入参<code>identity</code>是由用户自定义,相当于在流的开头,人工添加一个元素,需注意的是,在<code>parallel</code>模式中,<code>identity</code>极有可能被重复使用,因此需要额外注意</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Integer</span> <span class="variable">reduce</span> <span class="operator">=</span> Stream.of(<span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">3</span>).parallel().reduce(<span class="number">9</span>, (i1, i2) -> {</span><br><span class="line"> System.out.println(<span class="string">"i1="</span> + i1 + <span class="string">" i2="</span> + i2);</span><br><span class="line"> <span class="keyword">return</span> i1 * <span class="number">10</span> + i2;</span><br><span class="line">});</span><br><span class="line">System.out.println(<span class="string">"reduce with identity="</span> + reduce);</span><br><span class="line"></span><br><span class="line"><span class="comment">// i1=9 i2=1</span></span><br><span class="line"><span class="comment">// i1=9 i2=2</span></span><br><span class="line"><span class="comment">// i1=9 i2=5</span></span><br><span class="line"><span class="comment">// i1=9 i2=3</span></span><br><span class="line"><span class="comment">// i1=9 i2=8</span></span><br><span class="line"><span class="comment">// i1=95 i2=93</span></span><br><span class="line"><span class="comment">// i1=92 i2=98</span></span><br><span class="line"><span class="comment">// i1=91 i2=1043</span></span><br><span class="line"><span class="comment">// i1=1018 i2=1953</span></span><br><span class="line"><span class="comment">// reduce with identity=12133</span></span><br></pre></td></tr></table></figure><p>入参<code>combiner</code>仅在<code>parallel</code>模式中才会生效,非<code>parallel</code>模式的流将不会执行,而<code>combiner</code>的两个入参,分别是两个<code>accumulator</code>的执行结果,并且<code>combiner</code>本身的执行结果也会加入新的<code>combiner</code>中等待执行</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><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"><span class="type">Integer</span> <span class="variable">reduce1</span> <span class="operator">=</span> Stream.of(<span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">3</span>).parallel().reduce(<span class="number">7</span>, (i1, i2) -> {</span><br><span class="line"> System.out.println(<span class="string">"i1="</span> + i1 + <span class="string">" i2="</span> + i2);</span><br><span class="line"> <span class="keyword">return</span> i1 * <span class="number">10</span> + i2;</span><br><span class="line">}, (i1, i2) -> {</span><br><span class="line"> System.out.println(<span class="string">"a1="</span> + i1 + <span class="string">" a2="</span> + i2);</span><br><span class="line"> <span class="keyword">return</span> i1 * <span class="number">10</span> + i2;</span><br><span class="line">});</span><br><span class="line">System.out.println(<span class="string">"reduce with identity & combiner="</span> + reduce1);</span><br><span class="line"></span><br><span class="line"><span class="comment">// i1=7 i2=1</span></span><br><span class="line"><span class="comment">// i1=7 i2=2</span></span><br><span class="line"><span class="comment">// i1=7 i2=8</span></span><br><span class="line"><span class="comment">// i1=7 i2=5</span></span><br><span class="line"><span class="comment">// i1=7 i2=3</span></span><br><span class="line"><span class="comment">// a1=72 a2=78</span></span><br><span class="line"><span class="comment">// a1=75 a2=73</span></span><br><span class="line"><span class="comment">// a1=71 a2=823</span></span><br><span class="line"><span class="comment">// a1=798 a2=1533</span></span><br><span class="line"><span class="comment">// reduce with identity & combiner=9513</span></span><br></pre></td></tr></table></figure><h4 id="collect"><a href="#collect" class="headerlink" title="collect"></a>collect</h4><p>大多数业务场景都涉及模型的转换,此时,<code>reduce</code>将会力不从心,而<code>collect</code>将会完美的解决这个问题</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">List<Integer> collect = Stream.of(<span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">3</span>).collect(Collectors.toList());</span><br></pre></td></tr></table></figure><p>上述代码将<code>int</code>元素通过<code>collect</code>与<code>Collectors</code>转换为<code><List></code>集合,<code>collect</code>有重载的两个方法,分别如下所示</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><R, A> R <span class="title function_">collect</span><span class="params">(Collector<? <span class="built_in">super</span> T, A, R> collector)</span>;</span><br><span class="line"><R> R <span class="title function_">collect</span><span class="params">(Supplier<R> supplier, BiConsumer<R, ? <span class="built_in">super</span> T> accumulator, BiConsumer<R, R> combiner)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>大多数情况下,我们都是使用<code>Collector</code>,因<code>Collectors</code>已经内置大多数使用频率较高的函数,如<code>toList</code>/<code>toSet</code>/<code>toCollection</code>/<code>toMap</code>等,其中<code>toList</code>/<code>toSet</code>最容易理解,即转为<code>List</code>/<code>Set</code>,如果想转换为其他的集合,则可以使用<code>toCollection</code>,在调用时,同时声明想要转换的集合即可,如:<code>Collectors.toCollection(ArrayList::new)</code>即是转换为<code>ArrayList</code></p><h5 id="Collectors-toMap"><a href="#Collectors-toMap" class="headerlink" title="Collectors.toMap"></a>Collectors.toMap</h5><p>转换为<code>Map</code>稍微复杂一些,重载一共有以下三个</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Collector<T, ?, Map<K,U>> toMap(Function<? <span class="built_in">super</span> T, ? <span class="keyword">extends</span> <span class="title class_">K</span>> keyMapper, Function<? <span class="built_in">super</span> T, ? <span class="keyword">extends</span> <span class="title class_">U</span>> valueMapper)</span><br><span class="line">Collector<T, ?, Map<K,U>> toMap(Function<? <span class="built_in">super</span> T, ? <span class="keyword">extends</span> <span class="title class_">K</span>> keyMapper, Function<? <span class="built_in">super</span> T, ? <span class="keyword">extends</span> <span class="title class_">U</span>> valueMapper, BinaryOperator<U> mergeFunction)</span><br><span class="line">Collector<T, ?, M> toMap(Function<? <span class="built_in">super</span> T, ? <span class="keyword">extends</span> <span class="title class_">K</span>> keyMapper, Function<? <span class="built_in">super</span> T, ? <span class="keyword">extends</span> <span class="title class_">U</span>> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)</span><br></pre></td></tr></table></figure><p>其中<code>keyMapper</code>与<code>valueMapper</code>,是针对流中对象操作,分别处理出Map中的key与value</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">List<City> cities = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"><span class="type">City</span> <span class="variable">city1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"1"</span>, <span class="number">50_000</span>);</span><br><span class="line"><span class="type">City</span> <span class="variable">city2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"2"</span>, <span class="number">2_100_000</span>);</span><br><span class="line"><span class="type">City</span> <span class="variable">city3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"3"</span>, <span class="number">90_000</span>);</span><br><span class="line"><span class="type">City</span> <span class="variable">city4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"4"</span>, <span class="number">130_000</span>);</span><br><span class="line"><span class="type">City</span> <span class="variable">city5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"5"</span>, <span class="number">1_000_000</span>);</span><br><span class="line">cities.add(city1);</span><br><span class="line">cities.add(city2);</span><br><span class="line">cities.add(city3);</span><br><span class="line">cities.add(city4);</span><br><span class="line">cities.add(city5);</span><br><span class="line">Map<String, Integer> collect1 = cities.stream().collect(Collectors.toMap(City::getName, City::getPopulation));</span><br><span class="line">collect1.forEach((k, v) -> System.out.println(<span class="string">"key="</span> + k + <span class="string">" value="</span> + v));</span><br><span class="line"></span><br><span class="line"><span class="comment">// key=1 value=50000</span></span><br><span class="line"><span class="comment">// key=2 value=2100000</span></span><br><span class="line"><span class="comment">// key=3 value=90000</span></span><br><span class="line"><span class="comment">// key=4 value=130000</span></span><br><span class="line"><span class="comment">// key=5 value=1000000</span></span><br></pre></td></tr></table></figure><h6 id="Key值冲突"><a href="#Key值冲突" class="headerlink" title="Key值冲突"></a>Key值冲突</h6><p>而在转换为Map的时候,通常会遇到<code>Key</code>值重复的问题,如果此时不加以特殊处理,则会导致旧值被覆盖问题,此时<code>mergeFunction</code>则发挥了重要的作用,入参是两个<code>value</code>,不是<code>key</code>,处理结果将作为新的<code>value</code>插入到Map中</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">cities.add(<span class="keyword">new</span> <span class="title class_">City</span>(<span class="string">"1"</span>, <span class="number">5</span>));</span><br><span class="line">Map<String, Integer> collect = cities.stream().collect(Collectors.toMap(City::getName, City::getPopulation, (i1, i2) -> {</span><br><span class="line"> System.out.println(<span class="string">"i1="</span> + i1 + <span class="string">" i2="</span> + i2);</span><br><span class="line"> <span class="keyword">return</span> i1 + i2;</span><br><span class="line">}));</span><br><span class="line">collect.forEach((k, v) -> System.out.println(<span class="string">"key="</span> + k + <span class="string">" value="</span> + v));</span><br><span class="line"></span><br><span class="line"><span class="comment">// i1=50000 i2=5</span></span><br><span class="line"><span class="comment">// key=1 value=50005</span></span><br><span class="line"><span class="comment">// key=2 value=2100000</span></span><br><span class="line"><span class="comment">// key=3 value=90000</span></span><br><span class="line"><span class="comment">// key=4 value=130000</span></span><br><span class="line"><span class="comment">// key=5 value=1000000</span></span><br></pre></td></tr></table></figure><h6 id="指定Map类型"><a href="#指定Map类型" class="headerlink" title="指定Map类型"></a>指定Map类型</h6><p>有些场景中,我们需要使用其他<code>Map</code>类型,如<code>HashTable</code>,此时则需要<code>mapSupplier</code>的帮忙了,如下所示</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 其他Map类型均可自由指定,但必须实现Map接口</span></span><br><span class="line">Hashtable<String, Integer> collect2 = cities.stream().collect(Collectors.toMap(City::getName, City::getPopulation, Integer::sum, Hashtable::<span class="keyword">new</span>));</span><br></pre></td></tr></table></figure><h5 id="Collectors其他场景"><a href="#Collectors其他场景" class="headerlink" title="Collectors其他场景"></a>Collectors其他场景</h5><h6 id="Collectors-averagingInt"><a href="#Collectors-averagingInt" class="headerlink" title="Collectors.averagingInt"></a><code>Collectors.averagingInt</code></h6><p>求平均值</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Double</span> <span class="variable">collect4</span> <span class="operator">=</span> cities.stream().collect(Collectors.averagingInt(City::getPopulation));</span><br></pre></td></tr></table></figure><h6 id="Collectors-collectingAndThen"><a href="#Collectors-collectingAndThen" class="headerlink" title="Collectors.collectingAndThen"></a><code>Collectors.collectingAndThen</code></h6><p>先通过第一个入参处理流,然后再通过第二个入参处理上一个处理结果</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 找出人口最多的城市</span></span><br><span class="line"><span class="type">String</span> <span class="variable">collect5</span> <span class="operator">=</span> cities.stream().collect(Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(City::getPopulation)), e -> e.map(City::getName).orElse(<span class="literal">null</span>)));</span><br><span class="line"> System.out.println(<span class="string">"largest city = "</span> + collect5);</span><br></pre></td></tr></table></figure><h6 id="Collectors-joining"><a href="#Collectors-joining" class="headerlink" title="Collectors.joining"></a><code>Collectors.joining</code></h6><p>将流中所有字符串拼接为<code>String</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(Stream.of(<span class="string">"2"</span>, <span class="string">"8"</span>, <span class="string">"1"</span>, <span class="string">"5"</span>, <span class="string">"3"</span>).collect(Collectors.joining()));</span><br><span class="line">System.out.println(Stream.of(<span class="string">"2"</span>, <span class="string">"8"</span>, <span class="string">"1"</span>, <span class="string">"5"</span>, <span class="string">"3"</span>).collect(Collectors.joining(<span class="string">","</span>)));</span><br><span class="line">System.out.println(Stream.of(<span class="string">"2"</span>, <span class="string">"8"</span>, <span class="string">"1"</span>, <span class="string">"5"</span>, <span class="string">"3"</span>).collect(Collectors.joining(<span class="string">","</span>, <span class="string">"{"</span>, <span class="string">"}"</span>)));</span><br><span class="line"><span class="comment">// 28153</span></span><br><span class="line"><span class="comment">// 2,8,1,5,3</span></span><br><span class="line"><span class="comment">// {2,8,1,5,3}</span></span><br></pre></td></tr></table></figure><h6 id="Collectors-groupingBy"><a href="#Collectors-groupingBy" class="headerlink" title="Collectors.groupingBy"></a><code>Collectors.groupingBy</code></h6><p>根据自定义规则对流中数据进行分类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">Map<String, List<City>> collect6 = cities.stream().collect(Collectors.groupingBy(City::getName));</span><br><span class="line">collect6.forEach((k, v) -> System.out.println(<span class="string">"key="</span> + k + <span class="string">" value="</span> + v));</span><br><span class="line"><span class="comment">// key=1 value=[City{name='1', population=50000}, City{name='1', population=5}]</span></span><br><span class="line"><span class="comment">// key=2 value=[City{name='2', population=2100000}]</span></span><br><span class="line"><span class="comment">// key=3 value=[City{name='3', population=90000}]</span></span><br><span class="line"><span class="comment">// key=4 value=[City{name='4', population=130000}]</span></span><br><span class="line"><span class="comment">// key=5 value=[City{name='5', population=1000000}]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 重载函数2</span></span><br><span class="line"><span class="comment">// groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)</span></span><br><span class="line"><span class="comment">// downstream 是在分组完成后,再执行的处理函数</span></span><br><span class="line">Map<String, Long> collect7 = cities.stream().collect(Collectors.groupingBy(City::getName, Collectors.counting()));</span><br><span class="line">collect7.forEach((k, v) -> System.out.println(<span class="string">"key="</span> + k + <span class="string">" value="</span> + v));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 重载函数3</span></span><br><span class="line"><span class="comment">// groupingBy(Function<? super T, ? extends K> classifier, Supplier<M> mapFactory, Collector<? super T, A, D> downstream)</span></span><br><span class="line"><span class="comment">// mapFactory 指明一个Map的构造函数即可,将会影响最终返回Map类型</span></span><br><span class="line">Hashtable<String, Long> collect8 = cities.stream().collect(Collectors.groupingBy(City::getName, Hashtable::<span class="keyword">new</span>, Collectors.counting()));</span><br></pre></td></tr></table></figure><h6 id="Collectors-partitioningBy"><a href="#Collectors-partitioningBy" class="headerlink" title="Collectors.partitioningBy"></a><code>Collectors.partitioningBy</code></h6><p>根据自定义判断规则,对流中元素进行分类,仅分为两组,通常在类似与统计及格人数之类场景使用</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Map<Boolean, List<Integer>> collect9 = Stream.of(<span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">3</span>).collect(Collectors.partitioningBy(e -> e > <span class="number">5</span>));</span><br><span class="line">collect9.forEach((k, v) -> System.out.println(<span class="string">"key="</span> + k + <span class="string">" value="</span> + v));</span><br><span class="line"><span class="comment">// key=false value=[2, 1, 5, 3]</span></span><br><span class="line"><span class="comment">// key=true value=[8]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 重载函数 partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream)</span></span><br><span class="line"><span class="comment">// 新增入参是在分类结束后调用</span></span><br><span class="line">Map<Boolean, Long> collect10 = Stream.of(<span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">3</span>).collect(Collectors.partitioningBy(e -> e > <span class="number">5</span>, Collectors.counting()));</span><br><span class="line">collect10.forEach((k, v) -> System.out.println(<span class="string">"key="</span> + k + <span class="string">" value="</span> + v));</span><br><span class="line"><span class="comment">// key=false value=4</span></span><br><span class="line"><span class="comment">// key=true value=1</span></span><br></pre></td></tr></table></figure><h6 id="Collectors-summarizingInt"><a href="#Collectors-summarizingInt" class="headerlink" title="Collectors.summarizingInt"></a><code>Collectors.summarizingInt</code></h6><p>类似于<code>Stream</code>中的<code>summaryStatistics</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">IntSummaryStatistics</span> <span class="variable">collect11</span> <span class="operator">=</span> Stream.of(<span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">3</span>).collect(Collectors.summarizingInt(e -> e));</span><br><span class="line">System.out.println(<span class="string">"summarizingInt="</span> + collect11);</span><br><span class="line"><span class="comment">// summarizingInt=IntSummaryStatistics{count=5, sum=19, min=1, average=3.800000, max=8}</span></span><br></pre></td></tr></table></figure><h6 id="Collectors-summingInt"><a href="#Collectors-summingInt" class="headerlink" title="Collectors.summingInt"></a><code>Collectors.summingInt</code></h6><p>求和</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Integer</span> <span class="variable">collect12</span> <span class="operator">=</span> Stream.of(<span class="number">2</span>, <span class="number">8</span>, <span class="number">1</span>, <span class="number">5</span>, <span class="number">3</span>).collect(Collectors.summingInt(e -> e));</span><br><span class="line">System.out.println(<span class="string">"summingInt="</span> + collect12);</span><br><span class="line"><span class="comment">// summingInt=19</span></span><br></pre></td></tr></table></figure><h3 id="终章"><a href="#终章" class="headerlink" title="终章"></a>终章</h3><h4 id="Stream-1"><a href="#Stream-1" class="headerlink" title="Stream"></a>Stream</h4><h5 id="不存储"><a href="#不存储" class="headerlink" title="不存储"></a>不存储</h5><p><code>Stream</code>不存储任何对象在其中,其本身只充当管道的作用,它可以从各种数据结构、数组、生成器函数或I/O流进行数据传输</p><h5 id="天然的函数式"><a href="#天然的函数式" class="headerlink" title="天然的函数式"></a>天然的函数式</h5><p>对流的操作会产生结果,但不会修改其源。例如,过滤从集合中获得的 Stream 会生成一个没有过滤元素的新 Stream,而不是从源集合中删除元素。</p><h5 id="延迟执行"><a href="#延迟执行" class="headerlink" title="延迟执行"></a>延迟执行</h5><p>许多流操作,例如过滤、映射或重复删除,可以延迟实现,从而为优化提供机会。例如,“查找具有三个连续元音的第一个字符串”不需要检查所有输入字符串。<br>流操作分为中间(流产生)操作和终端(产生价值或副作用)操作。中间操作总是懒惰的。</p><h5 id="可能无限"><a href="#可能无限" class="headerlink" title="可能无限"></a>可能无限</h5><p>虽然集合的大小是有限的,但流不需要。诸如 limit(n) 或 findFirst() 之类的短路操作可以允许对无限流的计算在有限时间内完成。</p><h5 id="消耗品"><a href="#消耗品" class="headerlink" title="消耗品"></a>消耗品</h5><p>流的元素在流的生命周期中只被访问一次。像迭代器一样,必须生成一个新流来重新访问源的相同元素。</p><h5 id="中间操作-intermediate"><a href="#中间操作-intermediate" class="headerlink" title="中间操作(intermediate)"></a>中间操作(intermediate)</h5><h6 id="无状态-Stateless"><a href="#无状态-Stateless" class="headerlink" title="无状态(Stateless)"></a>无状态(Stateless)</h6><p>无状态操作,例如filter and map,在处理新元素时不保留先前看到的元素的状态——每个元素都可以独立于对其他元素的操作进行处理.仅包含无状态中间操作的管道可以单次处理,无论是顺序的还是并行的,数据缓冲最少。</p><h6 id="有状态-stateful"><a href="#有状态-stateful" class="headerlink" title="有状态(stateful)"></a>有状态(stateful)</h6><p>有状态的操作,例如 distinct和sorted,在处理新元素时可能会合并来自先前看到的元素的状态。<br>有状态的操作可能需要在产生结果之前处理整个输入。例如,在查看流的所有元素之前,无法通过对流进行排序产生任何结果。因此,在并行计算下,一些包含有状态中间操作的管道可能需要对数据进行多次传递,或者可能需要缓冲重要数据。</p><h6 id="短路操作-short-circuiting"><a href="#短路操作-short-circuiting" class="headerlink" title="短路操作(short-circuiting)"></a>短路操作(short-circuiting)</h6><p>如果在呈现无限输入时,中间操作可能会产生有限流(如<code>limit</code>),则它是短路的。如果一个终端操作在有无限输入时可能会在有限时间内终止,那么它就是短路的。在管道中进行短路操作是无限流处理在有限时间内正常终止的必要条件,但不是充分条件。</p><h4 id="流处理过程"><a href="#流处理过程" class="headerlink" title="流处理过程"></a>流处理过程</h4><h5 id="UML"><a href="#UML" class="headerlink" title="UML"></a>UML</h5><p><img src="http://img.janwarlen.com/blog/StreamAPI.png" alt="Stream类图"></p><h5 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用本文最开始案例进行展示</span></span><br><span class="line">sum = cities</span><br><span class="line"> .stream()</span><br><span class="line"> .mapToInt(e -> {</span><br><span class="line"> System.out.println(<span class="string">"map:"</span> + e.getName());</span><br><span class="line"> <span class="keyword">return</span> e.getPopulation();</span><br><span class="line"> })</span><br><span class="line"> .filter(population -> {</span><br><span class="line"> System.out.println(<span class="string">"filter:"</span> + population);</span><br><span class="line"> <span class="keyword">return</span> population > <span class="number">100_000</span>;</span><br><span class="line"> })</span><br><span class="line"> <span class="comment">// sum 内部也是通过reduce实现,此处用reduce是为了输出日志更好地展示过程</span></span><br><span class="line"> .reduce(<span class="number">0</span>, (i1, i2) -> {</span><br><span class="line"> System.out.println(<span class="string">"reduce:"</span> + i1 + <span class="string">" "</span> + i2);</span><br><span class="line"> <span class="keyword">return</span> i1 + i2;</span><br><span class="line"> });</span><br><span class="line">System.out.println(<span class="string">"Stream Sum = "</span> + sum);</span><br><span class="line"></span><br><span class="line"><span class="comment">// map:1</span></span><br><span class="line"><span class="comment">// filter:50000</span></span><br><span class="line"><span class="comment">// map:2</span></span><br><span class="line"><span class="comment">// filter:2100000</span></span><br><span class="line"><span class="comment">// reduce:0 2100000</span></span><br><span class="line"><span class="comment">// map:3</span></span><br><span class="line"><span class="comment">// filter:90000</span></span><br><span class="line"><span class="comment">// map:4</span></span><br><span class="line"><span class="comment">// filter:130000</span></span><br><span class="line"><span class="comment">// reduce:2100000 130000</span></span><br><span class="line"><span class="comment">// map:5</span></span><br><span class="line"><span class="comment">// filter:1000000</span></span><br><span class="line"><span class="comment">// reduce:2230000 1000000</span></span><br><span class="line"><span class="comment">// Stream Sum = 3230000</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><ol><li>通过集合<code>cities</code>的函数<code>stream()</code>创建一个新的<code>Stream</code>,临时称作<code>s1</code></li><li><code>s1</code>通过函数<code>mapToInt</code>在自己的基础上创建一个新的<code>Stream</code>,临时称作<code>s2</code>,并将<code>s1</code>设置为<code>s2</code>的上游<code>previousStage</code></li><li><code>s2</code>通过函数<code>filter</code>在自己的基础上创建一个新的<code>Stream</code>,临时称作<code>s3</code>,并将<code>s2</code>设置为<code>s3</code>的上游<code>previousStage</code></li><li><code>s3</code>调用函数<code>reduce</code>触发结算操作,结算过程有两个核心点</li><li><code>java.util.stream.AbstractPipeline#wrapSink</code>通过该函数将结算操作前的所有中间操作逆序遍历(通过<code>previousStage</code>),生成一个从上到下的新操作链<code>Sink</code></li><li><code>java.util.Spliterator#forEachRemaining</code>通过该函数将遍历数据源,并针对所有元素应用<code>Sink</code>(此时<code>Sink</code>是一条操作链,元素将会按顺序执行,直到结束或被中间操作剔除,如<code>filter</code>)</li><li>最终返回时,在<code>java.util.stream.ReduceOps.ReduceOp#evaluateSequential</code>中,通过对返回结果的<code>get()</code>函数取出最终返回结果</li><li>中间操作的叠加与结算时操作链的生成可以通过<code>装饰者模式</code>去理解,而结算时<code>downstream.accept(mapper.applyAsInt(u));</code>可以看出,先处理自己的操作<code>mapper.applyAsInt(u)</code>,然后再将结果传递给下一个中间操作<code>downstream.accept</code><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> IntStream <span class="title function_">mapToInt</span><span class="params">(ToIntFunction<? <span class="built_in">super</span> P_OUT> mapper)</span> {</span><br><span class="line"> Objects.requireNonNull(mapper);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">IntPipeline</span>.StatelessOp<P_OUT>(<span class="built_in">this</span>, StreamShape.REFERENCE,</span><br><span class="line"> StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> Sink<P_OUT> <span class="title function_">opWrapSink</span><span class="params">(<span class="type">int</span> flags, Sink<Integer> sink)</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Sink</span>.ChainedReference<P_OUT, Integer>(sink) {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">accept</span><span class="params">(P_OUT u)</span> {</span><br><span class="line"> downstream.accept(mapper.applyAsInt(u));</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></pre></td></tr></table></figure></li></ol><h4 id="parallelStream"><a href="#parallelStream" class="headerlink" title="parallelStream"></a>parallelStream</h4><h5 id="通过-fork-x2F-join-框架执行"><a href="#通过-fork-x2F-join-框架执行" class="headerlink" title="通过 fork/join 框架执行"></a>通过 fork/join 框架执行</h5><p>Terminal操作会在<code>java.util.stream.AbstractPipeline#evaluate(java.util.stream.TerminalOp<E_OUT,R>)</code>中判断是否是<code>parallel</code>,如果是,则会通过调用<code>ReduceTask</code>的invoke函数执行流处理,下方堆栈是我在生成操作链是通过断点,使用<code>Thread.currentThread().getStackTrace()</code>得出</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">0</span> = {StackTraceElement@<span class="number">821</span>} <span class="string">"java.lang.Thread.getStackTrace(Thread.java:1559)"</span></span><br><span class="line"><span class="number">1</span> = {StackTraceElement@<span class="number">822</span>} <span class="string">"java.util.stream.AbstractPipeline.wrapSink(AbstractPipeline.java:517)"</span></span><br><span class="line"><span class="number">2</span> = {StackTraceElement@<span class="number">823</span>} <span class="string">"java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)"</span></span><br><span class="line"><span class="number">3</span> = {StackTraceElement@<span class="number">824</span>} <span class="string">"java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:747)"</span></span><br><span class="line"><span class="number">4</span> = {StackTraceElement@<span class="number">825</span>} <span class="string">"java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:721)"</span></span><br><span class="line"><span class="number">5</span> = {StackTraceElement@<span class="number">826</span>} <span class="string">"java.util.stream.AbstractTask.compute(AbstractTask.java:316)"</span></span><br><span class="line"><span class="number">6</span> = {StackTraceElement@<span class="number">827</span>} <span class="string">"java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)"</span></span><br><span class="line"><span class="number">7</span> = {StackTraceElement@<span class="number">828</span>} <span class="string">"java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:289)"</span></span><br><span class="line"><span class="number">8</span> = {StackTraceElement@<span class="number">829</span>} <span class="string">"java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)"</span></span><br><span class="line"><span class="number">9</span> = {StackTraceElement@<span class="number">830</span>} <span class="string">"java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:401)"</span></span><br><span class="line"><span class="number">10</span> = {StackTraceElement@<span class="number">831</span>} <span class="string">"java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:734)"</span></span><br><span class="line"><span class="number">11</span> = {StackTraceElement@<span class="number">832</span>} <span class="string">"java.util.stream.ReduceOps$ReduceOp.evaluateParallel(ReduceOps.java:714)"</span></span><br><span class="line"><span class="number">12</span> = {StackTraceElement@<span class="number">833</span>} <span class="string">"java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)"</span></span><br><span class="line"><span class="number">13</span> = {StackTraceElement@<span class="number">834</span>} <span class="string">"java.util.stream.IntPipeline.reduce(IntPipeline.java:457)"</span></span><br><span class="line"><span class="number">14</span> = {StackTraceElement@<span class="number">835</span>} <span class="string">"com.janwarlen.jdk8.stream.StreamCasesCityPopulationSum.main(StreamCasesCityPopulationSum.java:29)"</span></span><br></pre></td></tr></table></figure><h5 id="Task类继承UML"><a href="#Task类继承UML" class="headerlink" title="Task类继承UML"></a>Task类继承UML</h5><p>注意:并非所有<code>parallel</code>都是调用<code>ReduceTask</code>,其他类还有<code>FindTask</code>、<code>ForEachOrderedTask</code>、<code>ForEachTask</code>和<code>MatchTask</code><br><img src="http://img.janwarlen.com/blog/parallelStreamTaskUML.png" alt="最终任务执行类UML"></p><h5 id="自定义ForkJoinPool"><a href="#自定义ForkJoinPool" class="headerlink" title="自定义ForkJoinPool"></a>自定义ForkJoinPool</h5><p><code>ParallelStreams</code> 默认使用 <code>ForkJoinPool.commonPool()</code>线程池。如果需要指定线程池,可参照如下操作:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ForkJoinPool</span> <span class="variable">customThreadPool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ForkJoinPool</span>(<span class="number">4</span>);</span><br><span class="line"><span class="type">long</span> <span class="variable">actualTotal</span> <span class="operator">=</span> customThreadPool.submit(() -> roster.parallelStream().reduce(<span class="number">0</span>, Integer::sum)).get();</span><br></pre></td></tr></table></figure><h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><ol><li>不建议将stream流操作分割为一步一步操作,这将会产生stream的临时变量,而stream对象是仅可操作一次的,这存在反复操作风险,同理也不建议作为方法函数的入参<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">List<City> cities = StreamCasesCityPopulationSum.getCities();</span><br><span class="line"><span class="type">IntStream</span> <span class="variable">mapped</span> <span class="operator">=</span> cities.stream().mapToInt(City::getPopulation);</span><br><span class="line"><span class="type">int</span> <span class="variable">all</span> <span class="operator">=</span> mapped.sum();</span><br><span class="line">System.out.println(<span class="string">"all="</span> + all);</span><br><span class="line"><span class="type">IntStream</span> <span class="variable">filted</span> <span class="operator">=</span> mapped.filter(population -> population > <span class="number">100_000</span>);</span><br><span class="line"><span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> filted.sum();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 控制台将会输出</span></span><br><span class="line"></span><br><span class="line">all=<span class="number">3370000</span></span><br><span class="line">Exception in thread <span class="string">"main"</span> java.lang.IllegalStateException: stream has already been operated upon or closed</span><br><span class="line">at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:<span class="number">203</span>)</span><br><span class="line">at java.util.stream.IntPipeline.<init>(IntPipeline.java:<span class="number">91</span>)</span><br><span class="line">at java.util.stream.IntPipeline$StatelessOp.<init>(IntPipeline.java:<span class="number">594</span>)</span><br><span class="line">at java.util.stream.IntPipeline$<span class="number">9.</span><init>(IntPipeline.java:<span class="number">333</span>)</span><br><span class="line">at java.util.stream.IntPipeline.filter(IntPipeline.java:<span class="number">332</span>)</span><br><span class="line">at com.janwarlen.jdk8.stream.StreamAttention.main(StreamAttention.java:<span class="number">13</span>)</span><br></pre></td></tr></table></figure></li><li>Stream流拆分或使用非线程安全的共享变量,则会存在数据在流处理过程中变更风险</li><li>在流处理中,尽量避免对元素的状态操作<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Set<Integer> seen = Collections.synchronizedSet(<span class="keyword">new</span> <span class="title class_">HashSet</span><>());</span><br><span class="line">stream.parallel().map(e -> { <span class="keyword">if</span> (seen.add(e)) <span class="keyword">return</span> <span class="number">0</span>; <span class="keyword">else</span> <span class="keyword">return</span> e; })</span><br></pre></td></tr></table></figure></li><li>尽量避免<code>Side-effects</code>操作,如forEach/peek等,尤其是<code>parallel</code>模式下,可能会导致线程不安全等问题<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">ArrayList<String> results = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line">stream.filter(s -> pattern.matcher(s).matches())</span><br><span class="line"> .forEach(s -> results.add(s)); <span class="comment">// Unnecessary use of side-effects!</span></span><br><span class="line"></span><br><span class="line">List<String>results =</span><br><span class="line">stream.filter(s -> pattern.matcher(s).matches())</span><br><span class="line"> .collect(Collectors.toList()); <span class="comment">// No side-effects!</span></span><br></pre></td></tr></table></figure></li><li>对顺序不敏感的流处理,可使用<code>unordered()</code>提高部分有状态或终端操作的并行性</li><li>仅在对顺序无感的流处理中使用<code>parallel</code>模式</li><li><code>parallel</code>模式默认情况下公用一个线程池,因此需要避免I/O操作,以免阻塞其他并行流,或自定义一个线程池,单独提交并行流</li><li>并行流中Map操作推荐使用<code>Concurrent</code>,如<code>groupingByConcurrent</code>替换<code>groupingBy</code>、<code>ConcurrentMap</code>替换<code>Map</code>、<code>Collectors.toConcurrentMap</code>替换<code>Collectors.toMap</code>等等,理由是在并行流中<code>Concurrent</code>在性能消耗方面要好很多</li><li>并行模式下避免使用有状态的lambda表达式,如下所示,与<em>注意事项4</em>相同<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">List<Integer> parallelStorage = Collections.synchronizedList(</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">ArrayList</span><>());</span><br><span class="line">Stream.of(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>).parallel()</span><br><span class="line"> <span class="comment">// Don't do this! It uses a stateful lambda expression.</span></span><br><span class="line"> .map(e -> { parallelStorage.add(e); <span class="keyword">return</span> e; })</span><br><span class="line"> .forEachOrdered(e -> System.out.print(e + <span class="string">" "</span>));</span><br><span class="line">System.out.println(parallelStorage);</span><br><span class="line"><span class="comment">// 每次运行,parallelStorage中存储顺序均不相同</span></span><br><span class="line"><span class="comment">// 1 2 3 4 5 [1, 3, 4, 2, 5]</span></span><br><span class="line"><span class="comment">// 1 2 3 4 5 [1, 3, 2, 5, 4]</span></span><br><span class="line"><span class="comment">// 推荐使用collect</span></span><br><span class="line">List<Integer> collect13 = Stream.of(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>).parallel().collect(Collectors.toList());</span><br><span class="line">System.out.println(collect13);</span><br><span class="line"><span class="comment">// [1, 2, 3, 4, 5]</span></span><br><span class="line"><span class="comment">// 每次运行结果不变</span></span><br></pre></td></tr></table></figure></li></ol>]]></content>
<summary type="html"><h3 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h3><ol>
<li><a href="https://dev.java/learn/the-stream-api/">Java官方学习手册-Stream</a></li>
<li><a href="https://github.com/CarpenterLee/JavaLambdaInternals">GitHub-CarpenterLee-JavaLambdaInternals</a></li>
<li><a href="https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html">Oracle关于parallelism官方文档</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#package.description">Oracle关于Stream包官方描述文档</a></summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="JDK8" scheme="http://janwarlen.com/tags/JDK8/"/>
<category term="StreamAPI" scheme="http://janwarlen.com/tags/StreamAPI/"/>
</entry>
<entry>
<title>Java版本特性-Lambda</title>
<link href="http://janwarlen.com/2022/06/07/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-Lambda/"/>
<id>http://janwarlen.com/2022/06/07/Java%E7%89%88%E6%9C%AC%E7%89%B9%E6%80%A7-Lambda/</id>
<published>2022-06-07T10:05:32.000Z</published>
<updated>2022-09-02T02:49:26.691Z</updated>
<content type="html"><![CDATA[<h4 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h4><ol><li><a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27">Oracle官方Lambda文档</a></li><li><a href="https://github.com/CarpenterLee/JavaLambdaInternals">GitHub-CarpenterLee-JavaLambdaInternals</a><span id="more"></span><h3 id="Lambda"><a href="#Lambda" class="headerlink" title="Lambda"></a>Lambda</h3></li></ol><h4 id="模型"><a href="#模型" class="headerlink" title="模型"></a>模型</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LambdaParameters -> LambdaBody</span><br></pre></td></tr></table></figure><h4 id="参数"><a href="#参数" class="headerlink" title="参数"></a>参数</h4><h5 id="无入参案例"><a href="#无入参案例" class="headerlink" title="无入参案例"></a>无入参案例</h5><h6 id="JDK7匿名内部类版本"><a href="#JDK7匿名内部类版本" class="headerlink" title="JDK7匿名内部类版本:"></a>JDK7匿名内部类版本:</h6><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"><span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() {</span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span>{</span><br><span class="line">System.out.println(<span class="string">"启动"</span>);</span><br><span class="line">}</span><br><span class="line">}).start();</span><br></pre></td></tr></table></figure><h6 id="JDK8-Lambda版本"><a href="#JDK8-Lambda版本" class="headerlink" title="JDK8 Lambda版本:"></a>JDK8 Lambda版本:</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -> System.out.println(<span class="string">"起飞"</span>)).start();</span><br></pre></td></tr></table></figure><h5 id="一个入参案例"><a href="#一个入参案例" class="headerlink" title="一个入参案例"></a>一个入参案例</h5><h6 id="自定义接口类"><a href="#自定义接口类" class="headerlink" title="自定义接口类"></a>自定义接口类</h6><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"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">LambdaSingleParam</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(<span class="type">int</span> i)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h6 id="自定义调用类"><a href="#自定义调用类" class="headerlink" title="自定义调用类"></a>自定义调用类</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LambdaFuncAccepter</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">testFunc</span><span class="params">(LambdaSingleParam lambda)</span> {</span><br><span class="line"> lambda.test();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h6 id="JDK7-匿名类版本"><a href="#JDK7-匿名类版本" class="headerlink" title="JDK7 匿名类版本"></a>JDK7 匿名类版本</h6><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">LambdaFuncAccepter.testFunc(<span class="keyword">new</span> <span class="title class_">LambdaSingleParam</span>() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(<span class="type">int</span> i)</span> {</span><br><span class="line"> System.out.println(i);</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h6 id="JDK8-Lambda版本-1"><a href="#JDK8-Lambda版本-1" class="headerlink" title="JDK8 Lambda版本"></a>JDK8 Lambda版本</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">LambdaFuncAccepter.testFunc((LambdaSingleParam) System.out::println);</span><br><span class="line"><span class="comment">// 当接收lambda表达式的类存在重载时,才需要显示的强转辅助编译器确认调用函数</span></span><br><span class="line"><span class="comment">// 当不存在重载时,直接可以使用 LambdaFuncAccepter.testFunc(System.out::println);</span></span><br><span class="line"><span class="comment">// 单个参数场景下的 System.out.println(i); 可以简写为 System.out::println </span></span><br><span class="line"><span class="comment">// 一般情况下,表达式类似于 a -> System.out.println(a); 参数的() 可以省略</span></span><br></pre></td></tr></table></figure><h5 id="多入参案例"><a href="#多入参案例" class="headerlink" title="多入参案例"></a>多入参案例</h5><h6 id="自定义接口类-1"><a href="#自定义接口类-1" class="headerlink" title="自定义接口类"></a>自定义接口类</h6><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"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">LambdaMultipleParams</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h6 id="自定义调用类-1"><a href="#自定义调用类-1" class="headerlink" title="自定义调用类"></a>自定义调用类</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LambdaFuncAccepter</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">testFunc</span><span class="params">(LambdaMultipleParams lambda)</span> {</span><br><span class="line"> lambda.test(<span class="number">1</span>, <span class="number">2</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h6 id="JDK7-匿名类版本-1"><a href="#JDK7-匿名类版本-1" class="headerlink" title="JDK7 匿名类版本"></a>JDK7 匿名类版本</h6><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">LambdaFuncAccepter.testFunc(<span class="keyword">new</span> <span class="title class_">LambdaMultipleParams</span>() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">test</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span> {</span><br><span class="line"> System.out.println(x + y);</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h6 id="JDK8-Lambda版本-2"><a href="#JDK8-Lambda版本-2" class="headerlink" title="JDK8 Lambda版本"></a>JDK8 Lambda版本</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">LambdaFuncAccepter.testFunc((a, b) -> System.out.println(a + b));</span><br><span class="line"><span class="comment">// 多参数场景下,入参的 '()' 必须存在</span></span><br></pre></td></tr></table></figure><h4 id="方法体"><a href="#方法体" class="headerlink" title="方法体"></a>方法体</h4><h5 id="单条执行语句"><a href="#单条执行语句" class="headerlink" title="单条执行语句"></a>单条执行语句</h5><h6 id="无return案例"><a href="#无return案例" class="headerlink" title="无return案例"></a>无return案例</h6><p><a href="#%E6%97%A0%E5%85%A5%E5%8F%82%E6%A1%88%E4%BE%8B">同无入参案例</a></p><h6 id="有return案例"><a href="#有return案例" class="headerlink" title="有return案例"></a>有return案例</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">LambdaWIthReturnNoParam</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">test</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"><span class="comment">// () -> 233 ,其中return可以省略</span></span><br><span class="line"><span class="type">LambdaWIthReturnNoParam</span> <span class="variable">lambdaWIthReturnNoParam</span> <span class="operator">=</span> () -> <span class="number">233</span>;</span><br><span class="line">System.out.println(lambdaWIthReturnNoParam.test());</span><br></pre></td></tr></table></figure><h5 id="多条执行语句"><a href="#多条执行语句" class="headerlink" title="多条执行语句"></a>多条执行语句</h5><h6 id="无return案例-1"><a href="#无return案例-1" class="headerlink" title="无return案例"></a>无return案例</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用runable接口</span></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="keyword">new</span> <span class="title class_">Thread</span>(() -> {</span><br><span class="line"> System.out.println(<span class="string">"line1"</span>);</span><br><span class="line"> System.out.println(<span class="string">"line2"</span>);</span><br><span class="line">}).start();</span><br></pre></td></tr></table></figure><h6 id="有return案例-1"><a href="#有return案例-1" class="headerlink" title="有return案例"></a>有return案例</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用 LambdaWIthReturnNoParam 接口</span></span><br><span class="line"><span class="comment">// return 也属于执行语句</span></span><br><span class="line"><span class="comment">// 多条语句场景 return 不可省略</span></span><br><span class="line"><span class="type">LambdaWIthReturnNoParam</span> <span class="variable">lambdaWIthReturnNoParam</span> <span class="operator">=</span> () -> {</span><br><span class="line"> System.out.println(<span class="string">"line1"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">2022</span>;</span><br><span class="line">};</span><br><span class="line">System.out.println(lambdaWIthReturnNoParam.test());</span><br></pre></td></tr></table></figure><h4 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h4><ol><li>lambda表达式的使用必须要有对应的函数接口,并且该接口有且仅有一个无默认实现的函数(当接口的其他函数均由默认实现时,该接口也可使用lambda,如<code>Comparator</code>)<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">Integer[] a = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>};</span><br><span class="line">Arrays.sort(a, (o1, o2) -> o2 - o1);</span><br><span class="line"><span class="comment">// 也可写成,不过Integer内置是固定升序的,无法修改</span></span><br><span class="line">Arrays.sort(a, Integer::compare);</span><br></pre></td></tr></table></figure></li><li>多参数场景下,要么都声明入参类型,要么都不声明,不允许一部分声明,一部分隐藏<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">LambdaFuncAccepter.testFunc((a, <span class="type">int</span> b) -> System.out.println(a + b));</span><br><span class="line"><span class="comment">// 此时编译器会报错</span></span><br></pre></td></tr></table></figure></li><li>final 不能修饰推断类型入参,如需使用final,需要将入参显示设置类型<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"><span class="comment">// 编译器会提示异常</span></span><br><span class="line">LambdaFuncAccepter.testFunc((a, <span class="keyword">final</span> b) -> System.out.println(a + b));</span><br><span class="line"><span class="comment">// 需调整为如下所示:</span></span><br><span class="line">LambdaFuncAccepter.testFunc((<span class="type">int</span> a, <span class="keyword">final</span> <span class="type">int</span> b) -> System.out.println(a + b));</span><br></pre></td></tr></table></figure></li><li>lambda方法体使用外部变量时,必须遵守声明时赋值后续不可赋值规则(同final修饰规则),但调用对象函数修改对象内部状态合法<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">StringBuilder</span> <span class="variable">sb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -> {</span><br><span class="line"> <span class="comment">// Variable used in lambda expression should be final or effectively final</span></span><br><span class="line"> <span class="comment">// 虽然sb没有被final声明,但是在lambda内部存在隐形的final限制</span></span><br><span class="line"> <span class="comment">// sb = new StringBuilder();</span></span><br><span class="line"> sb.append(<span class="number">111</span>);</span><br><span class="line"> System.out.println(<span class="string">"line1"</span>);</span><br><span class="line">}).start();</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="variable">y</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"><span class="comment">// 即便是在外部在初始化后再修改赋值,此时已经破坏final规则</span></span><br><span class="line">y = <span class="number">3</span>;</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -> {</span><br><span class="line"> <span class="comment">// Variable used in lambda expression should be final or effectively final</span></span><br><span class="line"> System.out.println(y);</span><br><span class="line">}).start();</span><br></pre></td></tr></table></figure></li><li>使用数组一类时,需要注意下标的使用,如下<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>[] b = {<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>};</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < b.length; i++) {</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -> {</span><br><span class="line"> <span class="comment">// Variable used in lambda expression should be final or effectively final</span></span><br><span class="line"> <span class="comment">// 此时 i 不是遵守final规则的变量</span></span><br><span class="line"> System.out.println(b[i]);</span><br><span class="line"> }).start();</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 可以修改调整如下所示:</span></span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> i : b) {</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -> {</span><br><span class="line"> <span class="comment">// 此时变量i作为临时变量还尚未经历再一次的赋值修改,因此还遵守 effectively final 规则</span></span><br><span class="line"> System.out.println(i);</span><br><span class="line"> }).start();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li>lambda方法体使用外部变量时,必须确保所有外部变量均以完成初始化(仅声明不行,如分支,必须确保变量初始化)<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> x;</span><br><span class="line"><span class="keyword">if</span> (sb.length() > <span class="number">5</span>) {</span><br><span class="line"> x = <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -> {</span><br><span class="line"> <span class="comment">// Variable 'x' might not have been initialized</span></span><br><span class="line"> System.out.println(x);</span><br><span class="line">}).start();</span><br></pre></td></tr></table></figure></li></ol>]]></content>
<summary type="html"><h4 id="引用参考文档链接"><a href="#引用参考文档链接" class="headerlink" title="引用参考文档链接"></a>引用参考文档链接</h4><ol>
<li><a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27">Oracle官方Lambda文档</a></li>
<li><a href="https://github.com/CarpenterLee/JavaLambdaInternals">GitHub-CarpenterLee-JavaLambdaInternals</a></summary>
<category term="Java" scheme="http://janwarlen.com/categories/Java/"/>
<category term="Java" scheme="http://janwarlen.com/tags/Java/"/>
<category term="Lambda" scheme="http://janwarlen.com/tags/Lambda/"/>
<category term="JDK8" scheme="http://janwarlen.com/tags/JDK8/"/>
</entry>
<entry>
<title>一书一图-Head First设计模式</title>
<link href="http://janwarlen.com/2022/06/05/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-Head%20First%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<id>http://janwarlen.com/2022/06/05/%E4%B8%80%E4%B9%A6%E4%B8%80%E5%9B%BE-Head%20First%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/</id>
<published>2022-06-04T16:01:49.000Z</published>
<updated>2022-09-02T02:49:26.822Z</updated>
<content type="html"><![CDATA[<h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol><li>本书适合刚学习编程,并对编程十分感兴趣的同学,此书无法立刻提升编码能力,仅仅只能提供认知拓展;</li><li>设计模式是一种经验总结,并非发明创造,因此不能刻版的使用,需要因地制宜,甚至有些场景下,不适用任何设计模式解决问题反而会是最佳方案;</li><li>有一定的项目编码经验的同学更适合其他学习方式,如文字版的网页菜鸟教程或者视频类其他知识分享者上传的视频教程,本人更推荐文字版的,比较方便,更能快速的了解;</li><li>学习设计模式主要核心是弄清楚要解决的问题即使用场景,最好是能根据教材/教程的案例,自己手敲一遍代码并运行去理解,仅仅靠定义去了解是远远不够的;</li><li>还想更进一步了解使用场景的同学可以去阅读各大开源项目的代码,尤其是中间件一类,因为更多会考虑性能和拓展问题,设计模式使用频率会远高于一般的业务项目;</li><li>思维导图中的一些优点缺点是结合部分网络教程的观点罗列而来,并非标准答案;</li><li>本文的思维导图仅是本人的读书笔记,不可作为权威版本去学习参考,可以简单浏览,如果感兴趣可以购买正版书籍学习;<span id="more"></span></li></ol><p><img src="http://img.janwarlen.com/blog/Head%20First%20%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.png" alt="Head First设计模式"></p>]]></content>
<summary type="html"><h4 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h4><ol>
<li>本书适合刚学习编程,并对编程十分感兴趣的同学,此书无法立刻提升编码能力,仅仅只能提供认知拓展;</li>
<li>设计模式是一种经验总结,并非发明创造,因此不能刻版的使用,需要因地制宜,甚至有些场景下,不适用任何设计模式解决问题反而会是最佳方案;</li>
<li>有一定的项目编码经验的同学更适合其他学习方式,如文字版的网页菜鸟教程或者视频类其他知识分享者上传的视频教程,本人更推荐文字版的,比较方便,更能快速的了解;</li>
<li>学习设计模式主要核心是弄清楚要解决的问题即使用场景,最好是能根据教材&#x2F;教程的案例,自己手敲一遍代码并运行去理解,仅仅靠定义去了解是远远不够的;</li>
<li>还想更进一步了解使用场景的同学可以去阅读各大开源项目的代码,尤其是中间件一类,因为更多会考虑性能和拓展问题,设计模式使用频率会远高于一般的业务项目;</li>
<li>思维导图中的一些优点缺点是结合部分网络教程的观点罗列而来,并非标准答案;</li>
<li>本文的思维导图仅是本人的读书笔记,不可作为权威版本去学习参考,可以简单浏览,如果感兴趣可以购买正版书籍学习;</summary>
<category term="设计模式" scheme="http://janwarlen.com/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<category term="设计模式" scheme="http://janwarlen.com/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<category term="读书笔记" scheme="http://janwarlen.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
</entry>
</feed>