-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1451 lines (1011 loc) · 116 KB
/
index.html
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.3.0">
<link rel="apple-touch-icon" sizes="180x180" href="/blogOfLin/images/apple-touch-icon-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/blogOfLin/images/favicon-32x32-next.png">
<link rel="icon" type="image/png" sizes="16x16" href="/blogOfLin/images/favicon-16x16-next.png">
<link rel="mask-icon" href="/blogOfLin/images/logo.svg" color="#222">
<link rel="stylesheet" href="/blogOfLin/css/main.css">
<link rel="stylesheet" href="/blogOfLin/lib/font-awesome/css/all.min.css">
<script id="hexo-configurations">
var NexT = window.NexT || {};
var CONFIG = {"hostname":"veysky.github.io","root":"/blogOfLin/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"always","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":true,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
</script>
<meta name="description" content="personal blog">
<meta property="og:type" content="website">
<meta property="og:title" content="lin's blog home">
<meta property="og:url" content="https://veysky.github.io/blogOfLin/index.html">
<meta property="og:site_name" content="lin's blog home">
<meta property="og:description" content="personal blog">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="木林森">
<meta name="twitter:card" content="summary">
<link rel="canonical" href="https://veysky.github.io/blogOfLin/">
<script id="page-configurations">
// https://hexo.io/docs/variables.html
CONFIG.page = {
sidebar: "",
isHome : true,
isPost : false,
lang : 'en'
};
</script>
<title>lin's blog home</title>
<noscript>
<style>
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-header { opacity: initial; }
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
</head>
<body itemscope itemtype="http://schema.org/WebPage">
<div class="container use-motion">
<div class="headband"></div>
<header class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="Toggle navigation bar">
<span class="toggle-line toggle-line-first"></span>
<span class="toggle-line toggle-line-middle"></span>
<span class="toggle-line toggle-line-last"></span>
</div>
</div>
<div class="site-meta">
<a href="/blogOfLin/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<h1 class="site-title">lin's blog home</h1>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger">
</div>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="main-menu menu">
<li class="menu-item menu-item-home">
<a href="/blogOfLin/" rel="section"><i class="fa fa-home fa-fw"></i>Home</a>
</li>
<li class="menu-item menu-item-about">
<a href="/blogOfLin/about/" rel="section"><i class="fa fa-user fa-fw"></i>About</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/blogOfLin/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>Tags</a>
</li>
<li class="menu-item menu-item-categories">
<a href="/blogOfLin/categories/" rel="section"><i class="fa fa-th fa-fw"></i>Categories</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/blogOfLin/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>Archives</a>
</li>
</ul>
</nav>
</div>
</header>
<div class="back-to-top">
<i class="fa fa-arrow-up"></i>
<span>0%</span>
</div>
<div class="reading-progress-bar"></div>
<a role="button" class="book-mark-link book-mark-link-fixed"></a>
<a href="https://github.com/veySky" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
<main class="main">
<div class="main-inner">
<div class="content-wrap">
<div class="content index posts-expand">
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://veysky.github.io/blogOfLin/2023/05/05/%E7%A7%BB%E5%8A%A8%E8%B7%A8%E5%B9%B3%E5%8F%B0%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88%E9%80%89%E5%9E%8B%E5%BB%BA%E8%AE%AE/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/blogOfLin/images/avatar.gif">
<meta itemprop="name" content="木林森">
<meta itemprop="description" content="personal blog">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="lin's blog home">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/blogOfLin/2023/05/05/%E7%A7%BB%E5%8A%A8%E8%B7%A8%E5%B9%B3%E5%8F%B0%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88%E9%80%89%E5%9E%8B%E5%BB%BA%E8%AE%AE/" class="post-title-link" itemprop="url">移动跨平台技术方案选型建议</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2023-05-05 13:45:19 / Modified: 13:46:11" itemprop="dateCreated datePublished" datetime="2023-05-05T13:45:19+08:00">2023-05-05</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p>从 iPhone 诞生至今,智能手机风靡全球已将近 20 年,智能手机操作系统 iOS 和 Android 也成为当仁不让的顶流般的存在,而作为其背后的灵魂,移动应用也随着技术的发展已经越来越丰富。如果从技术层面来讲,移动 App 也从最开始单一的原生开发(Native App)模式,演变出了混合开发(Hybird App)、网页应用开发(Web App),为什么会有这种发展的变化呢?</p>
<p>因为原有的 Native App 有一个明显的痛点,就是相同的功能需要在不同的平台上都实现一遍,所以就有了一个很迫切的需求,能否只需要写一次代码,就可以在各个端都运行?</p>
<h1 id="移动跨平台的逻辑"><a href="#移动跨平台的逻辑" class="headerlink" title="移动跨平台的逻辑"></a>移动跨平台的逻辑</h1><p>跨平台开发从本质上讲是为了增加业务代码的复用率,减少因为要适配多个平台带来的工作量,从而降低开发成本。在提高业务专注度的同时,能够为用户提供一致的用户体验,实现“多快好省”的效果。</p>
<p>跨平台是跨哪些平台?怎么样的跨平台逻辑?从当前的实际情况来看,移动端跨平台需求主要集中在以下 3 点:</p>
<ul>
<li>桌面端跨移动端:桌面向移动端过渡的早期,希望 PC Web 与移动 Web 复用同一套代码。</li>
<li>Native 跨 Web:一套功能差不多的 Web 页能够在端外访问,需要跨 Native App 与 Web。</li>
<li>跨系统双端:出于开发效率等原因,希望 Android、iOS 双端复用一套业务代码,这也是目前主要的需求点。</li>
</ul>
<p>而放眼未来,我们预见可能还会有这些跨平台需求:</p>
<ul>
<li>跨小程序/轻应用:即用即走的轻量级应用,如各平台的小程序、 Android 快应用、iOS App Clips。</li>
<li>跨 IoT 设备:各种有显示屏的设备都会成为新的入口,如车载设备、智能电视等。</li>
</ul>
<h1 id="移动跨平台方案的发展"><a href="#移动跨平台方案的发展" class="headerlink" title="移动跨平台方案的发展"></a>移动跨平台方案的发展</h1><p>不仅是移动应用的开发模式在持续的演变,跨平台开发方案也紧紧的跟随着开发模式的变化持续的演进,按照技术的发展,跨平台方案可以分为三个时代。</p>
<h2 id="1、Web-容器时代"><a href="#1、Web-容器时代" class="headerlink" title="1、Web 容器时代"></a>1、Web 容器时代</h2><p>基于 Web 相关技术通过浏览器组件来实现界面及功能,典型的框架包括 Cordova、Ionic 和微信小程序。Web 时代的方案,主要采用的是原生应用内嵌浏览器控件 WebView 的方式进行 HTML5 页面渲染,并定义 HTML5 与原生代码交互协议,将部分原生系统能力暴露给 HTML5,从而扩展 HTML5 的边界。这类交互协议,就是我们通常说的 JS Bridge。</p>
<p><img src="https://static001.geekbang.org/infoq/99/99b90367dcf5d842e101731b69eea48a.png" alt="img"></p>
<h2 id="2、泛-Web-容器时代"><a href="#2、泛-Web-容器时代" class="headerlink" title="2、泛 Web 容器时代"></a>2、泛 Web 容器时代</h2><p>采用类 Web 标准进行开发,但在运行时把绘制和渲染交由原生系统接管的技术,代表框架有 React Native、Weex 和快应用等。过渡到泛 Web 容器时代,优化了 Web 容器时代的加载、解析和渲染这三大过程,把影响它们独立运行的 Web 标准进行了裁剪,以相对简单的方式支持了构建移动端页面必要的 Web 标准(如 Flexbox 等),也保证了便捷的前端开发体验;同时,这个时代的解决方案基本上完全放弃了浏览器控件渲染,而是采用原生自带的 UI 组件实现代替了核心的渲染引擎,仅保持必要的基本控件渲染能力,从而使得渲染过程更加简化,也保证了良好的渲染性能。</p>
<p><img src="https://static001.geekbang.org/infoq/dd/ddd4650b9a7efceb93ac2b3f52526599.png" alt="img"></p>
<h2 id="3、自绘引擎时代"><a href="#3、自绘引擎时代" class="headerlink" title="3、自绘引擎时代"></a>3、自绘引擎时代</h2><p>自带渲染引擎,客户端仅提供一块画布即可获得从业务逻辑到功能呈现的多端高度一致的渲染体验。Flutter,是为数不多的代表。Flutter 开辟了一种全新的思路,即从头到尾重写一套跨平台的 UI 框架,包括渲染逻辑,甚至是开发语言。</p>
<p><img src="https://static001.geekbang.org/infoq/ec/ec56252b4c5f9c8d2abf58b4d22d53c6.png" alt="img"></p>
<h1 id="移动跨平台技术方案的对比"><a href="#移动跨平台技术方案的对比" class="headerlink" title="移动跨平台技术方案的对比"></a>移动跨平台技术方案的对比</h1><p>对比现有的跨平台技术和解决方案也可以分为三类,分别是 Web 跨端、容器跨端、小程序跨端。</p>
<h2 id="1、Web-跨端"><a href="#1、Web-跨端" class="headerlink" title="1、Web 跨端"></a>1、Web 跨端</h2><p>Web 跨端比较好理解,因为 Web 与生俱来就有跨端的能力,因为只要有浏览器或 WebView,现在绝大多数端上(甚至包括封闭的小程序生态)都支持 Webview,所以只要开发网页然后投放到多个端即可轻松跨平台,例如 Web App、PWA(Progressive Web Apps)、Hybrid App、PHA(Progress Hybrid App)。</p>
<p><strong>优点:</strong></p>
<ul>
<li>没有额外的学习成本,一套基础技术吃天下</li>
<li>不依赖特殊的配套设施,从开发、调试到运维等所有工程化环节都是通用的</li>
<li>背靠 npm 庞大的生态,百万模块,应有尽有</li>
</ul>
<p><strong>缺点:</strong></p>
<ul>
<li>经常会遇到白屏、卡顿等情况,用户的体验不佳</li>
<li>无法调用系统的权限,例如多媒体、蓝牙、相机等</li>
<li>性能不好,对内存的消耗大</li>
</ul>
<h2 id="2、容器跨端"><a href="#2、容器跨端" class="headerlink" title="2、容器跨端"></a>2、容器跨端</h2><p>另一种统一多端的思路是将 Native 定制成标准容器,让同一份代码跑在一个个标准容器中。比较典型的代表是 React Native 、Flutter、Weex,这类方案通过尽可能的取长补短,综合了 Web 生态和 Native 组件,让 JS 执行代码后用 Native 的组件进行渲染,以解决抛弃 Web 历史包袱的问题。</p>
<p>具体来讲 React Native 可以跨 Android、iOS、Web、Windows 四端,Flutter 可以跨 Android、iOS、Web、Linux 四端,Weex 可以跨 Android、iOS、Web 三端。</p>
<p><strong>优点:</strong></p>
<ul>
<li>Flutter 快速的开发,富有表现力的精美 UI 和类似本机的性能</li>
<li>React Native 专注于用户界面,使应用程序开发人员能够构建高度可靠的界面</li>
<li>Weex 页面就像开发普通网页一样;在渲染 Weex 页面时和渲染原生页面一样</li>
</ul>
<p><strong>缺点:</strong></p>
<ul>
<li>React Native 没有提供的需要自定义的应用,仍然需要使用原生开发</li>
<li>Flutter 构建的应用程序文件很大,没有广泛的资源基础,这意味着可能找不到开发所需的第三方库和包</li>
<li>Weex 由于起步比较晚,社区活跃度不如 RN,资料和开源项目也相对较少</li>
</ul>
<h2 id="3、小程序跨端"><a href="#3、小程序跨端" class="headerlink" title="3、小程序跨端"></a>3、小程序跨端</h2><p>小程序跨端也比较好理解,就是让同样代码的小程序能够运行在多个 App 中,例如开发完一个小程序除了让其运行在微信之外,还能运行在支付宝、百度等超级 App,甚至是自己的 App 中。</p>
<p>如果说小程序仍然是依靠 Web 技术运行的,那为什么还要单独去使用小程序呢?就像前面所说到的一样,Web 始终没法调用例如相机、蓝牙等这样的权限,并且用户使用体验会收到一定的影响。而小程序则不同,小程序具有强大的 Web 渲染引擎、提供丰富组件、支持本地缓存、避免 DOM 泄露等,并且其初衷是开放,例如微信、支付宝这样的超级 App 也都相继开放了小程序上架能力,小程序逐渐成为跨 App 的正规方式。</p>
<p>后期也甚至出现了例如 FinClip 这样的<a href="https://xie.infoq.cn/link?target=https://www.finclip.com/product?channel=infoqseo">小程序容器</a>,可以让个人或企业自己的 App 具备小程序的运行能力,让其他 App 能够具有超级 App 一致的小程序跨端能力。</p>
<p><strong>优势:</strong></p>
<ul>
<li>具备类似 Native App 的体验度,使用较为流畅丝滑</li>
<li>可以获取用户的相册、多媒体、蓝牙等基础权限</li>
<li>可以通过便捷化的上下架方式完成相关页面和业务的热更新</li>
</ul>
<p><strong>缺点:</strong></p>
<ul>
<li>大平台的框架标准不统一,会稍微有影响,但都大同小异,W3C 也在做小程序的标准化工作</li>
<li>部分的插件会用到原生相关的技术</li>
</ul>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://veysky.github.io/blogOfLin/2023/04/03/redis%E9%9D%A2%E8%AF%95%E5%85%AB%E8%82%A1%E6%96%87/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/blogOfLin/images/avatar.gif">
<meta itemprop="name" content="木林森">
<meta itemprop="description" content="personal blog">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="lin's blog home">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/blogOfLin/2023/04/03/redis%E9%9D%A2%E8%AF%95%E5%85%AB%E8%82%A1%E6%96%87/" class="post-title-link" itemprop="url">redis面试八股文</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2023-04-03 19:36:02 / Modified: 19:57:20" itemprop="dateCreated datePublished" datetime="2023-04-03T19:36:02+08:00">2023-04-03</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/blogOfLin/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p><code>redis的常见面试问题</code></p>
<h3 id="什么是缓存击穿"><a href="#什么是缓存击穿" class="headerlink" title="什么是缓存击穿"></a>什么是缓存击穿</h3><p>一个访问量比较大的key,缓存过期,导致所有请求直接打到DB上。 </p>
<h4 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h4><pre><code>1. 加锁更新: 就是写缓存逻辑加排它锁,只有一个线程去访问db获取缓存内容写缓存。
1. 将过期时间组合写到value中,通过异步方式不断更新缓存内容刷新过期时间,防止此类现象。 (线上一般很少使用这种方式)
</code></pre>
<h3 id="什么是缓存穿透"><a href="#什么是缓存穿透" class="headerlink" title="什么是缓存穿透"></a>什么是缓存穿透</h3><p>缓存穿透是指:查询缓存和数据库中都不存在的数据。 这样每次请求都直接打到数据库。 就好像缓存不存在</p>
<p>导致问题: 不存在的数据每次请求都要到存储层查询,失去了缓存保护后端存储的意义。 </p>
<h4 id="原因:"><a href="#原因:" class="headerlink" title="原因:"></a>原因:</h4><ol>
<li><p>业务代码有问题</p>
<pre><code> 2. 恶意攻击,爬虫造成空命中。
</code></pre>
</li>
</ol>
<h4 id="解决方案:"><a href="#解决方案:" class="headerlink" title="解决方案:"></a>解决方案:</h4><ol>
<li><p>缓存空值/默认值</p>
<ol>
<li>这种方式是在数据库不命中情况把一个空值,或者默认值保存到缓存,之后再访问这个数据就会从缓存中获取,保护了数据库</li>
</ol>
<p>缓存空值会造成的问题: </p>
<pre><code>1. 空值被缓存,意味这缓存层需要更多的内存空间,有效解决办法: 这类缓存设置一个较短的过期时间,让其自行删除。
2. 缓存层和db层数据会有一段时间的不一致,可能对业务有一定影响。 解决方案: 利用消息队列或者其他异步方式清理缓存中的空对象。
</code></pre>
</li>
<li><p>布隆过滤器方案:</p>
<ol>
<li>可以在存储和缓存之前加一个布隆过滤器,做一层过滤。 布隆过滤器会保存数据是否存在,如果判断是不不存在直接返回,就不会访问存储</li>
</ol>
</li>
</ol>
<h3 id="缓存雪崩"><a href="#缓存雪崩" class="headerlink" title="缓存雪崩"></a>缓存雪崩</h3><p>在某一时刻 发生大规模的缓存失效情况。 例如缓存服务宕机,大量key在同一时间过期。 这样的后果就是大量请求进来直接打到db上,可能导致整个系统的崩溃。 称为雪崩。 </p>
<p>缓存雪崩是三大缓存问题中最严重的</p>
<h4 id="预防和处理:"><a href="#预防和处理:" class="headerlink" title="预防和处理:"></a>预防和处理:</h4><ul>
<li>提高缓存可用性<ol>
<li>集群部署:通过集群提升缓存的可用性</li>
<li>多级缓存:设置多级缓存。 第一级缓存失效的情况下访问第二级缓存,每一级缓存失效时间都不同。</li>
</ol>
</li>
<li>过期时间调整<ol>
<li>均匀过期: 为了避免大量缓存同一时间过期,可以把不同key的过期时间随机生成,避免过期时间太过集中。 </li>
<li>热点数据永不过期。</li>
</ol>
</li>
<li>熔断降级 <ol>
<li>服务熔断: 当缓存服务宕机或者响应超时,为了防止整个系统出现雪崩,暂时停止业务服务访问缓存系统。 </li>
<li>服务降级: 当出现大量缓存失效,而且处于高并发高负荷的情况下,在业务逻辑内部舍弃一些非核心接口和数据的请求。 直接返回一个提前准备好的 fallback(退路) 错误处理信息</li>
</ol>
</li>
</ul>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://veysky.github.io/blogOfLin/2023/04/01/%E7%BC%93%E5%AD%98%E6%95%B0%E6%8D%AE%E4%B8%80%E8%87%B4%E6%80%A7%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/blogOfLin/images/avatar.gif">
<meta itemprop="name" content="木林森">
<meta itemprop="description" content="personal blog">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="lin's blog home">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/blogOfLin/2023/04/01/%E7%BC%93%E5%AD%98%E6%95%B0%E6%8D%AE%E4%B8%80%E8%87%B4%E6%80%A7%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" class="post-title-link" itemprop="url">缓存数据一致性问题解决方案</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2023-04-01 22:54:58" itemprop="dateCreated datePublished" datetime="2023-04-01T22:54:58+08:00">2023-04-01</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2023-04-03 20:29:04" itemprop="dateModified" datetime="2023-04-03T20:29:04+08:00">2023-04-03</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/blogOfLin/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="问题:如何保证本地缓存和redis缓存数据的一致性"><a href="#问题:如何保证本地缓存和redis缓存数据的一致性" class="headerlink" title="问题:如何保证本地缓存和redis缓存数据的一致性"></a>问题:如何保证本地缓存和redis缓存数据的一致性</h2><p>这个问题日常工作中应当比较常遇到,通用解决方案:</p>
<h4 id="方案一:"><a href="#方案一:" class="headerlink" title="方案一:"></a>方案一:</h4><p> 业务逻辑确定本地缓存有效期。就是给本地缓存添加一个相对短的过期时间。 适用与一些非重点数据,例如用户头像等可以接受短时间数据不一致情况。 </p>
<h4 id="方案二:"><a href="#方案二:" class="headerlink" title="方案二:"></a>方案二:</h4><p>采用Redis的发布/订阅机制(或者Rocketmq等) 分布式集群的所有阶段订阅删除。 </p>
<h4 id="方案三:"><a href="#方案三:" class="headerlink" title="方案三:"></a>方案三:</h4><p>通过调用接口方式删除。 实际操作是在数据变更发生是,触发接口调用所有容器删除本地缓存接口。 这样做的情况 需要在每个容器启动时注册当前容器ip和端口信息。 方案优点: 响应快,稳定性好。 缺点: 实现复杂度高。 </p>
<h4 id="方案四:"><a href="#方案四:" class="headerlink" title="方案四:"></a>方案四:</h4><p>通过Zookeeper中间件方式: 实现单个容器操作数据变更时 其他容器接收到数据变更消息删除本地缓存重新拉取。 </p>
<h2 id="问题:-如何保证redis缓存和mysql数据的一致性"><a href="#问题:-如何保证redis缓存和mysql数据的一致性" class="headerlink" title="问题: 如何保证redis缓存和mysql数据的一致性"></a>问题: 如何保证redis缓存和mysql数据的一致性</h2><p><strong>网上资料的解决方案:</strong></p>
<h4 id="选择合适的缓存更新策略"><a href="#选择合适的缓存更新策略" class="headerlink" title="选择合适的缓存更新策略"></a>选择合适的缓存更新策略</h4><ol>
<li><p>删除缓存而不是更新缓存</p>
<p>当一个线程对缓存的key进行写操作的时候,如果其他线程进来读数据库的时候,读到的就是脏数据。 产生了数据不一致问题。 </p>
<p>相比较而言: 删除缓存的速度,比更新缓存的速度快很多。 所以用时相对小很多。 读脏数据的概率也小很多。 </p>
</li>
<li><p>先更新数据后删除缓存</p>
<p>更新数据耗时是在删除缓存的百倍以上 。 如果先删缓存,在数据更新成功之前,其他线程获取数据会把脏数据写入到缓存。 </p>
<p>目前最流行的缓存读写策略就是采用先更新数据库,再删缓存的方式。</p>
</li>
</ol>
<h4 id="缓存不一致的处理"><a href="#缓存不一致的处理" class="headerlink" title="缓存不一致的处理"></a>缓存不一致的处理</h4><p>如果不是并发特别高,对缓存依赖性很强,其实一定程度的不一致是可以接受的。 </p>
<p>缓存和数据库数据不一致常见的原因: </p>
<ul>
<li>缓存key删除失败</li>
<li>并发导致写入脏数据</li>
</ul>
<p>解决方案: </p>
<p><strong>通过消息队列保证key被删除</strong>。 把要删除或者删除失败的key写入消息队列,利用消息队列的重试机制,重试删除对应key。 </p>
<p>缺点: 代码侵入</p>
<p><strong>数据库订阅+消息队列保证key被删除</strong> 可以用一个服务监听数据库的binlog 获取需要操作的数据。 然后用一个公共服务获取订阅程序传来的信息,进行缓存删除操作。 这种方式降低了对业务代码侵入,但是复杂度提升了。 适合基建完善的大厂。 </p>
<p><strong>延时双删防止脏数据</strong> 在缓存不存在的情况,写入了脏数据,这种情况先删缓存,再更新数据库的缓存更新策略下发生比较多。 解决方案: 延时双删</p>
<p>操作: 在第一次删除缓存后过了一段时间再次删除缓存。 这种方式的延时时间需要仔细斟酌。</p>
<p><strong>设置缓存过期时间兜底</strong></p>
<p>这个是朴素但是有用的方案,给缓存设置一个过期时间,即使发生数据不一致问题,它也不会永远不一致下去。 </p>
<p>**下面是我自己整理的解决方案: **</p>
<p>这个问题遇到的应当是最多的。 </p>
<p>常规解决方案: </p>
<h3 id="方案一:-1"><a href="#方案一:-1" class="headerlink" title="方案一:"></a>方案一:</h3><p>先删除缓存数据,然后执行mysql的数据更新。 这种方案 如果缓存没有有效期的情况 可能存在脏读。 就是缓存了 更新前的数据。 </p>
<p>解决方案:mysql 数据更新后 再次删除缓存。 也就是二次删除。 这样可以保证redis的数据 跟db数据一致。 这里有个前提,redis数据读取的需要是主库数据,如果是从库数据,可能存在主从同步延迟,导致重新读取的数据还是旧数据情况。 </p>
<h3 id="方案二:-1"><a href="#方案二:-1" class="headerlink" title="方案二:"></a>方案二:</h3><p>异步删除缓存。 先更新db数据,然后通过binlog或者rocketmq等方式 消费消息时进行缓存key的删除。 这样可以保证缓存数据一定被成功删除。 例如rocketmq消息消费失败,就是删除失败情况 可以多次尝试。 缺点: 对业务侵入大。 </p>
<h3 id="方案三:-1"><a href="#方案三:-1" class="headerlink" title="方案三:"></a>方案三:</h3><p>设置缓存过期时间,其实这个应当是基本要求,所有缓存都要设置过期时间。 但是设置过期时间后会引发另外一个 redis穿透问题。 需要对这种情况进行特殊处理。 </p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://veysky.github.io/blogOfLin/2023/03/31/mybatis%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%B8%80%EF%BC%89/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/blogOfLin/images/avatar.gif">
<meta itemprop="name" content="木林森">
<meta itemprop="description" content="personal blog">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="lin's blog home">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/blogOfLin/2023/03/31/mybatis%E6%BA%90%E7%A0%81%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%B8%80%EF%BC%89/" class="post-title-link" itemprop="url">mybatis源码学习(一)</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2023-03-31 14:47:47 / Modified: 15:04:43" itemprop="dateCreated datePublished" datetime="2023-03-31T14:47:47+08:00">2023-03-31</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/blogOfLin/categories/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="大纲"><a href="#大纲" class="headerlink" title="大纲"></a>大纲</h2><p>mybatis代码结构分为三个大部分</p>
<ul>
<li>接口层 提供SqlSession</li>
<li>核心层</li>
<li>基础支持层 包含: 解析器模块,反射器模块,日志模块,类型转换模块 。。。 (待完善)</li>
</ul>
<h3 id="基础支持层"><a href="#基础支持层" class="headerlink" title="基础支持层"></a>基础支持层</h3><h4 id="解析器模块:"><a href="#解析器模块:" class="headerlink" title="解析器模块:"></a>解析器模块:</h4><pre><code>这个模块主要是用于解析Xml文件,有mybatis-config.xml 和 Mapper.xml 等。
</code></pre>
<p>包含多个重点类: Xnode 是一个包装Dom的node类,用于解析属性信息。 使用到了 XpathParse 类 解析路径,这个parse使用了通用占位符解析,可以对属性值的占位符进行替换。 支持默认值。 </p>
<h4 id="反射器模块"><a href="#反射器模块" class="headerlink" title="反射器模块"></a>反射器模块</h4><pre><code>这个模块是mybatis增强后的反射模式,提供Reflector 每个对象对应一个类。 对这个类进行属性解析,方法,构造方法等的记录,能够支持操纵类。
</code></pre>
<h4 id="类型转换模块"><a href="#类型转换模块" class="headerlink" title="类型转换模块"></a>类型转换模块</h4><pre><code>这个模块主要作用是用于对 JavaType和JdbcType进行互相转换。 由于mysql的字段类型和Java的字段类型不一致,所以才有的这个模块。 这里使用TypeHandler 接口定义了 设置属性和获取属性的方法。 有一个BaseTypeHandler 抽象实现类,实现了空值的设置和获取方法,非空置的处理 交给对应子类实现。
类型转换模块的使用是通过TypeHandlerRegiester 进行注册管理。 mybatis默认实现了java所有常用类型对应TypeHandler的实现方法。 可以支持自定义类型实现方法。 通过在mybatis-config.xml 文件添加 typeHandler 配置指定实现类。
类型转换模块提供了 类型别名功能,对表名,列名等支持设置别名。 另外也支持对类设置别名。 mybatis对常用类型默认直接设置了别名。
</code></pre>
<h4 id="日志模块"><a href="#日志模块" class="headerlink" title="日志模块"></a>日志模块</h4><pre><code>这个模块是对常用第三方日志组件的封装,通过适配器模式定义了 Log接口。 为每一个Log组件实现了具体方法。
</code></pre>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://veysky.github.io/blogOfLin/2023/03/30/Spring-%E5%B8%B8%E7%94%A8%E6%B3%A8%E8%A7%A3/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/blogOfLin/images/avatar.gif">
<meta itemprop="name" content="木林森">
<meta itemprop="description" content="personal blog">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="lin's blog home">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/blogOfLin/2023/03/30/Spring-%E5%B8%B8%E7%94%A8%E6%B3%A8%E8%A7%A3/" class="post-title-link" itemprop="url">Spring 常用注解</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2023-03-30 10:18:25 / Modified: 17:02:04" itemprop="dateCreated datePublished" datetime="2023-03-30T10:18:25+08:00">2023-03-30</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/blogOfLin/categories/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<p><img src="https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/sidebar/sanfene/spring-8d0a1518-a425-4887-9735-45321095d927.png" alt="Java Spring 常用注解"></p>
<h3 id="Web"><a href="#Web" class="headerlink" title="Web:"></a><strong>Web</strong>:</h3><ul>
<li>@Controller:组合注解(组合了@Component 注解),应用在 MVC 层(控制层)。</li>
<li>@RestController:该注解为一个组合注解,相当于@Controller 和@ResponseBody 的组合,注解在类上,意味着,该 Controller 的所有方法都默认加上了@ResponseBody。</li>
<li>@RequestMapping:用于映射 Web 请求,包括访问路径和参数。如果是 Restful 风格接口,还可以根据请求类型使用不同的注解:<ul>
<li>@GetMapping</li>
<li>@PostMapping</li>
<li>@PutMapping</li>
<li>@DeleteMapping</li>
</ul>
</li>
<li><strong>@ResponseBody</strong>:支持将返回值放在 response 内,而不是一个页面,<strong>通常用于返回 json 数据。</strong></li>
<li>@RequestBody:允许 request 的参数在 request 体中,而不是在直接连接在地址后面。</li>
<li>@PathVariable:用于接收路径参数,比如 <code>@RequestMapping(“/hello/{name}”)</code>申明的路径,将注解放在参数中前,即可获取该值,通常作为 Restful 的接口实现方法。</li>
</ul>
<h3 id="容器"><a href="#容器" class="headerlink" title="容器:"></a><strong>容器</strong>:</h3><ul>
<li><strong>@Component</strong>:表示一个带注释的类是一个“组件”,成为 Spring 管理的 Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component 还是一个<strong>元注解</strong>。</li>
<li>@Service:组合注解(组合了@Component 注解),应用在 service 层(业务逻辑层)。</li>
<li>@Repository:组合注解(组合了@Component 注解),应用在 dao 层(数据访问层)。</li>
<li><strong>@Autowired</strong>:Spring 提供的工具(由 Spring 的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入)。</li>
<li><strong>@Qualifier</strong>:该注解通常跟 @Autowired 一起使用,当想对注入的过程做更多的控制,@Qualifier 可帮助配置,比如两个以上相同类型的 Bean 时 Spring 无法抉择,用到此注解</li>
<li>@Configuration:声明当前类是一个配置类(相当于一个 Spring 配置的 xml 文件)</li>
<li>@Value:可用在字段,构造器参数跟方法参数,指定一个默认值,支持 <code>#{} 跟 \${}</code> 两个方式。一般将 SpringbBoot 中的 application.properties 配置的属性值赋值给变量。</li>
<li>@Bean:注解在方法上,声明当前方法的返回值为一个 Bean。返回的 Bean 对应的类中可以定义 init()方法和 destroy()方法,然后在<code>@Bean(initMethod=”init”,destroyMethod=”destroy”)</code>定义,在构造之后执行 init,在销毁之前执行 destroy。</li>
<li>@Scope:定义我们采用什么模式去创建 Bean(方法上,得有@Bean) 其设置类型包括:Singleton 、Prototype、Request 、 Session、GlobalSession。</li>
</ul>
<h3 id="AOP"><a href="#AOP" class="headerlink" title="AOP:"></a><strong>AOP</strong>:</h3><ul>
<li><p>@Aspect:声明一个切面(类上) 使用@After、@Before、@Around 定义建言(advice),可直接将拦截规则(切点)作为参数。</p>
<ul>
<li><code>@After</code> :在方法执行之后执行(方法上)。</li>
<li><code>@Before</code>: 在方法执行之前执行(方法上)。</li>
<li><code>@Around</code>: 在方法执行之前与之后执行(方法上)。</li>
<li><code>@PointCut</code>: 声明切点 在 java 配置类中使用@EnableAspectJAutoProxy 注解开启 Spring 对 AspectJ 代理的支持(类上)。</li>
</ul>
</li>
</ul>
<h3 id="事务:"><a href="#事务:" class="headerlink" title="事务:"></a><strong>事务:</strong></h3><ul>
<li>@Transactional:在要开启事务的方法上使用@Transactional 注解,即可声明式开启事务。</li>
</ul>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://veysky.github.io/blogOfLin/2023/03/29/Java%E4%BB%8E%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96%E5%86%85%E5%AE%B9/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/blogOfLin/images/avatar.gif">
<meta itemprop="name" content="木林森">
<meta itemprop="description" content="personal blog">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="lin's blog home">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/blogOfLin/2023/03/29/Java%E4%BB%8E%E6%96%87%E4%BB%B6%E8%AF%BB%E5%8F%96%E5%86%85%E5%AE%B9/" class="post-title-link" itemprop="url">Java从文件读取内容</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2023-03-29 22:31:24 / Modified: 23:01:06" itemprop="dateCreated datePublished" datetime="2023-03-29T22:31:24+08:00">2023-03-29</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/blogOfLin/categories/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="总结通过文件获取内容方式"><a href="#总结通过文件获取内容方式" class="headerlink" title="总结通过文件获取内容方式"></a>总结通过文件获取内容方式</h2><p>在java中有很多读取文本文件的方法。文本文件由字符组成,因此可以使用<code>Reader</code>类。在java中读取文本文件也有一些实用程序类。</p>
<ul>
<li>使用<code>Files</code>类读取文本文件;</li>
<li>使用<code>FileReader</code>类读取文本文件;</li>
<li>使用<code>BufferedReader</code>类读取文本文件;</li>
<li>使用<code>Scanner</code>类读取文本文件;</li>
</ul>
<h3 id="方法一-使用java-nio-file-Files读取文本文件"><a href="#方法一-使用java-nio-file-Files读取文本文件" class="headerlink" title="方法一 使用java.nio.file.Files读取文本文件"></a>方法一 使用java.nio.file.Files读取文本文件</h3><p>使用<code>Files</code>类将文件的所有内容读入字节数组。<code>Files</code>类还有一个方法可以读取所有行到字符串列表。<code>Files</code>类是在Java 7中引入的,如果想加载所有文件内容,使用这个类是比较适合的。只有在处理小文件并且需要加载所有文件内容到内存中时才应使用此方法。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">String fileName = "D:/maxsu/docs/source.txt";</span><br><span class="line">Path path = Paths.get(fileName);</span><br><span class="line">byte[] bytes = Files.readAllBytes(path);</span><br><span class="line">List<String> allLines = Files.readAllLines(path, StandardCharsets.UTF_8);</span><br><span class="line"> </span><br></pre></td></tr></table></figure>
<h3 id="方法二:使用java-io-FileReader类"><a href="#方法二:使用java-io-FileReader类" class="headerlink" title="方法二:使用java.io.FileReader类"></a>方法二:使用java.io.FileReader类</h3><p>可以使用<code>FileReader</code>获取<code>BufferedReader</code>,然后逐行读取文件。FileReader不支持编码并使用系统默认编码,因此它不是一种java中读取文本文件的非常有效的方法。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">String fileName = "D:/maxsu/docs/source.txt";</span><br><span class="line">File file = new File(fileName);</span><br><span class="line">FileReader fr = new FileReader(file);</span><br><span class="line">BufferedReader br = new BufferedReader(fr);</span><br><span class="line">String line;</span><br><span class="line">while((line = br.readLine()) != null){</span><br><span class="line"> // 一行一行地处理...</span><br><span class="line"> System.out.println(line);</span><br><span class="line">}</span><br><span class="line"> </span><br></pre></td></tr></table></figure>
<h3 id="方法三:使用java-io-BufferedReader"><a href="#方法三:使用java-io-BufferedReader" class="headerlink" title="方法三:使用java.io.BufferedReader"></a>方法三:使用java.io.BufferedReader</h3><p>如果想逐行读取文件并对它们进行处理,那么<code>BufferedReader</code>是非常合适的。它适用于处理大文件,也支持编码。</p>
<p><code>BufferedReader</code>是同步的,因此可以安全地从多个线程完成对<code>BufferedReader</code>的读取操作。<code>BufferedReader</code>的默认缓冲区大小为:<code>8KB</code>。 </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">String fileName = "D:/maxsu/docs/source.txt";</span><br><span class="line">File file = new File(fileName);</span><br><span class="line">FileInputStream fis = new FileInputStream(file);</span><br><span class="line">InputStreamReader isr = new InputStreamReader(fis, cs);</span><br><span class="line">BufferedReader br = new BufferedReader(isr);</span><br><span class="line"></span><br><span class="line">String line;</span><br><span class="line">while((line = br.readLine()) != null){</span><br><span class="line"> //process the line</span><br><span class="line"> System.out.println(line);</span><br><span class="line">}</span><br><span class="line">br.close(); </span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="方法四:使用Scanner读取文本文件"><a href="#方法四:使用Scanner读取文本文件" class="headerlink" title="方法四:使用Scanner读取文本文件"></a>方法四:使用Scanner读取文本文件</h3><p>如果要逐行读取文件或基于某些java正则表达式读取文件,则可使用<code>Scanner</code>类。</p>
<p><code>Scanner</code>类使用分隔符模式将其输入分解为标记,分隔符模式默认匹配空格。然后可以使用各种下一种方法将得到的标记转换成不同类型的值。<code>Scanner</code>类不同步,因此不是线程安全的。 </p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Path path = Paths.get(fileName);</span><br><span class="line">Scanner scanner = new Scanner(path);</span><br><span class="line">System.out.println("Read text file using Scanner");</span><br><span class="line">// 一行一行地读取</span><br><span class="line">while(scanner.hasNextLine()){</span><br><span class="line"> //process each line</span><br><span class="line"> String line = scanner.nextLine();</span><br><span class="line"> System.out.println(line);</span><br><span class="line">}</span><br><span class="line">scanner.close(); </span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="方法五:-使用RandomAccessFile-读取文件"><a href="#方法五:-使用RandomAccessFile-读取文件" class="headerlink" title="方法五: 使用RandomAccessFile 读取文件"></a>方法五: 使用RandomAccessFile 读取文件</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">RandomAccessFile file = new RandomAccessFile("D:/maxsu/docs/readme.txt", "r");</span><br><span class="line">String str;</span><br><span class="line"></span><br><span class="line">while ((str = file.readLine()) != null) {</span><br><span class="line"> System.out.println(str);</span><br><span class="line">}</span><br><span class="line">file.close(); </span><br></pre></td></tr></table></figure>
<h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line">package com.journaldev.files;</span><br><span class="line"></span><br><span class="line">import java.io.BufferedReader;</span><br><span class="line">import java.io.File;</span><br><span class="line">import java.io.FileInputStream;</span><br><span class="line">import java.io.FileReader;</span><br><span class="line">import java.io.IOException;</span><br><span class="line">import java.io.InputStreamReader;</span><br><span class="line">import java.nio.charset.Charset;</span><br><span class="line">import java.nio.charset.StandardCharsets;</span><br><span class="line">import java.nio.file.Files;</span><br><span class="line">import java.nio.file.Path;</span><br><span class="line">import java.nio.file.Paths;</span><br><span class="line">import java.util.List;</span><br><span class="line">import java.util.Scanner;</span><br><span class="line"></span><br><span class="line">public class JavaReadFile {</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) throws IOException {</span><br><span class="line"> String fileName = "D:/maxsu/docs/source.txt";</span><br><span class="line"></span><br><span class="line"> //使用Java 7中的Files类处理小文件,获取完整的文件数据</span><br><span class="line"> readUsingFiles(fileName);</span><br><span class="line"></span><br><span class="line"> //使用Scanner类来处理大文件,逐行读取</span><br><span class="line"> readUsingScanner(fileName);</span><br><span class="line"></span><br><span class="line"> //使用BufferedReader读取,逐行读取</span><br><span class="line"> readUsingBufferedReader(fileName);</span><br><span class="line"> readUsingBufferedReaderJava7(fileName, StandardCharsets.UTF_8);</span><br><span class="line"> readUsingBufferedReader(fileName, StandardCharsets.UTF_8);</span><br><span class="line"></span><br><span class="line"> //使用FileReader读取,没有编码支持,效率不高</span><br><span class="line"> readUsingFileReader(fileName);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private static void readUsingFileReader(String fileName) throws IOException {</span><br><span class="line"> File file = new File(fileName);</span><br><span class="line"> FileReader fr = new FileReader(file);</span><br><span class="line"> BufferedReader br = new BufferedReader(fr);</span><br><span class="line"> String line;</span><br><span class="line"> System.out.println("Reading text file using FileReader");</span><br><span class="line"> while((line = br.readLine()) != null){</span><br><span class="line"> //逐行读取</span><br><span class="line"> System.out.println(line);</span><br><span class="line"> }</span><br><span class="line"> br.close();</span><br><span class="line"> fr.close();</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private static void readUsingBufferedReader(String fileName, Charset cs) throws IOException {</span><br><span class="line"> File file = new File(fileName);</span><br><span class="line"> FileInputStream fis = new FileInputStream(file);</span><br><span class="line"> InputStreamReader isr = new InputStreamReader(fis, cs);</span><br><span class="line"> BufferedReader br = new BufferedReader(isr);</span><br><span class="line"> String line;</span><br><span class="line"> System.out.println("Read text file using InputStreamReader");</span><br><span class="line"> while((line = br.readLine()) != null){</span><br><span class="line"> //逐行读取</span><br><span class="line"> System.out.println(line);</span><br><span class="line"> }</span><br><span class="line"> br.close();</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private static void readUsingBufferedReaderJava7(String fileName, Charset cs) throws IOException {</span><br><span class="line"> Path path = Paths.get(fileName);</span><br><span class="line"> BufferedReader br = Files.newBufferedReader(path, cs);</span><br><span class="line"> String line;</span><br><span class="line"> System.out.println("Read text file using BufferedReader Java 7 improvement");</span><br><span class="line"> while((line = br.readLine()) != null){</span><br><span class="line"> //逐行读取</span><br><span class="line"> System.out.println(line);</span><br><span class="line"> }</span><br><span class="line"> br.close();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private static void readUsingBufferedReader(String fileName) throws IOException {</span><br><span class="line"> File file = new File(fileName);</span><br><span class="line"> FileReader fr = new FileReader(file);</span><br><span class="line"> BufferedReader br = new BufferedReader(fr);</span><br><span class="line"> String line;</span><br><span class="line"> System.out.println("Read text file using BufferedReader");</span><br><span class="line"> while((line = br.readLine()) != null){</span><br><span class="line"> //逐行读取</span><br><span class="line"> System.out.println(line);</span><br><span class="line"> }</span><br><span class="line"> //close resources</span><br><span class="line"> br.close();</span><br><span class="line"> fr.close();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private static void readUsingScanner(String fileName) throws IOException {</span><br><span class="line"> Path path = Paths.get(fileName);</span><br><span class="line"> Scanner scanner = new Scanner(path);</span><br><span class="line"> System.out.println("Read text file using Scanner");</span><br><span class="line"> //逐行读取</span><br><span class="line"> while(scanner.hasNextLine()){</span><br><span class="line"> //逐行处理</span><br><span class="line"> String line = scanner.nextLine();</span><br><span class="line"> System.out.println(line);</span><br><span class="line"> }</span><br><span class="line"> scanner.close();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private static void readUsingFiles(String fileName) throws IOException {</span><br><span class="line"> Path path = Paths.get(fileName);</span><br><span class="line"> //将文件读取到字节数组</span><br><span class="line"> byte[] bytes = Files.readAllBytes(path);</span><br><span class="line"> System.out.println("Read text file using Files class");</span><br><span class="line"> //read file to String list</span><br><span class="line"> @SuppressWarnings("unused")</span><br><span class="line"> List<String> allLines = Files.readAllLines(path, StandardCharsets.UTF_8);</span><br><span class="line"> System.out.println(new String(bytes));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://veysky.github.io/blogOfLin/2023/03/29/Java%E4%BB%8E%E6%8E%A7%E5%88%B6%E5%8F%B0%E8%BE%93%E5%85%A5%E6%96%B9%E5%BC%8F/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/blogOfLin/images/avatar.gif">
<meta itemprop="name" content="木林森">
<meta itemprop="description" content="personal blog">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="lin's blog home">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/blogOfLin/2023/03/29/Java%E4%BB%8E%E6%8E%A7%E5%88%B6%E5%8F%B0%E8%BE%93%E5%85%A5%E6%96%B9%E5%BC%8F/" class="post-title-link" itemprop="url">Java从控制台输入方式</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2023-03-29 22:14:03 / Modified: 22:30:54" itemprop="dateCreated datePublished" datetime="2023-03-29T22:14:03+08:00">2023-03-29</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/blogOfLin/categories/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>今天面试时遇到了一套计算题,计算逻辑实现很简单,被从控制台获取输入信息难住了。 </p>
<p>所以面试结束后,查了下资料。 原来很简单…… </p>
<h2 id="从控制台获取输入内容的方式有那些"><a href="#从控制台获取输入内容的方式有那些" class="headerlink" title="从控制台获取输入内容的方式有那些"></a>从控制台获取输入内容的方式有那些</h2><h3 id="使用阅读缓冲器类"><a href="#使用阅读缓冲器类" class="headerlink" title="使用阅读缓冲器类"></a>使用阅读缓冲器类</h3><p>这个是经典的取输入方式 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">BufferedReader</span> <span class="variable">reader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">InputStreamReader</span>(System.in));</span><br><span class="line"> <span class="comment">// Reading data using readLine</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">while</span> (!<span class="string">"0"</span>.equals(name)) {</span><br><span class="line"> name = reader.readLine();</span><br><span class="line"> System.out.println(name);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h3 id="使用Scanner类"><a href="#使用Scanner类" class="headerlink" title="使用Scanner类"></a>使用Scanner类</h3><p>这可能是接受输入的最首选方法。Scanner 类的主要目的是使用<strong>正则表达式解析原始类型和字符串</strong>,但是,它也可用于在<strong>命令行中读取用户的输入</strong>。</p>
<p>从标记化输入中解析基元(nextInt()、nextFloat()、…)的便捷方法。</p>
<p>正则表达式可用于查找标记。</p>
<p>阅读方式不同步</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Scanner</span> <span class="variable">in</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line"> <span class="type">String</span> <span class="variable">s</span> <span class="operator">=</span> in.nextLine();</span><br><span class="line"> System.out.println(<span class="string">"You entered string "</span> + s);</span><br><span class="line"> <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> in.nextInt();</span><br><span class="line"> System.out.println(<span class="string">"You entered integer "</span> + a);</span><br><span class="line"> <span class="type">float</span> <span class="variable">b</span> <span class="operator">=</span> in.nextFloat();</span><br><span class="line"> System.out.println(<span class="string">"You entered float "</span> + b);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<h3 id="使用控制台类"><a href="#使用控制台类" class="headerlink" title="使用控制台类"></a>使用控制台类</h3><p>它已成为从命令行读取用户输入的首选方式。此外,它可以用于读取类似密码的输入,而不用回显用户输入的字符;也可以使用格式字符串语法(如 System.out.printf())。</p>
<p>优点:</p>
<p>读取密码而不回显输入的字符。</p>
<p>读取方法是同步的。</p>
<p>可以使用格式字符串语法。</p>
<p>**不适用于非交互环境(例如 IDE)**。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"><span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> System.console().readLine();</span><br><span class="line"> System.out.println(<span class="string">"You entered string "</span> + name);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>执行提示:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Exception in thread <span class="string">"main"</span> java.lang.NullPointerException</span><br><span class="line"> at com.lin.springlearn.service.PrimeNumber.main(Test.java:<span class="number">37</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="使用命令行参数"><a href="#使用命令行参数" class="headerlink" title="使用命令行参数"></a>使用命令行参数</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">public static void main(String[] args)</span><br><span class="line"> {</span><br><span class="line"> // check if length of args array is</span><br><span class="line"> // greater than 0</span><br><span class="line"> if (args.length > 0) {</span><br><span class="line"> System.out.println(</span><br><span class="line"> "The command line arguments are:");</span><br><span class="line"> // iterating the args array and printing</span><br><span class="line"> // the command line arguments</span><br><span class="line"> for (String val : args)</span><br><span class="line"> System.out.println(val);</span><br><span class="line"> }</span><br><span class="line"> else</span><br><span class="line"> System.out.println("No command line "</span><br><span class="line"> + "arguments found.");</span><br><span class="line"> }</span><br><span class="line"> </span><br></pre></td></tr></table></figure>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</article>
<article itemscope itemtype="http://schema.org/Article" class="post-block" lang="en">
<link itemprop="mainEntityOfPage" href="https://veysky.github.io/blogOfLin/2023/03/29/3%E6%9C%8829%E6%97%A5%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/blogOfLin/images/avatar.gif">
<meta itemprop="name" content="木林森">
<meta itemprop="description" content="personal blog">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="lin's blog home">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">
<a href="/blogOfLin/2023/03/29/3%E6%9C%8829%E6%97%A5%E9%9D%A2%E8%AF%95%E8%AE%B0%E5%BD%95/" class="post-title-link" itemprop="url">3月29日面试记录</a>
</h2>
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2023-03-29 20:06:51" itemprop="dateCreated datePublished" datetime="2023-03-29T20:06:51+08:00">2023-03-29</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">Edited on</span>
<time title="Modified: 2023-04-03 19:21:43" itemprop="dateModified" datetime="2023-04-03T19:21:43+08:00">2023-04-03</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">In</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/blogOfLin/categories/%E6%9D%82%E9%A1%B9/" itemprop="url" rel="index"><span itemprop="name">杂项</span></a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h2 id="面试过程:"><a href="#面试过程:" class="headerlink" title="面试过程:"></a>面试过程:</h2><ol>
<li><p>做题 7道 </p>
<ol>
<li>string reverse 字符串反转</li>
<li>从文件中读取最大长度word</li>
<li>sql查询 去重取id最小记录</li>
<li>验证ip</li>
<li>检测string中的重复字符</li>
<li>通过命令行输入表达式进行计算</li>
<li>检测一个数字是否基本类型 ?</li>
</ol>
</li>
<li><p>问题:</p>
<ol>
<li>如何使用@Controller 注解实现返回@RestController json结构</li>
<li>数据库连接池配置参数有那些</li>
<li>redis使用的数据接口有那些,以及大key使用zset结构场景有那些</li>
</ol>
</li>
</ol>
<h3 id="sql查询去重问题答案"><a href="#sql查询去重问题答案" class="headerlink" title="sql查询去重问题答案"></a>sql查询去重问题答案</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> a.id,a.user_icon <span class="keyword">from</span> user_info a <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> user_info b <span class="keyword">on</span> a.user_icon <span class="operator">=</span> b.user_icon <span class="keyword">and</span> a.id<span class="operator">!=</span>b.id <span class="keyword">WHERE</span> b.id <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">null</span> <span class="keyword">and</span> a.id<span class="operator"><</span>b.id</span><br><span class="line"><span class="keyword">union</span> <span class="keyword">all</span> </span><br><span class="line"><span class="keyword">select</span> a.id,a.user_icon <span class="keyword">from</span> user_info a <span class="keyword">where</span> a.user_icon <span class="keyword">not</span> <span class="keyword">in</span> (<span class="keyword">SELECT</span> user_icon <span class="keyword">from</span> user_info <span class="keyword">GROUP</span> <span class="keyword">BY</span> user_icon <span class="keyword">HAVING</span> <span class="built_in">count</span>(<span class="number">1</span>)<span class="operator">></span><span class="number">1</span>)</span><br></pre></td></tr></table></figure>
<h2 id="美的面试问题整理"><a href="#美的面试问题整理" class="headerlink" title="美的面试问题整理"></a>美的面试问题整理</h2><ol>
<li><p>spring的事务问题</p>
<ol>
<li>事务的隔离级别 5种: ISOLATION_DEFAULT 默认,使用后端数据库的默认隔离级别,Mysql的默认是可重复读</li>
<li>ISOLATION_READ_UNCOMMITTED 读未提交</li>
<li>ISOLATION_READ_COMMITTED 读已提交</li>
<li>ISOLATION_REPEATABLE_READ 可重复读</li>
<li>ISOLATION_SERIALIZABLE 串行化</li>
</ol>
<p>事务的传播机制,就是当多个事务同事存在的情况 Spring 如何处理这些事务的行为</p>
<ol>
<li>Required 默认,如果没有当前事务,就创建一个事务,如果存在一个事务,就加入到这个事务中</li>
<li>SUPPORTS 支持当前事务,如果没有当前事务,就以非事务方法执行</li>
<li>Mandatory 使用当前事务,如果没有当前事务,就抛出异常。 </li>