-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
2032 lines (1810 loc) · 267 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
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
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>vim操作命令大全</title>
<url>/2021/10/14/vim/</url>
<content><![CDATA[<p>曾经使用了两年多的Vim,手册也翻过一遍。虽然现在不怎么用vim了,曾经的笔记还是贴出来,与喜欢vim的朋友分享。</p>
<h1 id="1-Vim的几种模式"><a href="#1-Vim的几种模式" class="headerlink" title="1. Vim的几种模式"></a>1. Vim的几种模式</h1><ul>
<li>正常模式:可以使用快捷键命令,或按:输入命令行。</li>
<li>插入模式:可以输入文本,在正常模式下,按i、a、o等都可以进入插入模式。</li>
<li>可视模式:正常模式下按v可以进入可视模式, 在可视模式下,移动光标可以选择文本。按V进入可视行模式, 总是整行整行的选中。ctrl+v进入可视块模式。</li>
<li>替换模式:正常模式下,按R进入。</li>
</ul>
<a id="more"></a>
<h1 id="2-启动Vim"><a href="#2-启动Vim" class="headerlink" title="2. 启动Vim"></a>2. 启动Vim</h1><ul>
<li>vim -c cmd file: 在打开文件前,先执行指定的命令;</li>
<li>vim -r file: 恢复上次异常退出的文件;</li>
<li>vim -R file: 以只读的方式打开文件,但可以强制保存;</li>
<li>vim -M file: 以只读的方式打开文件,不可以强制保存;</li>
<li>vim -y num file: 将编辑窗口的大小设为num行;</li>
<li>vim + file: 从文件的末尾开始;</li>
<li>vim +num file: 从第num行开始;</li>
<li>vim +/string file: 打开file,并将光标停留在第一个找到的string上。</li>
<li>vim –remote file: 用已有的vim进程打开指定的文件。 如果你不想启用多个vim会话,这个很有用。但要注意, 如果你用vim,会寻找名叫VIM的服务器;如果你已经有一个gvim在运行了, 你可以用gvim –remote file在已有的gvim中打开文件。</li>
</ul>
<h1 id="3-文档操作"><a href="#3-文档操作" class="headerlink" title="3. 文档操作"></a>3. 文档操作</h1><ul>
<li>:e file –关闭当前编辑的文件,并开启新的文件。 如果对当前文件的修改未保存,vi会警告。</li>
<li>:e! file –放弃对当前文件的修改,编辑新的文件。</li>
<li>:e+file – 开始新的文件,并从文件尾开始编辑。</li>
<li>:e+n file – 开始新的文件,并从第n行开始编辑。</li>
<li>:enew –编译一个未命名的新文档。(CTRL-W n)</li>
<li>:e – 重新加载当前文档。</li>
<li>:e! – 重新加载当前文档,并丢弃已做的改动。</li>
<li>:e#或ctrl+^ – 回到刚才编辑的文件,很实用。</li>
<li>:f或ctrl+g – 显示文档名,是否修改,和光标位置。</li>
<li>:f filename – 改变编辑的文件名,这时再保存相当于另存为。</li>
<li>gf – 打开以光标所在字符串为文件名的文件。</li>
<li>:w – 保存修改。</li>
<li>:n1,n2w filename – 选择性保存从某n1行到另n2行的内容。</li>
<li>:wq – 保存并退出。</li>
<li>ZZ – 保存并退出。</li>
<li>:x – 保存并退出。</li>
<li>:q[uit] ——退出当前窗口。(CTRL-W q或CTRL-W CTRL-Q)</li>
<li>:saveas newfilename – 另存为</li>
<li>:browse e – 会打开一个文件浏览器让你选择要编辑的文件。 如果是终端中,则会打开netrw的文件浏览窗口; 如果是gvim,则会打开一个图形界面的浏览窗口。 实际上:browse后可以跟任何编辑文档的命令,如sp等。 用browse打开的起始目录可以由browsedir来设置:<ul>
<li>:set browsedir=last – 用上次访问过的目录(默认);</li>
<li>:set browsedir=buffer – 用当前文件所在目录;</li>
<li>:set browsedir=current – 用当前工作目录;</li>
</ul>
</li>
<li>:Sex – 水平分割一个窗口,浏览文件系统;</li>
<li>:Vex – 垂直分割一个窗口,浏览文件系统;</li>
</ul>
<h1 id="4-光标的移动"><a href="#4-光标的移动" class="headerlink" title="4. 光标的移动"></a>4. 光标的移动</h1><h2 id="4-1-基本移动"><a href="#4-1-基本移动" class="headerlink" title="4.1 基本移动"></a>4.1 基本移动</h2><p>以下移动都是在normal模式下。</p>
<ul>
<li>h或退格: 左移一个字符;</li>
<li>l或空格: 右移一个字符;</li>
<li>j: 下移一行;</li>
<li>k: 上移一行;</li>
<li>gj: 移动到一段内的下一行;</li>
<li>gk: 移动到一段内的上一行;</li>
<li>+或Enter: 把光标移至下一行第一个非空白字符。</li>
<li>-: 把光标移至上一行第一个非空白字符。</li>
<li>w: 前移一个单词,光标停在下一个单词开头;</li>
<li>W: 移动下一个单词开头,但忽略一些标点;</li>
<li>e: 前移一个单词,光标停在下一个单词末尾;</li>
<li>E: 移动到下一个单词末尾,如果词尾有标点,则移动到标点;</li>
<li>b: 后移一个单词,光标停在上一个单词开头;</li>
<li>B: 移动到上一个单词开头,忽略一些标点;</li>
<li>ge: 后移一个单词,光标停在上一个单词末尾;</li>
<li>gE: 同 ge ,不过‘单词’包含单词相邻的标点。</li>
<li>(: 前移1句。</li>
<li>): 后移1句。</li>
<li>{: 前移1段。</li>
<li>}: 后移1段。</li>
<li>fc: 把光标移到同一行的下一个c字符处</li>
<li>Fc: 把光标移到同一行的上一个c字符处</li>
<li>tc: 把光标移到同一行的下一个c字符前</li>
<li>Tc: 把光标移到同一行的上一个c字符后</li>
<li>;: 配合f & t使用,重复一次</li>
<li>,: 配合f & t使用,反向重复一次</li>
</ul>
<p>上面的操作都可以配合n使用,比如在正常模式(下面会讲到)下输入3h, 则光标向左移动3个字符。</p>
<ul>
<li>0: 移动到行首。</li>
<li>g0: 移到光标所在屏幕行行首。</li>
<li>^: 移动到本行第一个非空白字符。</li>
<li>g^: 同 ^ ,但是移动到当前屏幕行第一个非空字符处。</li>
<li>: 移动光标所在屏幕行行尾。</li>
<li>n|: 把光标移到递n列上。</li>
<li>nG: 到文件第n行。</li>
<li>:n<cr> 移动到第n行。</li>
<li>:$<cr> 移动到最后一行。</li>
<li>H: 把光标移到屏幕最顶端一行。</li>
<li>M: 把光标移到屏幕中间一行。</li>
<li>L: 把光标移到屏幕最底端一行。</li>
<li>gg: 到文件头部。</li>
<li>G: 到文件尾部。</li>
</ul>
<h2 id="4-2-翻屏"><a href="#4-2-翻屏" class="headerlink" title="4.2 翻屏"></a>4.2 翻屏</h2><ul>
<li>ctrl+f: 下翻一屏。</li>
<li>ctrl+b: 上翻一屏。</li>
<li>ctrl+d: 下翻半屏。</li>
<li>ctrl+u: 上翻半屏。</li>
<li>ctrl+e: 向下滚动一行。</li>
<li>ctrl+y: 向上滚动一行。</li>
<li>n%: 到文件n%的位置。</li>
<li>zz: 将当前行移动到屏幕中央。</li>
<li>zt: 将当前行移动到屏幕顶端。</li>
<li>zb: 将当前行移动到屏幕底端。</li>
</ul>
<h2 id="4-3-标记"><a href="#4-3-标记" class="headerlink" title="4.3 标记"></a>4.3 标记</h2><p>使用标记可以快速移动。到达标记后,可以用Ctrl+o返回原来的位置。 Ctrl+o和Ctrl+i 很像浏览器上的 后退 和 前进 。</p>
<ul>
<li>m{a-z}: 标记光标所在位置,局部标记,只用于当前文件。</li>
<li>m{A-Z}: 标记光标所在位置,全局标记。标记之后,退出Vim, 重新启动,标记仍然有效。</li>
<li>`{a-z}: 移动到标记位置。</li>
<li>‘{a-z}: 移动到标记行的行首。</li>
<li>`{0-9}:回到上[2-10]次关闭vim时最后离开的位置。</li>
<li>“: 移动到上次编辑的位置。”也可以,不过“精确到列,而”精确到行 。如果想跳转到更老的位置,可以按C-o,跳转到更新的位置用C-i。</li>
<li>`”: 移动到上次离开的地方。</li>
<li>`.: 移动到最后改动的地方。</li>
<li>:marks 显示所有标记。</li>
<li>:delmarks a b – 删除标记a和b。</li>
<li>:delmarks a-c – 删除标记a、b和c。</li>
<li>:delmarks a c-f – 删除标记a、c、d、e、f。</li>
<li>:delmarks! – 删除当前缓冲区的所有标记。</li>
<li>:help mark-motions 查看更多关于mark的知识。</li>
</ul>
<h1 id="5-插入文本"><a href="#5-插入文本" class="headerlink" title="5. 插入文本"></a>5. 插入文本</h1><h2 id="5-1-基本插入"><a href="#5-1-基本插入" class="headerlink" title="5.1 基本插入"></a>5.1 基本插入</h2><ul>
<li>i: 在光标前插入;一个小技巧:按8,再按i,进入插入模式,输入=, 按esc进入命令模式,就会出现8个=。 这在插入分割线时非常有用,如30i+<esc>就插入了36个+组成的分割线。</li>
<li>I: 在当前行第一个非空字符前插入;</li>
<li>gI: 在当前行第一列插入;</li>
<li>a: 在光标后插入;</li>
<li>A: 在当前行最后插入;</li>
<li>o: 在下面新建一行插入;</li>
<li>O: 在上面新建一行插入;</li>
<li>:r filename在当前位置插入另一个文件的内容。</li>
<li>:[n]r filename在第n行插入另一个文件的内容。</li>
<li>:r !date 在光标处插入当前日期与时间。同理,:r !command可以将其它shell命令的输出插入当前文档。</li>
</ul>
<h2 id="5-2-改写插入"><a href="#5-2-改写插入" class="headerlink" title="5.2 改写插入"></a>5.2 改写插入</h2><ul>
<li>c[n]w: 改写光标后1(n)个词。</li>
<li>c[n]l: 改写光标后n个字母。</li>
<li>c[n]h: 改写光标前n个字母。</li>
<li>[n]cc: 修改当前[n]行。</li>
<li>[n]s: 以输入的文本替代光标之后1(n)个字符,相当于c[n]l。</li>
<li>[n]S: 删除指定数目的行,并以所输入文本代替之。</li>
</ul>
<p>注意,类似cnw,dnw,ynw的形式同样可以写为ncw,ndw,nyw。</p>
<h1 id="6-剪切复制和寄存器"><a href="#6-剪切复制和寄存器" class="headerlink" title="6. 剪切复制和寄存器"></a>6. 剪切复制和寄存器</h1><h2 id="6-1-剪切和复制、粘贴"><a href="#6-1-剪切和复制、粘贴" class="headerlink" title="6.1 剪切和复制、粘贴"></a>6.1 剪切和复制、粘贴</h2><ul>
<li>[n]x: 剪切光标右边n个字符,相当于d[n]l。</li>
<li>[n]X: 剪切光标左边n个字符,相当于d[n]h。</li>
<li>y: 复制在可视模式下选中的文本。</li>
<li>yy or Y: 复制整行文本。</li>
<li>y[n]w: 复制一(n)个词。</li>
<li>y[n]l: 复制光标右边1(n)个字符。</li>
<li>y[n]h: 复制光标左边1(n)个字符。</li>
<li>yor D: 删除(剪切)当前位置到行尾的内容。</li>
<li>d[n]w: 删除(剪切)1(n)个单词</li>
<li>d[n]l: 删除(剪切)光标右边1(n)个字符。</li>
<li>d[n]h: 删除(剪切)光标左边1(n)个字符。</li>
<li>d0: 删除(剪切)当前位置到行首的内容</li>
<li>[n] dd: 删除(剪切)1(n)行。</li>
<li>:m,nd<cr> 剪切m行到n行的内容。</li>
<li>d1G或dgg: 剪切光标以上的所有行。</li>
<li>dG: 剪切光标以下的所有行。</li>
<li>daw和das:剪切一个词和剪切一个句子,即使光标不在词首和句首也没关系。</li>
<li>d/f<cr>:这是一个比较高级的组合命令,它将删除当前位置 到下一个f之间的内容。</li>
<li>p: 在光标之后粘贴。</li>
<li>P: 在光标之前粘贴。</li>
</ul>
<h2 id="6-2-文本对象"><a href="#6-2-文本对象" class="headerlink" title="6.2 文本对象"></a>6.2 文本对象</h2><ul>
<li>aw:一个词</li>
<li>as:一句。</li>
<li>ap:一段。</li>
<li>ab:一块(包含在圆括号中的)。</li>
</ul>
<p>y, d, c, v都可以跟文本对象。</p>
<h2 id="6-3-寄存器"><a href="#6-3-寄存器" class="headerlink" title="6.3 寄存器"></a>6.3 寄存器</h2><ul>
<li>a-z:都可以用作寄存器名。”ayy把当前行的内容放入a寄存器。</li>
<li>A-Z:用大写字母索引寄存器,可以在寄存器中追加内容。 如”Ayy把当前行的内容追加到a寄存器中。</li>
<li>:reg 显示所有寄存器的内容。</li>
<li>“”:不加寄存器索引时,默认使用的寄存器。</li>
<li>“<em>:当前选择缓冲区,”</em>yy把当前行的内容放入当前选择缓冲区。</li>
<li>“+:系统剪贴板。”+yy把当前行的内容放入系统剪贴板。</li>
</ul>
<h1 id="7-查找与替换"><a href="#7-查找与替换" class="headerlink" title="7. 查找与替换"></a>7. 查找与替换</h1><h2 id="7-1-查找"><a href="#7-1-查找" class="headerlink" title="7.1 查找"></a>7.1 查找</h2><ul>
<li>/something: 在后面的文本中查找something。</li>
<li>?something: 在前面的文本中查找something。</li>
<li>/pattern/+number: 将光标停在包含pattern的行后面第number行上。</li>
<li>/pattern/-number: 将光标停在包含pattern的行前面第number行上。</li>
<li>n: 向后查找下一个。</li>
<li>N: 向前查找下一个。</li>
</ul>
<p>可以用grep或vimgrep查找一个模式都在哪些地方出现过,</p>
<p>其中:grep是调用外部的grep程序,而:vimgrep是vim自己的查找算法。</p>
<p>用法为: :vim[grep]/pattern/[g] [j] files</p>
<p>g的含义是如果一个模式在一行中多次出现,则这一行也在结果中多次出现。</p>
<p>j的含义是grep结束后,结果停在第j项,默认是停在第一项。</p>
<p>vimgrep前面可以加数字限定搜索结果的上限,如</p>
<p>:1vim/pattern/ % 只查找那个模式在本文件中的第一个出现。</p>
<p>其实vimgrep在读纯文本电子书时特别有用,可以生成导航的目录。</p>
<p>比如电子书中每一节的标题形式为:n. xxxx。你就可以这样:</p>
<p>:vim/^d{1,}./ %</p>
<p>然后用:cw或:copen查看结果,可以用C-w H把quickfix窗口移到左侧,</p>
<p>就更像个目录了。</p>
<h2 id="7-2-替换"><a href="#7-2-替换" class="headerlink" title="7.2 替换"></a>7.2 替换</h2><ul>
<li>:s/old/new - 用new替换当前行第一个old。</li>
<li>:s/old/new/g - 用new替换当前行所有的old。</li>
<li>:n1,n2s/old/new/g - 用new替换文件n1行到n2行所有的old。</li>
<li>:%s/old/new/g - 用new替换文件中所有的old。</li>
<li>:%s/^/xxx/g - 在每一行的行首插入xxx,^表示行首。</li>
<li>:%s/表示行尾。</li>
<li>所有替换命令末尾加上c,每个替换都将需要用户确认。 如:%s/old/new/gc,加上i则忽略大小写(ignore)。</li>
</ul>
<p>还有一种比替换更灵活的方式,它是匹配到某个模式后执行某种命令,</p>
<p>语法为 :[range]g/pattern/command</p>
<p>例如 :%g/^ xyz/normal dd。</p>
<p>表示对于以一个空格和xyz开头的行执行normal模式下的dd命令。</p>
<p>关于range的规定为:</p>
<ul>
<li>如果不指定range,则表示当前行。</li>
<li>m,n: 从m行到n行。</li>
<li>0: 最开始一行(可能是这样)。</li>
<li>$: 最后一行</li>
<li>.: 当前行</li>
<li>%: 所有行</li>
</ul>
<h2 id="7-3-正则表达式"><a href="#7-3-正则表达式" class="headerlink" title="7.3 正则表达式"></a>7.3 正则表达式</h2><p>高级的查找替换就要用到正则表达式。</p>
<ul>
<li>\d: 表示十进制数(我猜的)</li>
<li>\s: 表示空格</li>
<li>\S: 非空字符</li>
<li>\a: 英文字母</li>
<li>|: 表示 或</li>
<li>.: 表示.</li>
<li>{m,n}: 表示m到n个字符。这要和 \s与\a等连用,如 \a{m,n} 表示m 到n个英文字母。</li>
<li>{m,}: 表示m到无限多个字符。</li>
<li>**: 当前目录下的所有子目录。</li>
</ul>
<p>:help pattern得到更多帮助。</p>
<hr>
<h1 id="8-排版"><a href="#8-排版" class="headerlink" title="8. 排版"></a>8. 排版</h1><h2 id="8-1-基本排版"><a href="#8-1-基本排版" class="headerlink" title="8.1 基本排版"></a>8.1 基本排版</h2><ul>
<li><< 向左缩进一个shiftwidth</li>
<li>>> 向右缩进一个shiftwidth</li>
<li>:ce(nter) 本行文字居中</li>
<li>:le(ft) 本行文字靠左</li>
<li>:ri(ght) 本行文字靠右</li>
<li>gq 对选中的文字重排,即对过长的文字进行断行</li>
<li>gqq 重排当前行</li>
<li>gqnq 重排n行</li>
<li>gqap 重排当前段</li>
<li>gqnap 重排n段</li>
<li>gqnj 重排当前行和下面n行</li>
<li>gqQ 重排当前段对文章末尾</li>
<li>J 拼接当前行和下一行</li>
<li>gJ 同 J ,不过合并后不留空格。</li>
</ul>
<h2 id="8-2-拼写检查"><a href="#8-2-拼写检查" class="headerlink" title="8.2 拼写检查"></a>8.2 拼写检查</h2><ul>
<li>:set spell-开启拼写检查功能</li>
<li>:set nospell-关闭拼写检查功能</li>
<li>]s-移到下一个拼写错误的单词</li>
<li>[s-作用与上一命令类似,但它是从相反方向进行搜索</li>
<li>z=-显示一个有关拼写错误单词的列表,可从中选择</li>
<li>zg-告诉拼写检查器该单词是拼写正确的</li>
<li>zw-与上一命令相反,告诉拼写检查器该单词是拼写错误的</li>
</ul>
<h2 id="8-3-统计字数"><a href="#8-3-统计字数" class="headerlink" title="8.3 统计字数"></a>8.3 统计字数</h2><p>g ^g可以统计文档字符数,行数。 将光标放在最后一个字符上,用字符数减去行数可以粗略统计中文文档的字数。 以上对 Mac 或 Unix 的文件格式适用。 如果是 Windows 文件格式(即换行符有两个字节),字数的统计方法为: 字符数 - 行数 * 2。</p>
<hr>
<h1 id="9-编辑多个文件"><a href="#9-编辑多个文件" class="headerlink" title="9. 编辑多个文件"></a>9. 编辑多个文件</h1><h2 id="9-1-一次编辑多个文件"><a href="#9-1-一次编辑多个文件" class="headerlink" title="9.1 一次编辑多个文件"></a>9.1 一次编辑多个文件</h2><p>我们可以一次打开多个文件,如</p>
<blockquote>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">vi a.txt b.txt c.txt</span><br></pre></td></tr></table></figure>
</blockquote>
<ul>
<li>使用:next(:n)编辑下一个文件。</li>
<li>:2n 编辑下2个文件。</li>
<li>使用:previous或:N编辑上一个文件。</li>
<li>使用:wnext,保存当前文件,并编辑下一个文件。</li>
<li>使用:wprevious,保存当前文件,并编辑上一个文件。</li>
<li>使用:args 显示文件列表。</li>
<li>:n filenames或:args filenames 指定新的文件列表。</li>
<li>vi -o filenames 在水平分割的多个窗口中编辑多个文件。</li>
<li>vi -O filenames 在垂直分割的多个窗口中编辑多个文件。</li>
</ul>
<h2 id="9-2-多标签编辑"><a href="#9-2-多标签编辑" class="headerlink" title="9.2 多标签编辑"></a>9.2 多标签编辑</h2><ul>
<li>vim -p files: 打开多个文件,每个文件占用一个标签页。</li>
<li>:tabe, tabnew – 如果加文件名,就在新的标签中打开这个文件, 否则打开一个空缓冲区。</li>
<li>^w gf – 在新的标签页里打开光标下路径指定的文件。</li>
<li>:tabn – 切换到下一个标签。Control + PageDown,也可以。</li>
<li>:tabp – 切换到上一个标签。Control + PageUp,也可以。</li>
<li>[n] gt – 切换到下一个标签。如果前面加了 n , 就切换到第n个标签。第一个标签的序号就是1。</li>
<li>:tab split – 将当前缓冲区的内容在新页签中打开。</li>
<li>:tabc[lose] – 关闭当前的标签页。</li>
<li>:tabo[nly] – 关闭其它的标签页。</li>
<li>:tabs – 列出所有的标签页和它们包含的窗口。</li>
<li>:tabm[ove] [N] – 移动标签页,移动到第N个标签页之后。 如 tabm 0 当前标签页,就会变成第一个标签页。</li>
</ul>
<h2 id="9-3-缓冲区"><a href="#9-3-缓冲区" class="headerlink" title="9.3 缓冲区"></a>9.3 缓冲区</h2><ul>
<li>:buffers或:ls或:files 显示缓冲区列表。</li>
<li>ctrl+^:在最近两个缓冲区间切换。</li>
<li>:bn – 下一个缓冲区。</li>
<li>:bp – 上一个缓冲区。</li>
<li>:bl – 最后一个缓冲区。</li>
<li>:b[n]或:[n]b – 切换到第n个缓冲区。</li>
<li>:nbw(ipeout) – 彻底删除第n个缓冲区。</li>
<li>:nbd(elete) – 删除第n个缓冲区,并未真正删除,还在unlisted列表中。</li>
<li>:ba[ll] – 把所有的缓冲区在当前页中打开,每个缓冲区占一个窗口。</li>
</ul>
<h1 id="10-分屏编辑"><a href="#10-分屏编辑" class="headerlink" title="10. 分屏编辑"></a>10. 分屏编辑</h1><ul>
<li>vim -o file1 file2:水平分割窗口,同时打开file1和file2</li>
<li>vim -O file1 file2:垂直分割窗口,同时打开file1和file2</li>
</ul>
<h2 id="10-1-水平分割"><a href="#10-1-水平分割" class="headerlink" title="10.1 水平分割"></a>10.1 水平分割</h2><ul>
<li>:split(:sp) – 把当前窗水平分割成两个窗口。(CTRL-W s 或 CTRL-W CTRL-S) 注意如果在终端下,CTRL-S可能会冻结终端,请按CTRL-Q继续。</li>
<li>:split filename – 水平分割窗口,并在新窗口中显示另一个文件。</li>
<li>:nsplit(:nsp) – 水平分割出一个n行高的窗口。</li>
<li>:[N]new – 水平分割出一个N行高的窗口,并编辑一个新文件。 (CTRL-W n或 CTRL-W CTRL-N)</li>
<li>ctrl+w f –水平分割出一个窗口,并在新窗口打开名称为光标所在词的文件 。</li>
<li>C-w C-^ – 水平分割一个窗口,打开刚才编辑的文件。</li>
</ul>
<h2 id="10-2-垂直分割"><a href="#10-2-垂直分割" class="headerlink" title="10.2 垂直分割"></a>10.2 垂直分割</h2><ul>
<li>:vsplit(:vsp) – 把当前窗口分割成水平分布的两个窗口。 (CTRL-W v或CTRL CTRL-V)</li>
<li>:[N]vne[w] – 垂直分割出一个新窗口。</li>
<li>:vertical 水平分割的命令: 相应的垂直分割。</li>
</ul>
<h2 id="10-3-关闭子窗口"><a href="#10-3-关闭子窗口" class="headerlink" title="10.3 关闭子窗口"></a>10.3 关闭子窗口</h2><ul>
<li>:qall – 关闭所有窗口,退出vim。</li>
<li>:wall – 保存所有修改过的窗口。</li>
<li>:only – 只保留当前窗口,关闭其它窗口。(CTRL-W o)</li>
<li>:close – 关闭当前窗口,CTRL-W c能实现同样的功能。 (象 :q :x同样工作 )</li>
</ul>
<h2 id="10-4-调整窗口大小"><a href="#10-4-调整窗口大小" class="headerlink" title="10.4 调整窗口大小"></a>10.4 调整窗口大小</h2><ul>
<li>ctrl+w + –当前窗口增高一行。也可以用n增高n行。</li>
<li>ctrl+w - –当前窗口减小一行。也可以用n减小n行。</li>
<li>ctrl+w _ –当前窗口扩展到尽可能的大。也可以用n设定行数。</li>
<li>:resize n – 当前窗口n行高。</li>
<li>ctrl+w = – 所有窗口同样高度。</li>
<li>n ctrl+w _ – 当前窗口的高度设定为n行。</li>
<li>ctrl+w < –当前窗口减少一列。也可以用n减少n列。</li>
<li>ctrl+w > –当前窗口增宽一列。也可以用n增宽n列。</li>
<li>ctrl+w | –当前窗口尽可能的宽。也可以用n设定列数。</li>
</ul>
<h2 id="10-5-切换和移动窗口"><a href="#10-5-切换和移动窗口" class="headerlink" title="10.5 切换和移动窗口"></a>10.5 切换和移动窗口</h2><p>如果支持鼠标,切换和调整子窗口的大小就简单了。</p>
<ul>
<li>ctrl+w ctrl+w: 切换到下一个窗口。或者是ctrl+w w。</li>
<li>ctrl+w p: 切换到前一个窗口。</li>
<li>ctrl+w h(l,j,k):切换到左(右,下,上)的窗口。</li>
<li>ctrl+w t(b):切换到最上(下)面的窗口。<BR></li>
<li>ctrl+w H(L,K,J): 将当前窗口移动到最左(右、上、下)面。</li>
<li>ctrl+w r:旋转窗口的位置。</li>
<li>ctrl+w T: 将当前的窗口移动到新的标签页上。</li>
</ul>
<h1 id="11-快速编辑"><a href="#11-快速编辑" class="headerlink" title="11. 快速编辑"></a>11. 快速编辑</h1><h2 id="11-1-改变大小写"><a href="#11-1-改变大小写" class="headerlink" title="11.1 改变大小写"></a>11.1 改变大小写</h2><ul>
<li>~: 反转光标所在字符的大小写。</li>
<li>可视模式下的U或u:把选中的文本变为大写或小写。</li>
<li>gu(U)接范围(如$,或G),可以把从光标当前位置到指定位置之间字母全部 转换成小写或大写。如ggguG,就是把开头到最后一行之间的字母全部变为小 写。再如gu5j,把当前行和下面四行全部变成小写。</li>
</ul>
<h2 id="11-2-替换(normal模式)"><a href="#11-2-替换(normal模式)" class="headerlink" title="11.2 替换(normal模式)"></a>11.2 替换(normal模式)</h2><ul>
<li>r: 替换光标处的字符,同样支持汉字。</li>
<li>R: 进入替换模式,按esc回到正常模式。</li>
</ul>
<h2 id="11-3-撤消与重做(normal模式)"><a href="#11-3-撤消与重做(normal模式)" class="headerlink" title="11.3 撤消与重做(normal模式)"></a>11.3 撤消与重做(normal模式)</h2><ul>
<li>[n] u: 取消一(n)个改动。</li>
<li>:undo 5 – 撤销5个改变。</li>
<li>:undolist – 你的撤销历史。</li>
<li>ctrl + r: 重做最后的改动。</li>
<li>U: 取消当前行中所有的改动。</li>
<li>:earlier 4m – 回到4分钟前</li>
<li>:later 55s – 前进55秒</li>
</ul>
<h2 id="11-4-宏"><a href="#11-4-宏" class="headerlink" title="11.4 宏"></a>11.4 宏</h2><ul>
<li>. –重复上一个编辑动作</li>
<li>qa:开始录制宏a(键盘操作记录)</li>
<li>q:停止录制</li>
<li>@a:播放宏a</li>
</ul>
<h1 id="12-编辑特殊文件"><a href="#12-编辑特殊文件" class="headerlink" title="12. 编辑特殊文件"></a>12. 编辑特殊文件</h1><h2 id="12-1-文件加解密"><a href="#12-1-文件加解密" class="headerlink" title="12.1 文件加解密"></a>12.1 文件加解密</h2><ul>
<li>vim -x file: 开始编辑一个加密的文件。</li>
<li>:X – 为当前文件设置密码。</li>
<li>:set key= – 去除文件的密码。</li>
</ul>
<p><a href="http://www.cnblogs.com/jiqingwu/admin/vim-quick-edit.html" target="_blank" rel="noopener">这里是</a> 滇狐总结的比较高级的vi技巧。</p>
<h2 id="12-2-文件的编码"><a href="#12-2-文件的编码" class="headerlink" title="12.2 文件的编码"></a>12.2 文件的编码</h2><ul>
<li>:e ++enc=utf8 filename, 让vim用utf-8的编码打开这个文件。</li>
<li>:w ++enc=gbk,不管当前文件什么编码,把它转存成gbk编码。</li>
<li>:set fenc或:set fileencoding,查看当前文件的编码。</li>
<li>在vimrc中添加set fileencoding=ucs-bom,utf-8,cp936,vim会根据要打开的文件选择合适的编码。 注意:编码之间不要留空格。 cp936对应于gbk编码。 ucs-bom对应于windows下的文件格式。</li>
</ul>
<p>让vim 正确处理文件格式和文件编码,有赖于 <a href="http://www.cnblogs.com/jiqingwu/admin/vimrc.html" target="_blank" rel="noopener">~/.vimrc的正确配置</a></p>
<h2 id="12-3-文件格式"><a href="#12-3-文件格式" class="headerlink" title="12.3 文件格式"></a>12.3 文件格式</h2><p>大致有三种文件格式:unix, dos, mac. 三种格式的区别主要在于回车键的编码:dos 下是回车加换行,unix 下只有 换行符,mac 下只有回车符。</p>
<ul>
<li>:e ++ff=dos filename, 让vim用dos格式打开这个文件。</li>
<li>:w ++ff=mac filename, 以mac格式存储这个文件。</li>
<li>:set ff,显示当前文件的格式。</li>
<li>在vimrc中添加set fileformats=unix,dos,mac,让vim自动识别文件格式。</li>
</ul>
<h1 id="13-编程辅助"><a href="#13-编程辅助" class="headerlink" title="13. 编程辅助"></a>13. 编程辅助</h1><h2 id="13-1-一些按键"><a href="#13-1-一些按键" class="headerlink" title="13.1 一些按键"></a>13.1 一些按键</h2><ul>
<li>gd: 跳转到局部变量的定义处;</li>
<li>gD: 跳转到全局变量的定义处,从当前文件开头开始搜索;</li>
<li>g;: 上一个修改过的地方;</li>
<li>g,: 下一个修改过的地方;</li>
<li>[[: 跳转到上一个函数块开始,需要有单独一行的{。</li>
<li>]]: 跳转到下一个函数块开始,需要有单独一行的{。</li>
<li>[]: 跳转到上一个函数块结束,需要有单独一行的}。</li>
<li>][: 跳转到下一个函数块结束,需要有单独一行的}。</li>
<li>[{: 跳转到当前块开始处;</li>
<li>]}: 跳转到当前块结束处;</li>
<li>[/: 跳转到当前注释块开始处;</li>
<li>]/: 跳转到当前注释块结束处;</li>
<li>%: 不仅能移动到匹配的(),{}或[]上,而且能在#if,#else, #endif之间跳跃。</li>
</ul>
<p>下面的括号匹配对编程很实用的。</p>
<ul>
<li>ci’, di’, yi’:修改、剪切或复制’之间的内容。</li>
<li>ca’, da’, ya’:修改、剪切或复制’之间的内容,包含’。</li>
<li>ci”, di”, yi”:修改、剪切或复制”之间的内容。</li>
<li>ca”, da”, ya”:修改、剪切或复制”之间的内容,包含”。</li>
<li>ci(, di(, yi(:修改、剪切或复制()之间的内容。</li>
<li>ca(, da(, ya(:修改、剪切或复制()之间的内容,包含()。</li>
<li>ci[, di[, yi[:修改、剪切或复制[]之间的内容。</li>
<li>ca[, da[, ya[:修改、剪切或复制[]之间的内容,包含[]。</li>
<li>ci{, di{, yi{:修改、剪切或复制{}之间的内容。</li>
<li>ca{, da{, ya{:修改、剪切或复制{}之间的内容,包含{}。</li>
<li>ci<, di<, yi<:修改、剪切或复制<>之间的内容。</li>
<li>ca<, da<, ya<:修改、剪切或复制<>之间的内容,包含<>。</li>
</ul>
<h2 id="13-2-ctags"><a href="#13-2-ctags" class="headerlink" title="13.2 ctags"></a>13.2 ctags</h2><ul>
<li>ctags -R: 生成tag文件,-R表示也为子目录中的文件生成tags</li>
<li>:set tags=path/tags – 告诉ctags使用哪个tag文件</li>
<li>:tag xyz – 跳到xyz的定义处,或者将光标放在xyz上按C-],返回用C-t</li>
<li>:stag xyz – 用分割的窗口显示xyz的定义,或者C-w ], 如果用C-w n ],就会打开一个n行高的窗口</li>
<li>:ptag xyz – 在预览窗口中打开xyz的定义,热键是C-w }。</li>
<li>:pclose – 关闭预览窗口。热键是C-w z。</li>
<li>:pedit abc.h – 在预览窗口中编辑abc.h</li>
<li>:psearch abc – 搜索当前文件和当前文件include的文件,显示包含abc的行。</li>
</ul>
<p>有时一个tag可能有多个匹配,如函数重载,一个函数名就会有多个匹配。 这种情况会先跳转到第一个匹配处。</p>
<ul>
<li>:[n]tnext – 下一[n]个匹配。</li>
<li>:[n]tprev – 上一[n]个匹配。</li>
<li>:tfirst – 第一个匹配</li>
<li>:tlast – 最后一个匹配</li>
<li>:tselect tagname – 打开选择列表</li>
</ul>
<p>tab键补齐</p>
<ul>
<li>:tag xyz<tab> – 补齐以xyz开头的tag名,继续按tab键,会显示其他的。</li>
<li>:tag /xyz<tab> – 会用名字中含有xyz的tag名补全。</li>
</ul>
<h2 id="13-3-cscope"><a href="#13-3-cscope" class="headerlink" title="13.3 cscope"></a>13.3 cscope</h2><ul>
<li>cscope -Rbq: 生成cscope.out文件</li>
<li>:cs add /path/to/cscope.out /your/work/dir</li>
<li>:cs find c func – 查找func在哪些地方被调用</li>
<li>:cw – 打开quickfix窗口查看结果</li>
</ul>
<h2 id="13-4-gtags"><a href="#13-4-gtags" class="headerlink" title="13.4 gtags"></a>13.4 gtags</h2><p>Gtags综合了ctags和cscope的功能。 使用Gtags之前,你需要安装GNU Gtags。 然后在工程目录运行 gtags 。</p>
<ul>
<li>:Gtags funcname 定位到 funcname 的定义处。</li>
<li>:Gtags -r funcname 查询 funcname被引用的地方。</li>
<li>:Gtags -s symbol 定位 symbol 出现的地方。</li>
<li>:Gtags -g string Goto string 出现的地方。 :Gtags -gi string 忽略大小写。</li>
<li>:Gtags -f filename 显示 filename 中的函数列表。 你可以用 :Gtags -f % 显示当前文件。</li>
<li>:Gtags -P pattern 显示路径中包含特定模式的文件。 如 :Gtags -P .h$ 显示所有头文件, :Gtags -P /vm/ 显示vm目录下的文件。</li>
</ul>
<h2 id="13-5-编译"><a href="#13-5-编译" class="headerlink" title="13.5 编译"></a>13.5 编译</h2><p>vim提供了:make来编译程序,默认调用的是make, 如果你当前目录下有makefile,简单地:make即可。</p>
<p>如果你没有make程序,你可以通过配置makeprg选项来更改make调用的程序。 如果你只有一个abc.<a href="http://lib.csdn.net/base/java" target="_blank" rel="noopener">Java</a>文件,你可以这样设置:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">set makeprg=javac\ abc.java</span><br></pre></td></tr></table></figure>
<p>然后:make即可。如果程序有错,可以通过quickfix窗口查看错误。 不过如果要正确定位错误,需要设置好errorformat,让vim识别错误信息。 如:</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">:setl efm=%A%f:%l:\ %m,%-Z%p^,%-C%.%#</span><br></pre></td></tr></table></figure>
<p>%f表示文件名,%l表示行号, %m表示错误信息,其它的还不能理解。 请参考 :help errorformat。</p>
<h2 id="13-6-快速修改窗口"><a href="#13-6-快速修改窗口" class="headerlink" title="13.6 快速修改窗口"></a>13.6 快速修改窗口</h2><p>其实是quickfix插件提供的功能, 对编译调试程序非常有用 :)</p>
<ul>
<li>:copen – 打开快速修改窗口。</li>
<li>:cclose – 关闭快速修改窗口。</li>
</ul>
<p>快速修改窗口在make程序时非常有用,当make之后:</p>
<ul>
<li>:cl – 在快速修改窗口中列出错误。</li>
<li>:cn – 定位到下一个错误。</li>
<li>:cp – 定位到上一个错误。</li>
<li>:cr – 定位到第一个错误。</li>
</ul>
<h2 id="13-7-自动补全"><a href="#13-7-自动补全" class="headerlink" title="13.7 自动补全"></a>13.7 自动补全</h2><ul>
<li>C-x C-s – 拼写建议。</li>
<li>C-x C-v – 补全vim选项和命令。</li>
<li>C-x C-l – 整行补全。</li>
<li>C-x C-f – 自动补全文件路径。弹出菜单后,按C-f循环选择,当然也可以按 C-n和C-p。</li>
<li>C-x C-p 和C-x C-n – 用文档中出现过的单词补全当前的词。 直接按C-p和C-n也可以。</li>
<li>C-x C-o – 编程时可以补全关键字和函数名啊。</li>
<li>C-x C-i – 根据头文件内关键字补全。</li>
<li>C-x C-d – 补全宏定义。</li>
<li>C-x C-n – 按缓冲区中出现过的关键字补全。 直接按C-n或C-p即可。</li>
</ul>
<p>当弹出补全菜单后:</p>
<ul>
<li>C-p 向前切换成员;</li>
<li>C-n 向后切换成员;</li>
<li>C-e 退出下拉菜单,并退回到原来录入的文字;</li>
<li>C-y 退出下拉菜单,并接受当前选项。</li>
</ul>
<h2 id="13-8-多行缩进缩出"><a href="#13-8-多行缩进缩出" class="headerlink" title="13.8 多行缩进缩出"></a>13.8 多行缩进缩出</h2><ul>
<li>正常模式下,按两下>;光标所在行会缩进。</li>
<li>如果先按了n,再按两下>;,光标以下的n行会缩进。</li>
<li>对应的,按两下<;,光标所在行会缩出。</li>
<li>如果在编辑代码文件,可以用=进行调整。</li>
<li>在可视模式下,选择要调整的代码块,按=,代码会按书写规则缩排好。</li>
<li>或者n =,调整n行代码的缩排。</li>
</ul>
<h2 id="13-9-折叠"><a href="#13-9-折叠" class="headerlink" title="13.9 折叠"></a>13.9 折叠</h2><ul>
<li>zf – 创建折叠的命令,可以在一个可视区域上使用该命令;</li>
<li>zd – 删除当前行的折叠;</li>
<li>zD – 删除当前行的折叠;</li>
<li>zfap – 折叠光标所在的段;</li>
<li>zo – 打开折叠的文本;</li>
<li>zc – 收起折叠;</li>
<li>za – 打开/关闭当前折叠;</li>
<li>zr – 打开嵌套的折行;</li>
<li>zm – 收起嵌套的折行;</li>
<li>zR (zO) – 打开所有折行;</li>
<li>zM (zC) – 收起所有折行;</li>
<li>zj – 跳到下一个折叠处;</li>
<li>zk – 跳到上一个折叠处;</li>
<li>zi – enable/disable fold;</li>
</ul>
<h1 id="14-命令行"><a href="#14-命令行" class="headerlink" title="14. 命令行"></a>14. 命令行</h1><p>normal模式下按:进入命令行模式</p>
<h2 id="14-1-命令行模式下的快捷键:"><a href="#14-1-命令行模式下的快捷键:" class="headerlink" title="14.1 命令行模式下的快捷键:"></a>14.1 命令行模式下的快捷键:</h2><ul>
<li>上下方向键:上一条或者下一条命令。如果已经输入了部分命令,则找上一 条或者下一条匹配的命令。</li>
<li>左右方向键:左/右移一个字符。</li>
<li>C-w: 向前删除一个单词。</li>
<li>C-h: 向前删除一个字符,等同于Backspace。</li>
<li>C-u: 从当前位置移动到命令行开头。</li>
<li>C-b: 移动到命令行开头。</li>
<li>C-e: 移动到命令行末尾。</li>
<li>Shift-Left: 左移一个单词。</li>
<li>Shift-Right: 右移一个单词。</li>
<li>@: 重复上一次的冒号命令。</li>
<li>q: 正常模式下,q然后按’:’,打开命令行历史缓冲区, 可以像编辑文件一样编辑命令。</li>
<li>q/和q? 可以打开查找历史记录。</li>
</ul>
<h2 id="14-2-执行外部命令"><a href="#14-2-执行外部命令" class="headerlink" title="14.2 执行外部命令"></a>14.2 执行外部命令</h2><ul>
<li>:! cmd 执行外部命令。</li>
<li>:!! 执行上一次的外部命令。</li>
<li>:sh 调用shell,用exit返回vim。</li>
<li>:r !cmd 将命令的返回结果插入文件当前位置。</li>
<li>:m,nw !cmd 将文件的m行到n行之间的内容做为命令输入执行命令。</li>
</ul>
<h1 id="15-其它"><a href="#15-其它" class="headerlink" title="15. 其它"></a>15. 其它</h1><h2 id="15-1-工作目录"><a href="#15-1-工作目录" class="headerlink" title="15.1 工作目录"></a>15.1 工作目录</h2><ul>
<li>:pwd 显示vim的工作目录。</li>
<li>:cd path 改变vim的工作目录。</li>
<li>:set autochdir 可以让vim 根据编辑的文件自动切换工作目录。</li>
</ul>
<h2 id="15-2-一些快捷键(收集中)"><a href="#15-2-一些快捷键(收集中)" class="headerlink" title="15.2 一些快捷键(收集中)"></a>15.2 一些快捷键(收集中)</h2><ul>
<li>K: 打开光标所在词的manpage。</li>
<li>*: 向下搜索光标所在词。</li>
<li>g*: 同上,但部分符合即可。</li>
<li>#: 向上搜索光标所在词。</li>
<li>g#: 同上,但部分符合即可。</li>
<li>g C-g: 统计全文或统计部分的字数。</li>
</ul>
<h2 id="15-3-在线帮助"><a href="#15-3-在线帮助" class="headerlink" title="15.3 在线帮助"></a>15.3 在线帮助</h2><ul>
<li>:h(elp)或F1 打开总的帮助。</li>
<li>:help user-manual 打开用户手册。</li>
<li>命令帮助的格式为:第一行指明怎么使用那个命令; 然后是缩进的一段解释这个命令的作用,然后是进一步的信息。</li>
<li>:helptags somepath 为somepath中的文档生成索引。</li>
<li>:helpgrep 可以搜索整个帮助文档,匹配的列表显示在quickfix窗口中。</li>
<li>Ctrl+] 跳转到tag主题,Ctrl+t 跳回。</li>
<li>:ver 显示版本信息。</li>
</ul>
<h2 id="15-4-一些小功能"><a href="#15-4-一些小功能" class="headerlink" title="15.4 一些小功能"></a>15.4 一些小功能</h2><ul>
<li>简单计算器: 在插入模式下,输入C-r =,然后输入表达式,就能在 光标处得到计算结果。</li>
</ul>
]]></content>
<categories>
<category>vim</category>
</categories>
<tags>
<tag>vim</tag>
</tags>
</entry>
<entry>
<title>Java泛型详解</title>
<url>/2021/09/16/Java%E6%B3%9B%E5%9E%8B%E8%AF%A6%E8%A7%A3/</url>
<content><![CDATA[<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">对java的泛型特性的了解仅限于表面的浅浅一层,直到在学习设计模式时发现有不了解的用法,才想起详细的记录一下。</span><br><span class="line"></span><br><span class="line">本文参考java 泛型详解、Java中的泛型方法、 java泛型详解</span><br></pre></td></tr></table></figure>
<a id="more"></a>
<h1 id="1-概述"><a href="#1-概述" class="headerlink" title="1. 概述"></a>1. 概述</h1><p>泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。</p>
<p>什么是泛型?为什么要使用泛型?</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。</span><br><span class="line"></span><br><span class="line">泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。</span><br></pre></td></tr></table></figure>
<h1 id="2-一个栗子"><a href="#2-一个栗子" class="headerlink" title="2. 一个栗子"></a>2. 一个栗子</h1><p>一个被举了无数次的例子:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List arrayList = <span class="keyword">new</span> ArrayList();</span><br><span class="line">arrayList.add(<span class="string">"aaaa"</span>);</span><br><span class="line">arrayList.add(<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i< arrayList.size();i++){</span><br><span class="line"> String item = (String)arrayList.get(i);</span><br><span class="line"> Log.d(<span class="string">"泛型测试"</span>,<span class="string">"item = "</span> + item);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>毫无疑问,程序的运行结果会以崩溃结束:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String</span><br></pre></td></tr></table></figure>
<p>ArrayList可以存放任意类型,例子中添加了一个String类型,添加了一个Integer类型,再使用时都以String的方式使用,因此程序崩溃了。为了解决类似这样的问题(在编译阶段就可以解决),泛型应运而生。</p>
<p>我们将第一行声明初始化list的代码更改一下,编译器会在编译阶段就能够帮我们发现类似这样的问题。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List<String> arrayList = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">...</span><br><span class="line"><span class="comment">//arrayList.add(100); 在编译阶段,编译器就会报错</span></span><br></pre></td></tr></table></figure>
<h1 id="3-特性"><a href="#3-特性" class="headerlink" title="3. 特性"></a>3. 特性</h1><p>泛型只在编译阶段有效。看下面的代码:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List<String> stringArrayList = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">List<Integer> integerArrayList = <span class="keyword">new</span> ArrayList<Integer>();</span><br><span class="line"></span><br><span class="line">Class classStringArrayList = stringArrayList.getClass();</span><br><span class="line">Class classIntegerArrayList = integerArrayList.getClass();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(classStringArrayList.equals(classIntegerArrayList)){</span><br><span class="line"> Log.d(<span class="string">"泛型测试"</span>,<span class="string">"类型相同"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>输出结果:<code>D/泛型测试: 类型相同</code>。</p>
<p>通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。</p>
<p>对此总结成一句话:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。</p>
<h1 id="4-泛型的使用"><a href="#4-泛型的使用" class="headerlink" title="4. 泛型的使用"></a>4. 泛型的使用</h1><p>泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法</p>
<h2 id="4-1-泛型类"><a href="#4-1-泛型类" class="headerlink" title="4.1 泛型类"></a>4.1 泛型类</h2><p>泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。</p>
<p>泛型类的最基本写法(这么看可能会有点晕,会在下面的例子中详解):</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型></span>{</span><br><span class="line"> <span class="keyword">private</span> 泛型标识 <span class="comment">/*(成员变量类型)*/</span> <span class="keyword">var</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>
<p>一个最普通的泛型类:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型</span></span><br><span class="line"><span class="comment">//在实例化泛型类时,必须指定T的具体类型</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Generic</span><<span class="title">T</span>></span>{ </span><br><span class="line"> <span class="comment">//key这个成员变量的类型为T,T的类型由外部指定 </span></span><br><span class="line"> <span class="keyword">private</span> T key;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Generic</span><span class="params">(T key)</span> </span>{ <span class="comment">//泛型构造方法形参key的类型也为T,T的类型由外部指定</span></span><br><span class="line"> <span class="keyword">this</span>.key = key;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> T <span class="title">getKey</span><span class="params">()</span></span>{ <span class="comment">//泛型方法getKey的返回值类型为T,T的类型由外部指定</span></span><br><span class="line"> <span class="keyword">return</span> key;</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="code"><pre><span class="line"><span class="comment">//泛型的类型参数只能是类类型(包括自定义类),不能是简单类型</span></span><br><span class="line"><span class="comment">//传入的实参类型需与泛型的类型参数类型相同,即为Integer.</span></span><br><span class="line">Generic<Integer> genericInteger = <span class="keyword">new</span> Generic<Integer>(<span class="number">123456</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//传入的实参类型需与泛型的类型参数类型相同,即为String.</span></span><br><span class="line">Generic<String> genericString = <span class="keyword">new</span> Generic<String>(<span class="string">"key_vlaue"</span>);</span><br><span class="line">Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key is "</span> + genericInteger.getKey());</span><br><span class="line">Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key is "</span> + genericString.getKey());</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="number">12</span>-<span class="number">27</span> <span class="number">09</span>:<span class="number">20</span>:<span class="number">04.432</span> <span class="number">13063</span>-<span class="number">13063</span>/? D/泛型测试: key is <span class="number">123456</span></span><br><span class="line"><span class="number">12</span>-<span class="number">27</span> <span class="number">09</span>:<span class="number">20</span>:<span class="number">04.432</span> <span class="number">13063</span>-<span class="number">13063</span>/? D/泛型测试: key is key_vlaue</span><br></pre></td></tr></table></figure>
<p>定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。</p>
<p>看一个例子:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Generic generic = <span class="keyword">new</span> Generic(<span class="string">"111111"</span>);</span><br><span class="line">Generic generic1 = <span class="keyword">new</span> Generic(<span class="number">4444</span>);</span><br><span class="line">Generic generic2 = <span class="keyword">new</span> Generic(<span class="number">55.55</span>);</span><br><span class="line">Generic generic3 = <span class="keyword">new</span> Generic(<span class="keyword">false</span>);</span><br><span class="line"></span><br><span class="line">Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key is "</span> + generic.getKey());</span><br><span class="line">Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key is "</span> + generic1.getKey());</span><br><span class="line">Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key is "</span> + generic2.getKey());</span><br><span class="line">Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key is "</span> + generic3.getKey());</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">D/泛型测试: key is <span class="number">111111</span></span><br><span class="line">D/泛型测试: key is <span class="number">4444</span></span><br><span class="line">D/泛型测试: key is <span class="number">55.55</span></span><br><span class="line">D/泛型测试: key is <span class="keyword">false</span></span><br></pre></td></tr></table></figure>
<p><strong>注意:</strong></p>
<ul>
<li>泛型的类型参数只能是类类型,不能是简单类型。</li>
<li>不能对确切的泛型类型使用instanceof操作。如下面的操作是非法的,编译时会出错。</li>
</ul>
<p> if(ex_num instanceof Generic<Number>){ }</p>
<h2 id="4-2-泛型接口"><a href="#4-2-泛型接口" class="headerlink" title="4.2 泛型接口"></a>4.2 泛型接口</h2><p>泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//定义一个泛型接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Generator</span><<span class="title">T</span>> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> T <span class="title">next</span><span class="params">()</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>当实现泛型接口的类,未传入泛型实参时:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中</span></span><br><span class="line"><span class="comment"> * 即:class FruitGenerator<T> implements Generator<T>{</span></span><br><span class="line"><span class="comment"> * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FruitGenerator</span><<span class="title">T</span>> <span class="keyword">implements</span> <span class="title">Generator</span><<span class="title">T</span>></span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> T <span class="title">next</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</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="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 传入泛型实参时:</span></span><br><span class="line"><span class="comment"> * 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T></span></span><br><span class="line"><span class="comment"> * 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。</span></span><br><span class="line"><span class="comment"> * 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型</span></span><br><span class="line"><span class="comment"> * 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FruitGenerator</span> <span class="keyword">implements</span> <span class="title">Generator</span><<span class="title">String</span>> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> String[] fruits = <span class="keyword">new</span> String[]{<span class="string">"Apple"</span>, <span class="string">"Banana"</span>, <span class="string">"Pear"</span>};</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">next</span><span class="params">()</span> </span>{</span><br><span class="line"> Random rand = <span class="keyword">new</span> Random();</span><br><span class="line"> <span class="keyword">return</span> fruits[rand.nextInt(<span class="number">3</span>)];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="4-3-泛型通配符"><a href="#4-3-泛型通配符" class="headerlink" title="4.3 泛型通配符"></a>4.3 泛型通配符</h2><p>我们知道<code>Ingeter</code>是<code>Number</code>的一个子类,同时在特性章节中我们也验证过<code>Generic<Ingeter></code>与<code>Generic<Number></code>实际上是相同的一种基本类型。那么问题来了,在使用<code>Generic<Number></code>作为形参的方法中,能否使用<code>Generic<Ingeter></code>的实例传入呢?在逻辑上类似于<code>Generic<Number></code>和<code>Generic<Ingeter></code>是否可以看成具有父子关系的泛型类型呢?</p>
<p>为了弄清楚这个问题,我们使用<code>Generic<T></code>这个泛型类继续看下面的例子:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showKeyValue1</span><span class="params">(Generic<Number> obj)</span></span>{</span><br><span class="line"> Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key value is "</span> + obj.getKey());</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Generic<Integer> gInteger = <span class="keyword">new</span> Generic<Integer>(<span class="number">123</span>);</span><br><span class="line">Generic<Number> gNumber = <span class="keyword">new</span> Generic<Number>(<span class="number">456</span>);</span><br><span class="line"></span><br><span class="line">showKeyValue(gNumber);</span><br><span class="line"></span><br><span class="line"><span class="comment">// showKeyValue这个方法编译器会为我们报错:Generic<java.lang.Integer> </span></span><br><span class="line"><span class="comment">// cannot be applied to Generic<java.lang.Number></span></span><br><span class="line"><span class="comment">// showKeyValue(gInteger);</span></span><br></pre></td></tr></table></figure>
<p>通过提示信息我们可以看到<code>Generic<Integer></code>不能被看作为``Generic<Number>`的子类。由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。</p>
<p>回到上面的例子,如何解决上面的问题?总不能为了定义一个新的方法来处理<code>Generic<Integer></code>类型的类,这显然与java中的多台理念相违背。因此我们需要一个在逻辑上可以表示同时是<code>Generic<Integer></code>和<code>Generic<Number></code>父类的引用类型。由此类型通配符应运而生。</p>
<p>我们可以将上面的方法改一下:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showKeyValue1</span><span class="params">(Generic<?> obj)</span></span>{</span><br><span class="line"> Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key value is "</span> + obj.getKey());</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。</p>
<p>可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。</p>
<h2 id="4-4-泛型方法"><a href="#4-4-泛型方法" class="headerlink" title="4.4 泛型方法"></a>4.4 泛型方法</h2><p>在java中,泛型类的定义非常简单,但是泛型方法就比较复杂了。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">尤其是我们见到的大多数泛型类中的成员方法也都使用了泛型,有的甚至泛型类中也包含着泛型方法,这样在初学者中非常容易将泛型方法理解错了。</span><br></pre></td></tr></table></figure>
<p>泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><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 class="doctag">@param</span> tClass 传入的泛型实参</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> T 返回值为T类型</span></span><br><span class="line"><span class="comment"> * 说明:</span></span><br><span class="line"><span class="comment"> * 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。</span></span><br><span class="line"><span class="comment"> * 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。</span></span><br><span class="line"><span class="comment"> * 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。</span></span><br><span class="line"><span class="comment"> * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <T> <span class="function">T <span class="title">genericMethod</span><span class="params">(Class<T> tClass)</span><span class="keyword">throws</span> InstantiationException ,</span></span><br><span class="line"><span class="function"> IllegalAccessException</span>{</span><br><span class="line"> T instance = tClass.newInstance();</span><br><span class="line"> <span class="keyword">return</span> instance;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Object obj = genericMethod(Class.forName(<span class="string">"com.test.test"</span>));</span><br></pre></td></tr></table></figure>
<h3 id="4-4-1-泛型方法的基本用法"><a href="#4-4-1-泛型方法的基本用法" class="headerlink" title="4.4.1 泛型方法的基本用法"></a>4.4.1 泛型方法的基本用法</h3><p>光看上面的例子有的同学可能依然会非常迷糊,我们再通过一个例子,把我泛型方法再总结一下。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GenericTest</span> </span>{</span><br><span class="line"> <span class="comment">//这个类是个泛型类,在上面已经介绍过</span></span><br><span class="line"> <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Generic</span><<span class="title">T</span>></span>{ </span><br><span class="line"> <span class="keyword">private</span> T key;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Generic</span><span class="params">(T key)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.key = key;</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></span><br><span class="line"> <span class="comment">//所以在这个方法中才可以继续使用 T 这个泛型。</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> T <span class="title">getKey</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> key;</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"> * 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"</span></span><br><span class="line"><span class="comment"> * 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。</span></span><br><span class="line"><span class="comment"> public E setKey(E key){</span></span><br><span class="line"><span class="comment"> this.key = keu</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><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"> * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T</span></span><br><span class="line"><span class="comment"> * 这个T可以出现在这个泛型方法的任意位置.</span></span><br><span class="line"><span class="comment"> * 泛型的数量也可以为任意多个 </span></span><br><span class="line"><span class="comment"> * 如:public <T,K> K showKeyName(Generic<T> container){</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="keyword">public</span> <T> <span class="function">T <span class="title">showKeyName</span><span class="params">(Generic<T> container)</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"container key :"</span> + container.getKey());</span><br><span class="line"> <span class="comment">//当然这个例子举的不太合适,只是为了说明泛型方法的特性。</span></span><br><span class="line"> T test = container.getKey();</span><br><span class="line"> <span class="keyword">return</span> test;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showKeyValue1</span><span class="params">(Generic<Number> obj)</span></span>{</span><br><span class="line"> Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key value is "</span> + obj.getKey());</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">//同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showKeyValue2</span><span class="params">(Generic<?> obj)</span></span>{</span><br><span class="line"> Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key value is "</span> + obj.getKey());</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"> * 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "</span></span><br><span class="line"><span class="comment"> * 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。</span></span><br><span class="line"><span class="comment"> * 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。</span></span><br><span class="line"><span class="comment"> public <T> T showKeyName(Generic<E> container){</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><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "</span></span><br><span class="line"><span class="comment"> * 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。</span></span><br><span class="line"><span class="comment"> * 所以这也不是一个正确的泛型方法声明。</span></span><br><span class="line"><span class="comment"> public void showkey(T genericObj){</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><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="4-4-2-类中的泛型方法"><a href="#4-4-2-类中的泛型方法" class="headerlink" title="4.4.2 类中的泛型方法"></a>4.4.2 类中的泛型方法</h3><p>当然这并不是泛型方法的全部,泛型方法可以出现杂任何地方和任何场景中使用。但是有一种情况是非常特殊的,当泛型方法出现在泛型类中时,我们再通过一个例子看一下</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GenericFruit</span> </span>{</span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Fruit</span></span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"fruit"</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Apple</span> <span class="keyword">extends</span> <span class="title">Fruit</span></span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"apple"</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Person</span></span>{</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"Person"</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">GenerateTest</span><<span class="title">T</span>></span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">show_1</span><span class="params">(T t)</span></span>{</span><br><span class="line"> System.out.println(t.toString());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。</span></span><br><span class="line"> <span class="comment">//由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。</span></span><br><span class="line"> <span class="keyword">public</span> <E> <span class="function"><span class="keyword">void</span> <span class="title">show_3</span><span class="params">(E t)</span></span>{</span><br><span class="line"> System.out.println(t.toString());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。</span></span><br><span class="line"> <span class="keyword">public</span> <T> <span class="function"><span class="keyword">void</span> <span class="title">show_2</span><span class="params">(T t)</span></span>{</span><br><span class="line"> System.out.println(t.toString());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> Apple apple = <span class="keyword">new</span> Apple();</span><br><span class="line"> Person person = <span class="keyword">new</span> Person();</span><br><span class="line"></span><br><span class="line"> GenerateTest<Fruit> generateTest = <span class="keyword">new</span> GenerateTest<Fruit>();</span><br><span class="line"> <span class="comment">//apple是Fruit的子类,所以这里可以</span></span><br><span class="line"> generateTest.show_1(apple);</span><br><span class="line"> <span class="comment">//编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person</span></span><br><span class="line"> <span class="comment">//generateTest.show_1(person);</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">//使用这两个方法都可以成功</span></span><br><span class="line"> generateTest.show_2(apple);</span><br><span class="line"> generateTest.show_2(person);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//使用这两个方法也都可以成功</span></span><br><span class="line"> generateTest.show_3(apple);</span><br><span class="line"> generateTest.show_3(person);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="4-4-3-泛型方法与可变参数"><a href="#4-4-3-泛型方法与可变参数" class="headerlink" title="4.4.3 泛型方法与可变参数"></a>4.4.3 泛型方法与可变参数</h3><p>再看一个泛型方法和可变参数的例子:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <T> <span class="function"><span class="keyword">void</span> <span class="title">printMsg</span><span class="params">( T... args)</span></span>{</span><br><span class="line"> <span class="keyword">for</span>(T t : args){</span><br><span class="line"> Log.d(<span class="string">"泛型测试"</span>,<span class="string">"t is "</span> + t);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">printMsg(<span class="string">"111"</span>,<span class="number">222</span>,<span class="string">"aaaa"</span>,<span class="string">"2323.4"</span>,<span class="number">55.55</span>);</span><br></pre></td></tr></table></figure>
<h3 id="4-4-4-静态方法与泛型"><a href="#4-4-4-静态方法与泛型" class="headerlink" title="4.4.4 静态方法与泛型"></a>4.4.4 静态方法与泛型</h3><p>静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。</p>
<p>即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StaticGenerator</span><<span class="title">T</span>> </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></span><br><span class="line"><span class="comment"> * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。</span></span><br><span class="line"><span class="comment"> * 如:public static void show(T t){..},此时编译器会提示错误信息:</span></span><br><span class="line"><span class="comment"> "StaticGenerator cannot be refrenced from static context"</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> <T> <span class="function"><span class="keyword">void</span> <span class="title">show</span><span class="params">(T t)</span></span>{</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="4-4-5-泛型方法总结"><a href="#4-4-5-泛型方法总结" class="headerlink" title="4.4.5 泛型方法总结"></a>4.4.5 泛型方法总结</h3><p>泛型方法能使方法独立于类而产生变化,以下是一个基本的指导原则:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">无论何时,如果你能做到,你就该尽量使用泛型方法。也就是说,如果使用泛型方法将整个类泛型化,</span><br><span class="line"></span><br><span class="line">那么就应该使用泛型方法。另外对于一个<span class="keyword">static</span>的方法而已,无法访问泛型类型的参数。</span><br><span class="line"></span><br><span class="line">所以如果<span class="keyword">static</span>方法要使用泛型能力,就必须使其成为泛型方法。</span><br></pre></td></tr></table></figure>
<h2 id="4-5-泛型上下边界"><a href="#4-5-泛型上下边界" class="headerlink" title="4.5 泛型上下边界"></a>4.5 泛型上下边界</h2><p>在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。</p>
<p>为泛型添加上边界,即传入的类型实参必须是指定类型的子类型</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">showKeyValue1</span><span class="params">(Generic<? extends Number> obj)</span></span>{</span><br><span class="line"> Log.d(<span class="string">"泛型测试"</span>,<span class="string">"key value is "</span> + obj.getKey());</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Generic<String> generic1 = <span class="keyword">new</span> Generic<String>(<span class="string">"11111"</span>);</span><br><span class="line">Generic<Integer> generic2 = <span class="keyword">new</span> Generic<Integer>(<span class="number">2222</span>);</span><br><span class="line">Generic<Float> generic3 = <span class="keyword">new</span> Generic<Float>(<span class="number">2.4f</span>);</span><br><span class="line">Generic<Double> generic4 = <span class="keyword">new</span> Generic<Double>(<span class="number">2.56</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//这一行代码编译器会提示错误,因为String类型并不是Number类型的子类</span></span><br><span class="line"><span class="comment">//showKeyValue1(generic1);</span></span><br><span class="line"></span><br><span class="line">showKeyValue1(generic2);</span><br><span class="line">showKeyValue1(generic3);</span><br><span class="line">showKeyValue1(generic4);</span><br></pre></td></tr></table></figure>
<p>如果我们把泛型类的定义也改一下:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Generic</span><<span class="title">T</span> <span class="keyword">extends</span> <span class="title">Number</span>></span>{</span><br><span class="line"> <span class="keyword">private</span> T key;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Generic</span><span class="params">(T key)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.key = key;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> T <span class="title">getKey</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">return</span> key;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//这一行代码也会报错,因为String不是Number的子类</span></span><br><span class="line">Generic<String> generic1 = <span class="keyword">new</span> Generic<String>(<span class="string">"11111"</span>);</span><br></pre></td></tr></table></figure>
<p>再来一个泛型方法的例子:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的<T>上添加上下边界,即在泛型声明的时候添加</span></span><br><span class="line"><span class="comment">//public <T> T showKeyName(Generic<T extends Number> container),编译器会报错:"Unexpected bound"</span></span><br><span class="line"><span class="keyword">public</span> <T extends Number> <span class="function">T <span class="title">showKeyName</span><span class="params">(Generic<T> container)</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"container key :"</span> + container.getKey());</span><br><span class="line"> T test = container.getKey();</span><br><span class="line"> <span class="keyword">return</span> test;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>通过上面的两个例子可以看出:泛型的上下边界添加,必须与泛型的声明在一起 。</p>
<h2 id="4-6-关于泛型数组要提一下"><a href="#4-6-关于泛型数组要提一下" class="headerlink" title="4.6 关于泛型数组要提一下"></a>4.6 关于泛型数组要提一下</h2><p>看到了很多文章中都会提起泛型数组,经过查看sun的说明文档,在java中是”不能创建一个确切的泛型类型的数组”的。</p>
<p>也就是说下面的这个例子是不可以的:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List<String>[] ls = <span class="keyword">new</span> ArrayList<String>[<span class="number">10</span>];</span><br></pre></td></tr></table></figure>
<p>而使用通配符创建泛型数组是可以的,如下面这个例子:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List<?>[] ls = <span class="keyword">new</span> ArrayList<?>[<span class="number">10</span>];</span><br></pre></td></tr></table></figure>
<p>这样也是可以的:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List<String>[] ls = <span class="keyword">new</span> ArrayList[<span class="number">10</span>];</span><br></pre></td></tr></table></figure>
<p>下面使用<a href="http://docs.oracle.com/javase/tutorial/extra/generics/fineprint.html" target="_blank" rel="noopener">Sun</a><a href="http://docs.oracle.com/javase/tutorial/extra/generics/fineprint.html" target="_blank" rel="noopener">的一篇文档</a>的一个例子来说明这个问题:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List<String>[] lsa = <span class="keyword">new</span> List<String>[<span class="number">10</span>]; <span class="comment">// Not really allowed. </span></span><br><span class="line">Object o = lsa; </span><br><span class="line">Object[] oa = (Object[]) o; </span><br><span class="line">List<Integer> li = <span class="keyword">new</span> ArrayList<Integer>(); </span><br><span class="line">li.add(<span class="keyword">new</span> Integer(<span class="number">3</span>)); </span><br><span class="line">oa[<span class="number">1</span>] = li; <span class="comment">// Unsound, but passes run time store check </span></span><br><span class="line">String s = lsa[<span class="number">1</span>].get(<span class="number">0</span>); <span class="comment">// Run-time error: ClassCastException.</span></span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[<span class="number">1</span>]赋上一个ArrayList而不会出现异常,但是在取出数据的时候却要做一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。</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="code"><pre><span class="line">List<?>[] lsa = <span class="keyword">new</span> List<?>[<span class="number">10</span>]; <span class="comment">// OK, array of unbounded wildcard type. </span></span><br><span class="line">Object o = lsa; </span><br><span class="line">Object[] oa = (Object[]) o; </span><br><span class="line">List<Integer> li = <span class="keyword">new</span> ArrayList<Integer>(); </span><br><span class="line">li.add(<span class="keyword">new</span> Integer(<span class="number">3</span>)); </span><br><span class="line">oa[<span class="number">1</span>] = li; <span class="comment">// Correct. </span></span><br><span class="line">Integer i = (Integer) lsa[<span class="number">1</span>].get(<span class="number">0</span>); <span class="comment">// OK</span></span><br></pre></td></tr></table></figure>
<h1 id="5-最后"><a href="#5-最后" class="headerlink" title="5. 最后"></a>5. 最后</h1><p>本文中的例子主要是为了阐述泛型中的一些思想而简单举出的,并不一定有着实际的可用性。另外,一提到泛型,相信大家用到最多的就是在集合中,其实,在实际的编程过程中,自己可以使用泛型去简化开发,且能很好的保证代码质量。</p>
]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title>RabbitMQ入门</title>
<url>/2021/08/25/%E7%AE%97%E6%B3%95/</url>
<content><![CDATA[<h1 id="RabbitMQ入门"><a href="#RabbitMQ入门" class="headerlink" title="RabbitMQ入门"></a>RabbitMQ入门</h1><h2 id="新建一个maven工程,添加amqp-client依赖"><a href="#新建一个maven工程,添加amqp-client依赖" class="headerlink" title="新建一个maven工程,添加amqp-client依赖"></a>新建一个maven工程,添加amqp-client依赖</h2><figure class="highlight xml"><table><tr><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>com.rabbitmq<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>amqp-client<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>5.7.1<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>
<h2 id="连接工具类:"><a href="#连接工具类:" class="headerlink" title="连接工具类:"></a>连接工具类:</h2><a id="more"></a>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ConnectionUtil</span> </span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 建立与RabbitMQ的连接</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Connection <span class="title">getConnection</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">//定义连接工厂</span></span><br><span class="line"> ConnectionFactory factory = <span class="keyword">new</span> ConnectionFactory();</span><br><span class="line"> <span class="comment">//设置服务地址</span></span><br><span class="line"> factory.setHost(<span class="string">"192.168.1.103"</span>);</span><br><span class="line"> <span class="comment">//端口</span></span><br><span class="line"> factory.setPort(<span class="number">5672</span>);</span><br><span class="line"> <span class="comment">//设置账号信息,用户名、密码、vhost</span></span><br><span class="line"> factory.setVirtualHost(<span class="string">"/kavito"</span>);<span class="comment">//设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机就相当于一个独立的mq</span></span><br><span class="line"> factory.setUsername(<span class="string">"kavito"</span>);</span><br><span class="line"> factory.setPassword(<span class="string">"123456"</span>);</span><br><span class="line"> <span class="comment">// 通过工厂获取连接</span></span><br><span class="line"> Connection connection = factory.newConnection();</span><br><span class="line"> <span class="keyword">return</span> connection;</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><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Send</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String QUEUE_NAME = <span class="string">"simple_queue"</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] argv)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">// 1、获取到连接</span></span><br><span class="line"> Connection connection = ConnectionUtil.getConnection();</span><br><span class="line"> <span class="comment">// 2、从连接中创建通道,使用通道才能完成消息相关的操作</span></span><br><span class="line"> Channel channel = connection.createChannel();</span><br><span class="line"> <span class="comment">// 3、声明(创建)队列</span></span><br><span class="line"> <span class="comment">//参数:String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments</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"> * 1、queue 队列名称</span></span><br><span class="line"><span class="comment"> * 2、durable 是否持久化,如果持久化,mq重启后队列还在</span></span><br><span class="line"><span class="comment"> * 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建</span></span><br><span class="line"><span class="comment"> * 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)</span></span><br><span class="line"><span class="comment"> * 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> channel.queueDeclare(QUEUE_NAME, <span class="keyword">false</span>, <span class="keyword">false</span>, <span class="keyword">false</span>, <span class="keyword">null</span>);</span><br><span class="line"> <span class="comment">// 4、消息内容</span></span><br><span class="line"> String message = <span class="string">"Hello World!"</span>;</span><br><span class="line"> <span class="comment">// 向指定的队列中发送消息</span></span><br><span class="line"> <span class="comment">//参数:String exchange, String routingKey, BasicProperties props, byte[] body</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"> * 1、exchange,交换机,如果不指定将使用mq的默认交换机(设置为"")</span></span><br><span class="line"><span class="comment"> * 2、routingKey,路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认交换机,routingKey设置为队列的名称</span></span><br><span class="line"><span class="comment"> * 3、props,消息的属性</span></span><br><span class="line"><span class="comment"> * 4、body,消息内容</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> channel.basicPublish(<span class="string">""</span>, QUEUE_NAME, <span class="keyword">null</span>, message.getBytes());</span><br><span class="line"> System.out.println(<span class="string">" [x] Sent '"</span> + message + <span class="string">"'"</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//关闭通道和连接(资源关闭最好用try-catch-finally语句处理)</span></span><br><span class="line"> channel.close();</span><br><span class="line"> connection.close();</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><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Recv</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> String QUEUE_NAME = <span class="string">"simple_queue"</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] argv)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">// 获取到连接</span></span><br><span class="line"> Connection connection = ConnectionUtil.getConnection();</span><br><span class="line"> <span class="comment">//创建会话通道,生产者和mq服务所有通信都在channel通道中完成</span></span><br><span class="line"> Channel channel = connection.createChannel();</span><br><span class="line"> <span class="comment">// 声明队列</span></span><br><span class="line"> <span class="comment">//参数:String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments</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"> * 1、queue 队列名称</span></span><br><span class="line"><span class="comment"> * 2、durable 是否持久化,如果持久化,mq重启后队列还在</span></span><br><span class="line"><span class="comment"> * 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建</span></span><br><span class="line"><span class="comment"> * 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)</span></span><br><span class="line"><span class="comment"> * 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> channel.queueDeclare(QUEUE_NAME, <span class="keyword">false</span>, <span class="keyword">false</span>, <span class="keyword">false</span>, <span class="keyword">null</span>);</span><br><span class="line"> <span class="comment">//实现消费方法</span></span><br><span class="line"> DefaultConsumer consumer = <span class="keyword">new</span> DefaultConsumer(channel){</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 class="doctag">@param</span> consumerTag 消费者标签,用来标识消费者的,在监听队列时设置channel.basicConsume</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> envelope 信封,通过envelope</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> properties 消息属性</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> body 消息内容</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> IOException</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleDelivery</span><span class="params">(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, <span class="keyword">byte</span>[] body)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> <span class="comment">//交换机</span></span><br><span class="line"> String exchange = envelope.getExchange();</span><br><span class="line"> <span class="comment">//消息id,mq在channel中用来标识消息的id,可用于确认消息已接收</span></span><br><span class="line"> <span class="keyword">long</span> deliveryTag = envelope.getDeliveryTag();</span><br><span class="line"> <span class="comment">// body 即消息体</span></span><br><span class="line"> String msg = <span class="keyword">new</span> String(body,<span class="string">"utf-8"</span>);</span><br><span class="line"> System.out.println(<span class="string">" [x] received : "</span> + msg + <span class="string">"!"</span>);</span><br><span class="line"> }</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">//参数:String queue, boolean autoAck, Consumer callback</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"> * 1、queue 队列名称</span></span><br><span class="line"><span class="comment"> * 2、autoAck 自动回复,当消费者接收到消息后要告诉mq消息已接收,如果将此参数设置为tru表示会自动回复mq,如果设置为false要通过编程实现回复</span></span><br><span class="line"><span class="comment"> * 3、callback,消费方法,当消费者接收到消息要执行的方法</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> channel.basicConsume(QUEUE_NAME, <span class="keyword">true</span>, consumer);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>RabbitMQ</category>
</categories>
<tags>
<tag>RabbitMQ</tag>
</tags>
</entry>
<entry>
<title>JAVA基础知识整理</title>
<url>/2020/05/26/JAVA%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E6%95%B4%E7%90%86/</url>
<content><![CDATA[<h3 id="面向对象"><a href="#面向对象" class="headerlink" title="面向对象"></a>面向对象</h3><ul>
<li><p>面向对象都有哪些特性以及你对这些特性的理解</p>
<ul>
<li>继承:继承是从已有类中继承信息创建新类的过程。提供继承信息的类被称为父类,继承信息的类被称为子类(派生类)。</li>
<li>封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已经定义的接口。</li>
<li>多态:多态是指允许不同子类型的对象对同一消息做出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。</li>
<li>抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。<a id="more"></a>
<code>注意:默认情况下面向对象有3大特征,封装、继承、多态;</code></li>
</ul>
</li>
<li><p>访问权限修饰符 public、private、protect,以及不写(默认)时的区别:</p>
</li>
</ul>
<table>
<thead>
<tr>
<th>修饰符</th>
<th>当前类</th>
<th>同包</th>
<th>子类</th>
<th>其他包</th>
</tr>
</thead>
<tbody><tr>
<td>public</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td>√</td>
</tr>
<tr>
<td>private</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td>×</td>
</tr>
<tr>
<td>default</td>
<td>√</td>
<td>√</td>
<td>×</td>
<td>×</td>
</tr>
<tr>
<td>protect</td>
<td>√</td>
<td>×</td>
<td>×</td>
<td>×</td>
</tr>
</tbody></table>
<ul>
<li>new 一个对象的过程和 clone 一个对象的过程区别 <ul>
<li>new 操作符的本意是分配内存。程序执行到 new 操作符时,首先去看 new 操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。</li>
<li>clone 在第一步是和 new 相似的,都是分配内存,调用 clone 方法时,分配的内存和原对象(即调用 clone 方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone 方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。</li>
</ul>
</li>
<li>如何进行深拷贝<ul>
<li>如果想要深拷贝一个对象,这个对象必须要实现 Cloneable 接口,实现 clone方法,并且在 clone 方法内部,把该对象引用的其他对象也要 clone 一份,这就要求这个被引用的对象必须也要实现Cloneable 接口并且实现 clone 方法。那么,按照上面的结论,实现以下代码 Body 类组合了 Head 类,要想深拷贝Body 类,必须在 Body 类的 clone 方法中将 Head 类也要拷贝一份。</li>
</ul>
</li>
</ul>
<h3 id="JavaSE-语法"><a href="#JavaSE-语法" class="headerlink" title="JavaSE 语法"></a>JavaSE 语法</h3><ul>
<li>& 和 && 的区别<ul>
<li>&运算符有两种用法:(1)按位与;(2)逻辑与。</li>
<li>&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是 null 而且不是空字符串,应<br>当写为 username != null &&!username.equals(“”),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的 equals 比较,否则会产生 NullPointerException 异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。</li>
</ul>
</li>
<li>两个对象值相同 (x.equals(y) == true) ,但却可有不同的 hashCode,这句话对不对?<ul>
<li>不对,如果两个对象 x 和 y 满足 x.equals(y) == true,它们的哈希码(hashCode)应当相同。</li>
<li>Java 对于 eqauls 方法和 hashCode 方法是这样规定的:(1)如果两个对象相同(equals 方法返回 true),那么它们的 hashCode 值一定要相同;(2)如果两个对象的 hashCode 相同,它们并不一定相同。</li>
</ul>
</li>
<li>重载(overload)和重写(override)的区别?重载的方法能否根据返回类型<br>进行区分?<ul>
<li>方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。</li>
<li>方法重载的规则:<ul>
<li>1.方法名一致,参数列表中参数的顺序,类型,个数不同。</li>
<li>2.重载与方法的返回值无关,存在于父类和子类,同类中。</li>
<li>3.可以抛出不同的异常,可以有不同修饰符。</li>
</ul>
</li>
<li>方法重写的规则:<ul>
<li>1.参数列表必须完全与被重写方法的一致,返回类型必须完全与被重写方法的返回类型一致。</li>
<li>2.构造方法不能被重写,声明为 final 的方法不能被重写,声明为 static 的方法不能被重写,但是能够被再次声明。</li>
<li>3.访问权限不能比父类中被重写的方法的访问权限更低。</li>
<li>4.重写的方法能够抛出任何非强制异常(UncheckedException,也叫非运行时异常),无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><code>为什么函数不能根据返回类型来区分重载? 答:因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。函数的返回值只是作为函数运行之后的一个“状态”,他是保持方法的调用者与被调用者进行通信的关键。并不能
作为某个方法的“标识”。</code></p>
<ul>
<li><p>抽象抽象类(abstract class)和接口(interface)有什么异同?</p>
<ul>
<li>不同:<ul>
<li>抽象类:<br>1.抽象类中可以定义构造器<br>2.可以有抽象方法和具体方法<br>3.接口中的成员全都是 public 的<br>4.抽象类中可以定义成员变量<br>5.有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法<br>6.抽象类中可以包含静态方法<br>7.一个类只能继承一个抽象类</li>
<li>接口:<br>1.接口中不能定义构造器<br>2.方法全部都是抽象方法<br>3.抽象类中的成员可以是 private、默认、protected、public<br>4.接口中定义的成员变量实际上都是常量<br>5.接口中不能有静态方法<br>6.一个类可以实现多个接口</li>
</ul>
</li>
<li>相同:<br>1.不能够实例化<br>2.可以将抽象类和接口类型作为引用类型<br>3.一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类</li>
</ul>
</li>
<li><p>阐述静态变量和实例变量的区别?</p>
<ul>
<li>静态变量: 是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;</li>
<li>实例变量: 必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。</li>
</ul>
</li>
<li><p>==和 equals 的区别?</p>
<ul>
<li>equals 和== 最大的区别是一个是方法一个是运算符。</li>
<li>==:如果比较的对象是基本数据类型,则比较的是数值是否相等;如果比较的是引用数据类型,则比较的是对象的地址值是否相等。</li>
<li>equals():用来比较方法两个对象的内容是否相等。<br>注意:equals 方法不能用于基本数据类型的变量,如果没有对 equals 方法进行重写,则比较的是引用类型的变量所指向的对象的地址。</li>
</ul>
</li>
<li><p>break 和 continue 的区别?</p>
<ul>
<li>break 和 continue 都是用来控制循环的语句。</li>
<li>break 用于完全结束一个循环,跳出循环体执行循环后面的语句。</li>
<li>continue 用于跳过本次循环,执行下次循环。</li>
</ul>
</li>
</ul>
<h3 id="java异常处理机制"><a href="#java异常处理机制" class="headerlink" title="java异常处理机制"></a>java异常处理机制</h3><ul>
<li>请写出你最常见的 5 个 RuntimeException<ul>
<li>1)java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。</li>
<li>2)java.lang.ClassNotFoundException 指定的类找不到;出现原因:类的名称和路径加载错误;通常都是程序试图通过字符串来加载某个类时可能引发异常。<br>3)java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。</li>
<li>4)java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生。</li>
<li>5)java.lang.IllegalArgumentException 方法传递参数错误。</li>
<li>6)java.lang.ClassCastException 数据类型转换异常。</li>
<li>7)java.lang.NoClassDefFoundException 未找到类定义错误。</li>
<li>8)SQLException SQL 异常,常见于操作数据库时的 SQL 语句错误。</li>
<li>9)java.lang.InstantiationException 实例化异常。</li>
<li>10)java.lang.NoSuchMethodException 方法不存在异常。</li>
</ul>
</li>
<li>throw 和 throws 的区别?<ul>
<li>throw:<br>1)throw 语句用在方法体内,表示抛出异常,由方法体内的语句处理。<br>2)throw 是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行 throw 一定是抛出了某种异常。</li>
<li>throws:<br>1)throws 语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。<br>2)throws 主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。<br>3)throws 表示出现异常的一种可能性,并不一定会发生这种异常。</li>
</ul>
</li>
<li>final、finally、finalize 的区别?<ul>
<li>1)final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,被其修饰的类不可继承。</li>
<li>2)finally:异常处理语句结构的一部分,表示总是执行。</li>
<li>3)finalize:Object 类的一个方法,在垃圾回收器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。该方法更像是一个对象生命周期的临终方法,当该方法被系统调用则代表该对象即将“死亡”,但是需要注意的是,我们主动行为上去调用该方法并不会导致该对象“死亡”,这是一个被动的方法(其实就是回调方法),不需要我们调用。</li>
</ul>
</li>
</ul>
<h3 id="JavaSE-常用-API"><a href="#JavaSE-常用-API" class="headerlink" title="JavaSE 常用 API"></a>JavaSE 常用 API</h3><ul>
<li><p>Java 中的日期和时间</p>
<ul>
<li><p>如何取得年月日、小时分钟秒?</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DateTimeTest</span> </span>{</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> Calendar cal = Calendar.getInstance();</span><br><span class="line"> System.out.println(cal.get(Calendar.YEAR));</span><br><span class="line"> System.out.println(cal.get(Calendar.MONTH)); <span class="comment">// 0 - 11</span></span><br><span class="line"> System.out.println(cal.get(Calendar.DATE));</span><br><span class="line"> System.out.println(cal.get(Calendar.HOUR_OF_DAY));</span><br><span class="line"> System.out.println(cal.get(Calendar.MINUTE));</span><br><span class="line"> System.out.println(cal.get(Calendar.SECOND));</span><br><span class="line"> <span class="comment">// Java 8</span></span><br><span class="line"> LocalDateTime dt = LocalDateTime.now();</span><br><span class="line"> System.out.println(dt.getYear());</span><br><span class="line"> System.out.println(dt.getMonthValue()); <span class="comment">// 1 - 12</span></span><br><span class="line"> System.out.println(dt.getDayOfMonth());</span><br><span class="line"> System.out.println(dt.getHour());</span><br><span class="line"> System.out.println(dt.getMinute());</span><br><span class="line"> System.out.println(dt.getSecond());</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
</li>
<li><p>如何取得从 1970 年 1 月 1 日 0 时 0 分 0 秒到现在的毫秒数?</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Calendar.getInstance().getTimeInMillis(); <span class="comment">//第一种方式</span></span><br><span class="line">System.currentTimeMillis(); <span class="comment">//第二种方式</span></span><br><span class="line"><span class="comment">// Java 8</span></span><br><span class="line">Clock.systemDefaultZone().millis();</span><br></pre></td></tr></table></figure>
</li>
<li><p>如何格式化日期?</p>
<ul>
<li>1)Java.text.DataFormat 的子类(如 SimpleDateFormat 类)中的 format(Date)方法可将日期格式化。</li>
<li>2)Java 8 中可以用 java.time.format.DateTimeFormatter 来格式化时间日期,代码如下所示:</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"> <span class="keyword">import</span> java.text.SimpleDateFormat;</span><br><span class="line"> <span class="keyword">import</span> java.time.LocalDate;</span><br><span class="line"> <span class="keyword">import</span> java.time.format.DateTimeFormatter;</span><br><span class="line"> <span class="keyword">import</span> java.util.Date;</span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">DateFormatTest</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> SimpleDateFormat oldFormatter = <span class="keyword">new</span> SimpleDateFormat(<span class="string">"yyyy/MM/dd"</span>);</span><br><span class="line"> Date date1 = <span class="keyword">new</span> Date();</span><br><span class="line"> System.out.println(oldFormatter.format(date1));</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// Java 8</span></span><br><span class="line"> DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern(<span class="string">"yyyy/MM/dd"</span>);</span><br><span class="line"> LocalDate date2 = LocalDate.now();</span><br><span class="line"> System.out.println(date2.format(newFormatter));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"><span class="comment">//.parse() 将字符串转为时间类型</span></span><br></pre></td></tr></table></figure>
<p><code>补充:Java 的时间日期 API 一直以来都是被诟病的东西,为了解决这一问题,Java 8 中引入了新的时间日期 API,其中包括 LocalDate、LocalTime、LocalDateTime、Clock、Instant 等类,这些的类的设计都使用了不变模式,因此是线程安全的设计。</code></p>
</li>
</ul>
</li>
<li><p>Java 的数据类型 </p>
<ul>
<li><p>Java 的基本数据类型都有哪些各占几个字节?</p>
<table>
<thead>
<tr>
<th align="center">四类</th>
<th align="center">八种</th>
<th align="center">字节数</th>
<th align="center">数据表示范围</th>
</tr>
</thead>
<tbody><tr>
<td align="center">整形</td>
<td align="center">byte</td>
<td align="center">1</td>
<td align="center">-128~127</td>
</tr>
<tr>
<td align="center">整形</td>
<td align="center">short</td>
<td align="center">2</td>
<td align="center">-32768~32767</td>
</tr>
<tr>
<td align="center">整形</td>
<td align="center">int</td>
<td align="center">4</td>
<td align="center">-2147483648~2147483647</td>
</tr>
<tr>
<td align="center">整形</td>
<td align="center">long</td>
<td align="center">8</td>
<td align="center">-2的63次幂~2的 63次幂-1</td>
</tr>
<tr>
<td align="center">浮点型</td>
<td align="center">float</td>