-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
2411 lines (2240 loc) · 473 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><![CDATA[2018年计划]]></title>
<url>/2018/01/03/2018%E5%B9%B4%E8%AE%A1%E5%88%92/</url>
<content type="html"><![CDATA[<a id="more"></a>
<p>转眼间已经2018年了,2017的计划如约又作废了。日子慢慢久了 自己好像适应了这种日子,上班下班睡觉玩游戏做做白日梦,有时想来简直想抽自己几巴掌,自己怎么又可能真的打呢^v^。好颓废的生活呀,如果继续这样的话慢慢就被社会所淘汰了。 人生的日子还有很久但不可以这样浪费,人生的日子很短要活出自己。2018年 新年新气象给自己下个约束鞭挞自己,给自己定个目标慢慢朝这个方向前进。</p>
<ol>
<li>腹中有书气自华,人丑就要多读书(网络小说除外,自己对这种网络小说读起来简直了)。 今天目标48本书籍,读书笔记24篇。</li>
<li>学历提升。本科多如狗,硕士博士满地走,天 要我这个小小的专科怎么活。人生压力好大/(ㄒoㄒ)/~~ 今年两次考试 8门或者7门考试</li>
<li>技术提升。OC目前看来是要慢慢被淘汰的,swift学起来。 如果可以的话尽量转向服务器开发。</li>
<li>身体健康。目前体重108斤,太瘦了 要变得strong。健身房这种药还是不能停的。今天增重20斤,尽可能是肌肉,肥肉的话可能会哭死。</li>
</ol>
<p>是时候来一波正能量了,人生数载总要有个目标往前奔,人生是一段接力赛没有人能凭空站在终点。</p>
<h3 id="每月打卡"><a href="#每月打卡" class="headerlink" title="每月打卡"></a>每月打卡</h3><p>为了让自己坚持下去,每月总结下对计划的执行状态;</p>
]]></content>
<categories>
<category> 个人计划 </category>
</categories>
<tags>
<tag> 个人计划 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[升级Node.js和npm]]></title>
<url>/2017/06/29/NodeJs%E5%92%8Cnpm%E5%8D%87%E7%BA%A7/</url>
<content type="html"><![CDATA[<h3 id="升级Node-js"><a href="#升级Node-js" class="headerlink" title="升级Node.js"></a>升级Node.js</h3><p>npm中有一个模块“n”,用来管理node.js版本;<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">n 显示已安装的Node版本</div><div class="line">n latest 安装最新版本Node</div><div class="line">n stable 安装最新稳定版Node</div><div class="line">n lts 安装最新长期维护版(lts)Node</div><div class="line">n <version> 根据提供的版本号安装Node</div></pre></td></tr></table></figure></p>
<p>更新命令<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">npm install -g n</div><div class="line">n stable</div></pre></td></tr></table></figure></p>
<p>最新版本用 <code>n latest</code> 也可以使用具体的版本号:<code>n v6.2.0</code></p>
<h3 id="升级npm"><a href="#升级npm" class="headerlink" title="升级npm"></a>升级npm</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install npm -g</div></pre></td></tr></table></figure>
]]></content>
</entry>
<entry>
<title><![CDATA[Java基础(十)]]></title>
<url>/2017/06/14/Java%E5%9F%BA%E7%A1%80-%E5%8D%81/</url>
<content type="html"><![CDATA[<h2 id="类加载机制与反射"><a href="#类加载机制与反射" class="headerlink" title="类加载机制与反射"></a>类加载机制与反射</h2><p>当程序主动使用某个类时,如果该类还未加载到内存中,系统会通过加载、连接、初始化三个步骤对该类进行初始化,JVM会连续完成这三个步骤,这三个步骤统称类加载或类初始化;</p>
<h3 id="类加载"><a href="#类加载" class="headerlink" title="类加载"></a>类加载</h3><p>类加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象。类的加载有类加载器完成,类加载器通常由JVM提供也称系统类加载器。除此之外还可以通过集成ClassLoader基类自定义类加载器。<br>通过使用不同的类加载器,可以从不同的来源加载类的二进制数据:</p>
<blockquote>
<ol>
<li>从本地文件系统加载class文件;</li>
<li>从JAR包加载class文件;</li>
<li>通过网络加载class文件;</li>
<li>把一个Java源文件动态编译,并执行加载;</li>
</ol>
</blockquote>
<h3 id="类连接的过程"><a href="#类连接的过程" class="headerlink" title="类连接的过程"></a>类连接的过程</h3><p>当类被加载后,系统产生对应的Class对象,接着进入连接阶段。连接阶段负责把类的二进制数据合并到JRE中。类连接可分为如下三个阶段:</p>
<blockquote>
<ol>
<li>验证阶段: 检验被加载的类是否有正确的内部结构,并和其他类协调一致;</li>
<li>准备阶段: 负责为类的类变量分配内存,并设置默认初始值;</li>
<li>解析阶段: 将类的二进制数据中的符号引用替换成直接引用;</li>
</ol>
</blockquote>
<h3 id="类初始化的过程"><a href="#类初始化的过程" class="headerlink" title="类初始化的过程"></a>类初始化的过程</h3><p>类初始化阶段,虚拟机负责对类进行初始化,主要是对<font color="red">类变量</font>进行初始化。Java类中类变量指定初始值有两种:声明类变量指定初始值;使用静态初始化块为类变量进行初始化;<br>JVM初始化的步骤:</p>
<blockquote>
<ol>
<li>假如这个类还没有被加载和连接,则程序先加载并连接该类</li>
<li>加入该类的直接父类没有被初始化,则先初始化其直接父类</li>
<li>加入类中有初始化语句,则系统一次执行这些初始化语句</li>
</ol>
</blockquote>
<p>当Java程序首次通过下面几种方式使用某个类或者接口,系统就会初始化该类或接口:</p>
<blockquote>
<ol>
<li>创建类的实例,包括使用new操作符、通过反射创建实例,通过反序列化创建实例</li>
<li>调用某个类的类方法</li>
<li>访问或赋值某个类或接口的类变量,</li>
<li>使用反射方式强制创建某个类或接口对应的java.lang.Class对象</li>
<li>初始化某个类的子类。初始化子类,会先初始化父类,一直到Object类</li>
<li>直接使用java.exe命令运行某个主类</li>
</ol>
</blockquote>
<p>对于一个final型的类变量,如果该类变量的值在编译时可以确定,Java编译器会在编译时把使用这个类变量替换成相应的值,因此程序使用该静态类变量,并不会导致类的初始化;</p>
<h3 id="类加载器以及实现机制"><a href="#类加载器以及实现机制" class="headerlink" title="类加载器以及实现机制"></a>类加载器以及实现机制</h3><p>类加载器负责加载所有的类,一旦一个类被载入JVM中,同一个类就不会被再次载入。在JVM中,一个类的全限定类名(包名和类名)和类加载器作为唯一标示。<br>当JVM启动时,会形成由三个类加载器组成的初始类加载层次结构:</p>
<blockquote>
<ol>
<li>Bootstrap ClassLoader: 根类加载器,负责加载Java的核心类,根类加载器不是java.lang.ClassLoader的子类,是由JVM自身实现;</li>
<li>Extension ClassLoader: 扩展类加载器,负责加载JRE的扩展目录中JAR包的类</li>
<li>System ClassLoader: 系统类加载器,负责在JVM启动时加载来自java命令的-classpath选项、java.class.path系统属性或CLASSPATH环境变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器</li>
</ol>
</blockquote>
<p>JVM的类加载机制主要有如下三种:</p>
<blockquote>
<ol>
<li>全盘负责: 当一个类加载器加载某个Class时,该Class所依赖和引用的其他Class也是由该类加载器负责再付,除非显式使用另外一个类加载加载;</li>
<li>父类委托: 先让parent(父)类加载器视图加载该Class,只有父类加载器无法加载该类时,才尝试从自己的类路径加载;</li>
<li>缓存机制: 已经加载过的Class都会被缓存,当程序需要使用某个Class时,先从缓存区查找,查找不到,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区</li>
</ol>
</blockquote>
<p>除了Java提供的类加载器之外,也可以通过集成ClassLoader自定义类加载器.JVM中这4种类加载器的层次结构:<br><img src="http://opbae2xuz.bkt.clouddn.com/images/bhlin/2017-06-14-01.png" alt=""><br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ClassLoaderPropTest</span> </span>{</div><div class="line"></div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> ClassLoader systemLoader = ClassLoader.getSystemClassLoader();</div><div class="line"> System.out.println(<span class="string">"系统类加载器:"</span> + systemLoader);</div><div class="line"> Enumeration<URL> eml = systemLoader.getResources(<span class="string">""</span>);</div><div class="line"> <span class="keyword">while</span> (eml.hasMoreElements()) {</div><div class="line"> System.out.println(eml.nextElement());</div><div class="line"> }</div><div class="line"> ClassLoader extendsionLader = systemLoader.getParent();</div><div class="line"> System.out.println(<span class="string">"扩展类加载器: "</span> + extendsionLader);</div><div class="line"> System.out.println(<span class="string">"扩展类加载的加载路径: "</span> + System.getProperty(<span class="string">"java.ext.dirs"</span>));</div><div class="line"> System.out.println(<span class="string">"扩展类加载器的parent: "</span> + extendsionLader.getParent());</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>类加载器加载Class大致要经过如下8个步骤:</p>
<blockquote>
<ol>
<li>检测Class缓存区是否有此Class,有则进入第8步</li>
<li>如果父类加载器不存在,则进入第4步</li>
<li>使用父类加载器去载入目标,成功载入进入第8步,否则进入第5步</li>
<li>使用根类加载器载入目标类,成功载入进入第8步,否则进入第7步</li>
<li>当前类加载器尝试寻找Class文件,找到进入第6步,否则进入第8步</li>
<li>从文件中载入Class,成功载入后跳转第8步</li>
<li>抛出ClassNotFoundException异常</li>
<li>返回对应的java.lang.Class对象</li>
</ol>
</blockquote>
<h3 id="继承ClassLoader实现自定义类加载器"><a href="#继承ClassLoader实现自定义类加载器" class="headerlink" title="继承ClassLoader实现自定义类加载器"></a>继承ClassLoader实现自定义类加载器</h3><p>JVM中除了跟类加载器之外,所有的类加载器都是ClassLoader子类的实例。ClassLoader类有如下两个关键方法:</p>
<blockquote>
<ol>
<li>loadClass(String name, boolean resolve): 该方法为ClassLoader的入口点,根据指定名称来加载类,系统就是调用ClassLoader的该方法来获取指定类对应的Class对象。</li>
<li>findClass(String name): 根据指定名称来查找类</li>
</ol>
</blockquote>
<p>如果自定义ClassLoader,则可以重写以上两个方法实现,通常推荐重写findClass()方法,而不是loadClass()方法。loadClass()方法的执行步骤如下:</p>
<blockquote>
<ol>
<li>用findLoadedClass(string)检查是否已经加载类,如果已经加载直接返回</li>
<li>在父类加载器调用loadClass()方法。如果父类加载器为null,则使用根类加载器加载</li>
<li>调用finClass(String)方法查找类</li>
</ol>
</blockquote>
<p>重写findClass()方法避免覆盖默认类加载器的父类委托、缓冲机制两种策略;<br>CalssLoader还有一个核心方法,Class defineClass(String name, byte[] b, int off, int len), 该方法将指定类的字节码文件读入字节数组b内,并将它转换成Class对象,该字节码文件可以来源于文件、网络等<br>ClassLoader还包含一些普通方法:</p>
<blockquote>
<ol>
<li>findSystemClass(String name):从本地文件系统装入文件</li>
<li>static getSystemClassLoader(): 返回系统类加载器</li>
<li>getParent(): 返回类加载器的父类加载器</li>
<li>resolveClass(Class<?> c): 链接指定的类。类加载器可以使用此方法链接类c</li>
<li>findLoadedCladd(String name): 如果Java虚拟机已经加载名为name的类,则直接返回该类对应的Class实例,否则返回null;<h3 id="使用URLClassLoader"><a href="#使用URLClassLoader" class="headerlink" title="使用URLClassLoader"></a>使用URLClassLoader</h3>Java为ClassLoader提供一个URLClassLoader实现类,该类也是系统类加载器和扩展类加载器的父类。URLClassLoader可以从本地文件系统获取二进制文件加载类,也可以从远程主机获取二进制文件加载类。<br>URLClassLoader类提供两个构造器:</li>
<li>URLClassLoader(URL[] urls): 使用默认父类加载器创建一个实例,该实例将从urls所指定的系列路径来查询并加载类</li>
<li>URLClassLoader(URL[] urls, ClassLoader parent): 使用指定的父类加载器创建一个ClassLoader,功能和上一个一样</li>
</ol>
</blockquote>
<h3 id="使用Class对象"><a href="#使用Class对象" class="headerlink" title="使用Class对象"></a>使用Class对象</h3><p>Java程序获得Class对象通常由如下三种方式:</p>
<blockquote>
<ol>
<li>使用Class类的forName(String clazzName)静态方法。clazzName是某个类的全限定名(包名+类名)</li>
<li>调用某个类的class属性获得该类对应的Class对象。</li>
<li>调用某个对象的getClass()方法。该方法是由java.lang.Object类的方法</li>
</ol>
</blockquote>
<p>下面4个方法获取Class对应类的构造器:</p>
<blockquote>
<ol>
<li>Connstrucotr<t> getConstructor(Class<?>… parameterTypes): 返回Class对象类的、带指定形参列表的public构造器</t></li>
<li>Connstrucotr<?>[] getConstructor(): 返回Class对象所有public构造器</li>
<li>Connstrucotr<t> getDeclaredConstructor(Class<?>… parameterTypes): 返回Class对象对应类的带指定形参列表的构造器,与构造器访问权限无关</t></li>
<li>Connstrucotr<?>[] getDeclaredConstructor(): 返回Class对象所有的构造器,与构造器访问权限无关</li>
</ol>
</blockquote>
<p>下面4个方法获取Class对应类的方法:</p>
<blockquote>
<ol>
<li>Method getMethod(String name, Class<?>… paramterTypes): 返回Class对应类的,带指定形参的public方法</li>
<li>Method[] getMethods(): 返回Class对应类的所有public方法</li>
<li>Method getDeclaredMethod(String name, Class<?>… paramterTypes): 返回Class对应类的,带指定形参的方法,与方法的访问权限无关</li>
<li>Method[] getDeclaredMethods(): 返回Class对应类的所有方法,与方法的访问权限无关</li>
</ol>
</blockquote>
<p>下面4个方法用于访问Class对应类的成员变量:</p>
<blockquote>
<ol>
<li>Field getField(String name): 返回Class对应类的、指定名称的public成员变量</li>
<li>Field[] getFields(): 返回Class对应类的、所有的public成员变量</li>
<li>Field getDeclaredField(String name): 返回Class对应类的、指定名称的成员变量,与成员变量的访问权限无关</li>
<li>Field[] getDeclaredFields(): 返回Class对应类的、所有的成员变量,与成员变量的访问权限无关</li>
</ol>
</blockquote>
<p>如下几个方法用于访问Class对应类的Annotation:</p>
<blockquote>
<ol>
<li><a extends="" annotation="">A getAnnotation(Class<a> annotationClass): 获取Class对应类存在的、指定类型的Annotation; 不存在返回null</a></a></li>
<li><a extends="" annotation="">A getDeclaredAnnotation(Class<a> annotationClass): Java8新增方法,获取直接修饰该Class对应类的、指定类型的Annotation;</a></a></li>
<li>Annotation[] getAnnotations(): 返回修饰该Class对应类上所有Annotation</li>
<li>Annotation[] getDeclaredAnnotations(): 返回直接修饰该Class对应类上所有Annotation</li>
<li><a extends="" annotation=""> A[] getDeclaredAnnotationByType(Class<a> annotationClass): 该方法与前面getAnnotation()类似,Java8增加重复注解功能,需要使用该方法直接修饰该类、指定类型的Annotation</a></a></li>
</ol>
</blockquote>
<p>如下方法用于访问Class对应类包含的内部类:</p>
<blockquote>
<ol>
<li>Class<?>[] getDeclaredClasses(): 返回该Class对应类包含的全部内部类</li>
</ol>
</blockquote>
<p>如下方法用于访问Class对应类所在的外部类:</p>
<blockquote>
<ol>
<li>Class<?> getDeclaringClass(): 返回Class对应类所在的外部类</li>
</ol>
</blockquote>
<p>如下方法用于访问Class对应类所实现的接口:</p>
<blockquote>
<ol>
<li>Class<?>[] getInterfaces(): 返回Class对象对应类所实现的全部接口</li>
</ol>
</blockquote>
<p>如下方法用于访问该Class对应类所继承的父类</p>
<blockquote>
<ol>
<li>Class<? super T> getSuperclass(): 返回Class对应类的父类Class对象</li>
</ol>
</blockquote>
<p>如下方法用于获取Class对应类的修饰符、所在包、类名等基本信息:</p>
<blockquote>
<ol>
<li>int getModifiers(): 返回此类后接口的所有修饰符</li>
<li>Package getPackage(): 获取此类的包</li>
<li>String getName(): 返回Class对象所表示类的名称</li>
<li>String getSimpleName(): 返回Class对象所表示类的简称</li>
</ol>
</blockquote>
<p>除此之外Class对象还可调用如下几个判断方法来判断是否为接口、枚举、注解类型等:</p>
<blockquote>
<ol>
<li>boolean isAnnotation(): 返回此Class对象是否表示一个注解类型</li>
<li>boolean isAnnotationPresent(Class<? extends Annotation> annotationClass): 判断此Class对象是否使用了Annotation修饰</li>
<li>boolean isAnonymousClass(): Class对象是否是一个匿名类</li>
<li>boolean isArray(): Class对象是否表示一个数组类</li>
<li>boolean isEnum(): Class对象是否表示一个枚举</li>
<li>boolean isInterface(): Class对象是否表示一个接口</li>
<li>boolean isInstance(Object obj): 判断obj是否是此Class对象的实例,该方法完全可代替instanceof操作符</li>
</ol>
</blockquote>
<h3 id="Java8新增的方法参数反射"><a href="#Java8新增的方法参数反射" class="headerlink" title="Java8新增的方法参数反射"></a>Java8新增的方法参数反射</h3><p>Java8在java.lang.reflect包下新增Executable抽象基类,该类派生Constructor、Method两个子类。<br>Executable提供如下方法获取方法或参数的形参个数及形参名:</p>
<blockquote>
<ol>
<li>int getParameterCount(): 获取该构造器或方法的形参个数</li>
<li>Parameter[] getParameters(): 获取该构造器或方法的所有形参</li>
</ol>
</blockquote>
<p>parameter是Java8新增的API,提供大量方法获取声明该参数的泛型信息,还提供如下方法获取参数信息:</p>
<blockquote>
<ol>
<li>getModifiers(): 获取修饰该形参的修饰符</li>
<li>String getName(): 获取形参名</li>
<li>Type getParameterizedType(): 获取带泛型的形参类型</li>
<li>Class<?> getType(): 获取形参类型</li>
<li>boolean isNamePresent(): 该方法返回该类的class文件中是否包含了方法的形参名信息</li>
<li>boolean isVarArgs(): 该方法用于判断该参数是否为个数可变的形参;<h3 id="动态创建Java对象"><a href="#动态创建Java对象" class="headerlink" title="动态创建Java对象"></a>动态创建Java对象</h3>通过反射生成对应有如下两种方式:</li>
<li>使用Class对象的newInstance()方法创建实例,这种方式要求对应类有默认构造器,执行newInstance()利用默认构造器创建实例</li>
<li>先使用Class对象获取指定Constructor对象,在调用Constructor对象的newInstance()方法创建实例。这种方式可以选择指定构造器创建实例</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//第一种方式</span></div><div class="line">Class<?> clazz = Class.forName(<span class="string">"java.util.Date"</span>);</div><div class="line">Object obj = clazz.newInstance();</div><div class="line"></div><div class="line"><span class="comment">//第二种方式</span></div><div class="line">Class<?> jframeClazz = Class.forName(<span class="string">"javax.swing.JFrame"</span>);</div><div class="line">Constructor ctor = jframeClazz.getConstructor(String.class);</div><div class="line">Object obj = ctor.newInstance(<span class="string">"测试窗口"</span>);</div></pre></td></tr></table></figure>
<h3 id="动态调用方法"><a href="#动态调用方法" class="headerlink" title="动态调用方法"></a>动态调用方法</h3><p>通过Class对象的getMethod()方法获得Method对象。Method对象包含一个invoke()方法,该方法签名如下:</p>
<blockquote>
<ol>
<li>Object invoke(Object obj, Object… args): 该方法的obj是执行该方法的主调,args是执行该方法传入方法的实参</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//获得StringBuffer的Class对象</span></div><div class="line">Class<?> clazz = Class.forName(<span class="string">"java.lang.StringBuffer"</span>);</div><div class="line"><span class="comment">//获得StringBuffer带有String的构造器</span></div><div class="line">Constructor cotr = clazz.getConstructor(String.class);</div><div class="line"><span class="comment">//创建StringBuffer对象</span></div><div class="line">Object obj = cotr.newInstance(<span class="string">"abcdefg"</span>);</div><div class="line"><span class="comment">//获得StringBuffer的append()方法</span></div><div class="line">Method method = clazz.getMethod(<span class="string">"append"</span>, String.class);</div><div class="line"><span class="comment">//调用StringBuffer的append()方法</span></div><div class="line">method.invoke(obj, <span class="string">"1234"</span>);</div><div class="line">System.out.println(obj.toString());</div></pre></td></tr></table></figure>
<h3 id="访问并修改Java对象的属性值"><a href="#访问并修改Java对象的属性值" class="headerlink" title="访问并修改Java对象的属性值"></a>访问并修改Java对象的属性值</h3><p>通过Class对象提供的获取Field方法获取成员变量。Field提供如下两组方法读取或设置成员变量。</p>
<blockquote>
<ol>
<li>getXxx(Object obj): 获取obj对象的成员变量的值。Xxx对应8中基本类型,如果是引用类型,则取消Xxx</li>
<li>setXxx(Object obj, Xxx val): 设置obj对象的成员变量的val值。Xxx对应8中基本类型,如果是引用类型,则取消Xxx</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span> </span>{</div><div class="line"> <span class="keyword">private</span> String name;</div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> age;</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="string">"Person[name:"</span> + name + <span class="string">", age:"</span> + age + <span class="string">"]"</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">Person p = <span class="keyword">new</span> Person();</div><div class="line">Class<Person> personClazz = Person.class;</div><div class="line">Field nameField = personClazz.getDeclaredField(<span class="string">"name"</span>);</div><div class="line">nameField.setAccessible(<span class="keyword">true</span>);</div><div class="line">nameField.set(p, <span class="string">"张三"</span>);</div><div class="line">Field ageField = personClazz.getDeclaredField(<span class="string">"age"</span>);</div><div class="line">ageField.setAccessible(<span class="keyword">true</span>);</div><div class="line">ageField.setInt(p, <span class="number">18</span>);</div><div class="line">System.out.println(p);</div></pre></td></tr></table></figure>
<h3 id="使用反射操作数组"><a href="#使用反射操作数组" class="headerlink" title="使用反射操作数组"></a>使用反射操作数组</h3><p>java.lang.reflect包下还提供了Array类,Array对象代表数组。程序通过使用Array动态创建数组,操作数组元素。Array提供如下几类方法:</p>
<blockquote>
<ol>
<li>static Object newInstance(Class<?> componentType, int… length): 创建一个具有指定元素类型、指定维度的数组</li>
<li>static xxx getXxx(Object array, int index): 返回array数组中的第index个元素。Xxx对应8中基本类型,如果是引用类型,则取消Xxx</li>
<li>static xxx setXxx(Object array, int index, xxx val): 将array数组中第index个元素设置值为val。Xxx对应8中基本类型,如果是引用类型,则取消Xxx</li>
</ol>
</blockquote>
<h3 id="使用Proxy和InvocationHandler创建动态代理"><a href="#使用Proxy和InvocationHandler创建动态代理" class="headerlink" title="使用Proxy和InvocationHandler创建动态代理"></a>使用Proxy和InvocationHandler创建动态代理</h3><p>Proxy提供了用于创建动态代理类和代理对象的静态方法,它也是所有动态代理类的父类。如果在程序中为一个或多个接口动态生成实现类,可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态创建实例,也可以使用Proxy创建动态代理实例;<br>Proxy提供了如下两个方法创建动态代理类和动态代理实例:</p>
<blockquote>
<ol>
<li>static Class<?> getProxyClass(ClassLoader loader, Class<?>… interfaces): 创建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口。</li>
<li>static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, Invocationhandler h): 直接创建一个动态代理对象,该代理对象实现类实现了interfaces指定的接口,执行代理对象的每个方法时都会被替换成InvocationHandler对象的invoke方法;</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line">InvocationHandler handler = <span class="keyword">new</span> MyInvokationHandler();</div><div class="line">Class<?> proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class);</div><div class="line">Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);</div><div class="line">Person p = (Person) constructor.newInstance(handler);</div><div class="line">p.walk();</div><div class="line">p.sayHello(<span class="string">"孙悟空"</span>);</div><div class="line"></div><div class="line"><span class="comment">//上面代码可以替换成如下:</span></div><div class="line">InvocationHandler handler = <span class="keyword">new</span> MyInvokationHandler();</div><div class="line">Person p = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), <span class="keyword">new</span> Class[]{Person</div><div class="line"> .class}, handler);</div><div class="line">p.walk();</div><div class="line">p.sayHello(<span class="string">"孙悟空"</span>);</div><div class="line"></div><div class="line"><span class="comment">//完整代码</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyInvokationHandler</span> <span class="keyword">implements</span> <span class="title">InvocationHandler</span> </span>{</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> Object <span class="title">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable </span>{</div><div class="line"> System.out.println(<span class="string">"---正在执行的方法:"</span> + method);</div><div class="line"> <span class="keyword">if</span> (args != <span class="keyword">null</span>) {</div><div class="line"> System.out.println(<span class="string">"下面是执行该方法是传入的实参:"</span>);</div><div class="line"> <span class="keyword">for</span> (Object val : args) {</div><div class="line"> System.out.println(val);</div><div class="line"> }</div><div class="line"> }<span class="keyword">else</span> {</div><div class="line"> System.out.println(<span class="string">"调用方法没有实参"</span>);</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">ProxyTest</span> </span>{</div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> InvocationHandler handler = <span class="keyword">new</span> MyInvokationHandler();</div><div class="line"> Person p = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), <span class="keyword">new</span> Class[]{Person</div><div class="line"> .class}, handler);</div><div class="line"> p.walk();</div><div class="line"> p.sayHello(<span class="string">"孙悟空"</span>);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<!--### Class类的泛型-->
<!--### 通过反射获取泛型类型-->
]]></content>
<categories>
<category> Java </category>
</categories>
<tags>
<tag> Java </tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java基础(九)]]></title>
<url>/2017/06/12/Java%E5%9F%BA%E7%A1%80-%E4%B9%9D/</url>
<content type="html"><![CDATA[<h2 id="网络编程"><a href="#网络编程" class="headerlink" title="网络编程"></a>网络编程</h2><p>通过Java网络通信的支持,Java程序可以方便的访问互联网上的HTTP服务、FTP服务等,并可以直接获取互联网的远程资源,还可以像远程资源发送GET、POST请求。</p>
<h3 id="使用InetAddress包装IP地址"><a href="#使用InetAddress包装IP地址" class="headerlink" title="使用InetAddress包装IP地址"></a>使用InetAddress包装IP地址</h3><p>Java提供了InetAddress类代表IP地址,InetAddress有两个子类:Inet4Address、Inet6Address分别代表IPv4和IPv6。<br>InetAddress没有提供构造器,提供了两个静态方法获取InetAddress实例:</p>
<blockquote>
<ol>
<li>getByName(String host): 根据主机获取对应的InetAddress对象;</li>
<li>getByAddress(byte[] addr): 根据原始IP地址获取对应的InetAddress对象;</li>
</ol>
</blockquote>
<p>InetAddress提供如下三个方法获取InetAddress实例对应的IP地址和主机名:</p>
<blockquote>
<ol>
<li>String getCanonicalHostName(): 获取此IP地址的全限定域名;</li>
<li>String getHostAddress(): 返回该InetAddress实例对应的IP对应的IP字符串;</li>
<li>String getHostName(): 获取IP地址的主机名;</li>
</ol>
</blockquote>
<p>InetAddress还提供getLocalHost()方法获取本机IP地址对应的InetAddress实例;还提供一个用于测试是否可以到达该地址方法:isReachable();<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">InetAddressTest</span> </span>{</div><div class="line"></div><div 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 class="keyword">throws</span> Exception</span>{</div><div class="line"> <span class="comment">//根据主机名获取对应的InetAddress实例</span></div><div class="line"> InetAddress ip = InetAddress.getByName(<span class="string">"www.baidu.com"</span>);</div><div class="line"> <span class="comment">//判断是否能到达</span></div><div class="line"> System.out.println(<span class="string">"百度是否可达:"</span> + ip.isReachable(<span class="number">2000</span>));</div><div class="line"> <span class="comment">//获取改InetAddress实例的ip</span></div><div class="line"> System.out.println(<span class="string">"百度的IP:"</span> + ip.getHostAddress());</div><div class="line"> <span class="comment">//根据原始IP地址获取对应的InetAddress实例</span></div><div class="line"> InetAddress local = InetAddress.getByAddress(<span class="keyword">new</span> <span class="keyword">byte</span>[]{<span class="number">127</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>});</div><div class="line"> System.out.println(<span class="string">"本机是否可达: "</span> + local.isReachable(<span class="number">2000</span>));</div><div class="line"> <span class="comment">//获取实例对应的全限定域名</span></div><div class="line"> System.out.println(local.getCanonicalHostName());</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h3 id="使用URLEncoder和URLDecoder工具类"><a href="#使用URLEncoder和URLDecoder工具类" class="headerlink" title="使用URLEncoder和URLDecoder工具类"></a>使用URLEncoder和URLDecoder工具类</h3><p>URLEncoder类包含一个decode(String s, String enc)静态方法,用于将application/x-www-from-urlencoded MIME字符串转换成普通字符串;URLDecoder类包含一个encode(String s, String enc)静态方法,用于将普通字符串转换成application/x-www-from-urlencoded MIME字符串。通俗来讲就是将浏览器地址栏中中文乱码互相转换。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">URLDecoderTest</span> </span>{</div><div class="line"></div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> String keyWord = URLDecoder.decode(<span class="string">"%e5%93%88%e5%93%88%e5%b0%8f%e9%80%97%e6%af%94"</span>, <span class="string">"utf-8"</span>);</div><div class="line"> System.out.println(keyWord);</div><div class="line"> String urlStr = URLEncoder.encode(<span class="string">"哈哈哈"</span>, <span class="string">"utf-8"</span>);</div><div class="line"> System.out.println(urlStr);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h3 id="使用URLConnection访问远程资源"><a href="#使用URLConnection访问远程资源" class="headerlink" title="使用URLConnection访问远程资源"></a>使用URLConnection访问远程资源</h3><p>URL类用于创建URL对象,URL是有协议名、主机、端口和资源组成,即下面格式:</p>
<blockquote>
<p>protocol://host:port/resourceName</p>
</blockquote>
<p>URL可以调用如下方法访问URL对应的资源:</p>
<blockquote>
<ol>
<li>String getFile(): 获取URL资源名</li>
<li>String getHost(): 获取URL主机名</li>
<li>String getPath(): 获取URL路径部分</li>
<li>int getPort(): 获取URL端口号</li>
<li>String getProtocol(): 获取URL协议名称</li>
<li>String getQuery(): 获取URL查询字符串部分</li>
<li>URLConnection openConnection(): 返回一个URLConnection对象,它代表URL所引用远程对象的连接</li>
<li>InputStream openStream(): 打开与此URL的连接,并返回一个用于读取该URL资源的InputStream</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DownUtil</span> </span>{</div><div class="line"> <span class="comment">//下载资源路径</span></div><div class="line"> <span class="keyword">private</span> String path;</div><div class="line"> <span class="comment">//下载文件保存位置</span></div><div class="line"> <span class="keyword">private</span> String targetFile;</div><div class="line"> <span class="comment">//线程个数</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> threadNum;</div><div class="line"> <span class="comment">//下载的线程对象数组</span></div><div class="line"> <span class="keyword">private</span> DownThread[] threads;</div><div class="line"> <span class="comment">//文件大小</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> fileSize;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">DownUtil</span><span class="params">(String path, String targetFile, <span class="keyword">int</span> threadNum)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.path = path;</div><div class="line"> <span class="keyword">this</span>.targetFile = targetFile;</div><div class="line"> <span class="keyword">this</span>.threadNum = threadNum;</div><div class="line"> <span class="comment">//初始化threads数组</span></div><div class="line"> <span class="keyword">this</span>.threads = <span class="keyword">new</span> DownThread[threadNum];</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">download</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> URL url = <span class="keyword">new</span> URL(path);</div><div class="line"> HttpURLConnection conn = (HttpURLConnection) url.openConnection();</div><div class="line"> conn.setConnectTimeout(<span class="number">5</span> * <span class="number">1000</span>);</div><div class="line"> conn.setRequestMethod(<span class="string">"GET"</span>);</div><div class="line"> conn.setRequestProperty(<span class="string">"Accept"</span>, <span class="string">"image/gif, image/jpeg, image/pjpeg, image/pjpeg,"</span> +</div><div class="line"> <span class="string">"application/x-shockwave-flash, application/xmal+xml,"</span> +</div><div class="line"> <span class="string">"application/vnd.ms-xpsdocument, application/x-ms-xbap,"</span> +</div><div class="line"> <span class="string">"application/x-ms-application, application/vnd.ms-excel,"</span> +</div><div class="line"> <span class="string">"application/vnd.ms-powerpoint, application/msword, */*"</span>);</div><div class="line"> conn.setRequestProperty(<span class="string">"Accept-Language"</span>, <span class="string">"zh-CN"</span>);</div><div class="line"> conn.setRequestProperty(<span class="string">"Charset"</span>, <span class="string">"UTF-8"</span>);</div><div class="line"> conn.setRequestProperty(<span class="string">"Connection"</span>, <span class="string">"Keep-Alive"</span>);</div><div class="line"> <span class="comment">//得到文件大小</span></div><div class="line"> fileSize = conn.getContentLength();</div><div class="line"> conn.disconnect();</div><div class="line"> <span class="keyword">int</span> currentPartSize = fileSize / threadNum + <span class="number">1</span>;</div><div class="line"> RandomAccessFile file = <span class="keyword">new</span> RandomAccessFile(targetFile, <span class="string">"rw"</span>);</div><div class="line"> <span class="comment">//设置本地文件大小</span></div><div class="line"> file.setLength(fileSize);</div><div class="line"> <span class="comment">//关闭文件</span></div><div class="line"> file.close();</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < threadNum; i++) {</div><div class="line"> <span class="comment">//计算每个线程下载的位置</span></div><div class="line"> <span class="keyword">int</span> startPos = i * currentPartSize;</div><div class="line"> RandomAccessFile currentPart = <span class="keyword">new</span> RandomAccessFile(targetFile, <span class="string">"rw"</span>);</div><div class="line"> <span class="comment">//定位该线程现在的位置</span></div><div class="line"> currentPart.seek(startPos);</div><div class="line"> <span class="comment">//创建下载线程</span></div><div class="line"> threads[i] = <span class="keyword">new</span> DownThread(startPos, currentPartSize, currentPart);</div><div class="line"> threads[i].start();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="comment">//获取下载的完成百分比</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">getCompleteRate</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">int</span> sumSize = <span class="number">0</span>;</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < threadNum; i++) {</div><div class="line"> sumSize += threads[i].length;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> sumSize * <span class="number">1.0</span> / fileSize;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">DownThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</div><div class="line"> <span class="comment">//当前线程的下载位置</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> startPos;</div><div class="line"> <span class="comment">//当前线程负责下载的文件大小</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> currentPartSzie;</div><div class="line"> <span class="comment">//当前线程需要下载的文件快</span></div><div class="line"> <span class="keyword">private</span> RandomAccessFile currentPart;</div><div class="line"> <span class="comment">//定义线程已下载的字节数</span></div><div class="line"> <span class="keyword">public</span> <span class="keyword">int</span> length;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">DownThread</span><span class="params">(<span class="keyword">int</span> startPos, <span class="keyword">int</span> currentPartSzie, RandomAccessFile currentPart)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.startPos = startPos;</div><div class="line"> <span class="keyword">this</span>.currentPart = currentPart;</div><div class="line"> <span class="keyword">this</span>.currentPartSzie = currentPartSzie;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> URL url = <span class="keyword">new</span> URL(path);</div><div class="line"> HttpURLConnection conn = (HttpURLConnection) url.openConnection();</div><div class="line"> conn.setConnectTimeout(<span class="number">5</span> * <span class="number">1000</span>);</div><div class="line"> conn.setRequestProperty(<span class="string">"Accept"</span>, <span class="string">"image/gif, image/jpeg, image/pjpeg, image/pjpeg,"</span> +</div><div class="line"> <span class="string">"application/x-shockwave-flash, application/xmal+xml,"</span> +</div><div class="line"> <span class="string">"application/vnd.ms-xpsdocument, application/x-ms-xbap,"</span> +</div><div class="line"> <span class="string">"application/x-ms-application, application/vnd.ms-excel,"</span> +</div><div class="line"> <span class="string">"application/vnd.ms-powerpoint, application/msword, */*"</span>);</div><div class="line"> conn.setRequestProperty(<span class="string">"Accept-Language"</span>, <span class="string">"zh-CN"</span>);</div><div class="line"> conn.setRequestProperty(<span class="string">"Charset"</span>, <span class="string">"UTF-8"</span>);</div><div class="line"> InputStream inStream = url.openStream();</div><div class="line"> <span class="comment">//跳过startPos个字节,表明该线程只下载自己负责的那部分</span></div><div class="line"> inStream.skip(<span class="keyword">this</span>.startPos);</div><div class="line"> <span class="keyword">byte</span>[] buffer = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">1024</span>];</div><div class="line"> <span class="keyword">int</span> hasRead = <span class="number">0</span>;</div><div class="line"> <span class="keyword">while</span> (length < currentPartSzie && (hasRead = inStream.read(buffer)) != -<span class="number">1</span>) {</div><div class="line"> currentPart.write(buffer, <span class="number">0</span>, hasRead);</div><div class="line"> length += hasRead;</div><div class="line"> }</div><div class="line"> currentPart.close();</div><div class="line"> inStream.close();</div><div class="line"> } <span class="keyword">catch</span> (MalformedURLException e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> } <span class="keyword">catch</span> (IOException e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"></div><div 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[] age)</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> String path = <span class="string">"https://timgsa.baidu"</span> +</div><div class="line"> <span class="string">".com/timg?image&quality=80&size=b9999_10000&sec=1497329776318&di=a95d10efa28b7010991b7de6d892c43a&imgtype=0&src=http%3A%2F%2Fandroid-wallpapers.25pp.com%2Ffs01%2F2014%2F09%2F30%2F0_3675c5ca51a2f5fec8f756217ea66dd6_900x675.jpg"</span>;</div><div class="line"> DownUtil downUtil = <span class="keyword">new</span> DownUtil(path, <span class="string">"ios-bhlin.jpg"</span>, <span class="number">4</span>);</div><div class="line"></div><div class="line"> downUtil.download();</div><div class="line"></div><div class="line"> <span class="keyword">new</span> Thread(()->{</div><div class="line"> <span class="keyword">while</span> (downUtil.getCompleteRate() < <span class="number">1</span>) {</div><div class="line"> System.out.println(<span class="string">"已完成: "</span> + downUtil.getCompleteRate());</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> Thread.sleep(<span class="number">100</span>);</div><div class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }).start();</div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="TCP协议基础"><a href="#TCP协议基础" class="headerlink" title="TCP协议基础"></a>TCP协议基础</h3><p>TCP协议被称作是一种端对端的协议。当一台计算机需要与另一台计算机连接是,TCP协议会让它们建立连接:用于发送和接受数据的虚拟链路。在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接 时它们可以释放这个连接,连接的建立是需要三次握手的,而释放则需要4次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的;</p>
<h3 id="使用ServerSocket和Socket"><a href="#使用ServerSocket和Socket" class="headerlink" title="使用ServerSocket和Socket"></a>使用ServerSocket和Socket</h3><p>Java中能接受其他通信实体请求的类是ServerSocket,ServerSocket对象用于监听来自客户端Socket的连接,如果没有连接,他将一直处于等待状态。ServerSocket包含一个监听来自客户端连接请求的方法:</p>
<blockquote>
<ol>
<li>Socket accept(): 如果接收到客户端的Socket连接请求,该方法返回一个与客户端Socket对应的Socket;否则该方法会一直处于阻塞线程的状态</li>
<li>ServerSocket(int port): 用指定端口port创建ServerSocket,该端口值0~65535</li>
<li>ServerSocket(int port, int backlog): 增加一个用来改变连接队列长度的参数backlog</li>
<li>ServerSocket(int port, int backlog, InetAddress localAddr): 在机器存在多个IP的情况下,语序通过localAddr参数指定将ServerSocket绑定到指定的IP地址</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">ServerSocket ss= <span class="keyword">new</span> ServerSocket(<span class="number">30000</span>);</div><div class="line"><span class="keyword">while</span>(<span class="keyword">true</span>) {</div><div class="line"> Socket s = ss.accept();</div><div class="line"> <span class="comment">//下面就可以使用Socket进行通信了</span></div><div class="line"> ...</div><div class="line">}</div></pre></td></tr></table></figure>
<p>客户端通常使用Socket的构造器指定服务器:</p>
<blockquote>
<ol>
<li>Socket(InetAddress/String remoteAddress, int port): 连接到指定远程主机、端口,该构造器没有指定本地地址和本地端口,默认使用本地主机的IP和系统动态分配的端口</li>
<li>Socket(InetAddress/String remoteAddress, int port, InetAddress localAddr, int localPort): 连接到指定远程主机、端口,并指定本地ip和本地端口</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">Socket s = <span class="keyword">new</span> Socket(<span class="string">"127.0.0.1"</span>, <span class="number">30000</span>);</div><div class="line"><span class="comment">//上面代码就会连接到指定的服务器,让服务端的accept()方法向下执行</span></div><div class="line"></div><div class="line"><span class="comment">//下面就可以使用Socket进行通信了</span></div></pre></td></tr></table></figure>
<p>Socket提供了两个方法获取输入流和输出流</p>
<blockquote>
<ol>
<li>InputStream getInputStream(): 返回该Socket对象对应的输入流,让程序通过输入流从Socket中取数据</li>
<li>OutputStream getOutputStream(): 返回该Socket对象对应的输出流,让程序通过输出流从Socket中输出数据</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Server</span> </span>{</div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> ServerSocket ss = <span class="keyword">new</span> ServerSocket(<span class="number">30000</span>);</div><div class="line"> <span class="keyword">while</span> (<span class="keyword">true</span>) {</div><div class="line"> Socket s = ss.accept();</div><div class="line"> PrintStream ps = <span class="keyword">new</span> PrintStream(s.getOutputStream());</div><div class="line"> ps.println(<span class="string">"你好,你收到了服务器的推送消息"</span>);</div><div class="line"> ps.close();</div><div class="line"> s.close();</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>{</div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> Socket s = <span class="keyword">new</span> Socket(<span class="string">"127.0.0.1"</span>, <span class="number">30000</span>);</div><div class="line"> BufferedReader br = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(s.getInputStream()));</div><div class="line"> String line = br.readLine();</div><div class="line"> System.out.println(<span class="string">"来自服务器的数据: "</span> + line);</div><div class="line"> br.close();</div><div class="line"> s.close();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="使用NIO实现非阻塞式网络通信"><a href="#使用NIO实现非阻塞式网络通信" class="headerlink" title="使用NIO实现非阻塞式网络通信"></a>使用NIO实现非阻塞式网络通信</h3><p>Java的NIO为非阻塞式Socket通信提供了如下几个特殊类:</p>
<blockquote>
<ol>
<li>Selector: 它是SelectableChannel对象的多路复用器,所有采用非阻塞方式进行通信的Cannel都应该注册Selector对象。通过通过调用此类的<font color="red">open()</font>静态方法创建Selector实例,该方法使用系统默认的Selector来返回新的Selector</li>
</ol>
</blockquote>
<p>Selector可以同时监控多个SelectableChannel的IO状况,是非阻塞IO的核心。一个Selector实例有三个SelectionKey集合:</p>
<blockquote>
<ol>
<li>所有的SelectionKey的集合: 注册在该Selector上的Channel,调用keys()方法返回</li>
<li>被选择的SelectionKey的集合: 所有可以通过select()方法获取的、需要进行IO处理的Channel,调用selectedKeys()返回</li>
<li>被取消的SelectionKey集合: 代表所有被取消注册关系的Channel,在下次执行select()方法时,这个Channel对应的SelectionKey会被彻底删除,程序无须直接方法该集合</li>
</ol>
</blockquote>
<p>除此之外Selector还提供一系列和select()相关的方法:</p>
<blockquote>
<ol>
<li>int select(): 监控所有注册的Channel,当它们中间需要进行IO处理时,该方法返回,并将对应的SelectionKey加入被选择的SelectionKey集合中,返回这些Channel的数量;</li>
<li>int select(long timeout): 可以设置超时时间的select()操作;</li>
<li>int selectNow(): 执行一个立即返回select()操作,相对于无参的select()方法而言,该方法不会阻塞线程</li>
<li>Selector wakeup(): 使一个还未返回的select()方法立刻返回</li>
<li>SelectableChannel: 可以支持非阻塞IO操作的Channel对象,它可被注册到Selector上,这种注册关系有SelectionKey实例表示。Selector对象提供了一个select()方法,该方法允许应用程序同事监控多个IO Channel</li>
</ol>
</blockquote>
<p>可调用SelectableChannel的register()方法将其注册到指定的Selector上,当该Selector上的SelectableChannel需要处理IOc操作,可以调用Selector实例的select()方法获取他们数量,通过selectedKeys()获取需要处理IO的SelectableChannel集<br>SelectableChannel支持阻塞和非阻塞两种模式:</p>
<blockquote>
<ol>
<li>SelectableChannel configureBlocking(boolean block): 是否采用阻塞模式</li>
<li>boolean isBlocking(): 返回该Channel是否是阻塞模式</li>
</ol>
</blockquote>
<p>不同的SelectableChanne支持的操作不一样,例如ServerSocketChannel代表一个ServerSocket,它只支持OP_ACCEPT操作。<br>除此之外,SelectableChannel还提供了如下几个方法来获取它的注册状态:</p>
<blockquote>
<ol>
<li>boolean isRegistered(): 返回该Channel是否已注册在一个或多个Selector上</li>
<li>SelectionKey keyFor(Selector sel): 返回Channel和sel之间注册关系</li>
<li>SelectionKey: 该对象代表SelectableChannel和Selector之间的注册关系</li>
<li>ServerSocketChannel: 支持非阻塞操作,只支持OP_ACCEOT操作,相当于java.net.serverSocket类<br>5、SocketChannel: 支持非阻塞操作,支持OP_CONNECT、OP_READ、OP_WRITE操作,相当于java.net.Socket类</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NServer</span> </span>{</div><div class="line"> <span class="comment">//用于检测所有Channel状态的Selector</span></div><div class="line"> <span class="keyword">private</span> Selector selector = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PORT = <span class="number">30000</span>;</div><div class="line"> <span class="keyword">private</span> Charset charset = Charset.forName(<span class="string">"UTF-8"</span>);</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> selector = Selector.open();</div><div class="line"> <span class="comment">//用open方法打开一个未绑定的serverSocketChannel实例</span></div><div class="line"> ServerSocketChannel server = ServerSocketChannel.open();</div><div class="line"> InetSocketAddress isa = <span class="keyword">new</span> InetSocketAddress(<span class="string">"127.0.0.1"</span>, PORT);</div><div class="line"> <span class="comment">//将改ServerSocketChannel绑定指定IP地址</span></div><div class="line"> server.bind(isa);</div><div class="line"> <span class="comment">//设置ServerSocket以非阻塞方式工作</span></div><div class="line"> server.configureBlocking(<span class="keyword">false</span>);</div><div class="line"> <span class="comment">//将server注册到指定的Selector对象</span></div><div class="line"> server.register(selector, SelectionKey.OP_ACCEPT);</div><div class="line"></div><div class="line"> <span class="keyword">while</span> (selector.select() > <span class="number">0</span>) {</div><div class="line"> <span class="comment">//依次处理selector上每个已选择的SelectionKey</span></div><div class="line"> <span class="keyword">for</span> (SelectionKey sk : selector.selectedKeys()) {</div><div class="line"> <span class="comment">//从已选择的key中删除正在处理的sk</span></div><div class="line"> selector.selectedKeys().remove(sk);</div><div class="line"> <span class="comment">//sk对应的Channel包含客户端的连接请求</span></div><div class="line"> <span class="keyword">if</span> (sk.isAcceptable()) {</div><div class="line"> <span class="comment">//调用accept方法接受连接,产生服务端的SocketChannel</span></div><div class="line"> SocketChannel sc = server.accept();</div><div class="line"> <span class="comment">//设置非阻塞模式</span></div><div class="line"> sc.configureBlocking(<span class="keyword">false</span>);</div><div class="line"> <span class="comment">//将sc注册到selector上</span></div><div class="line"> sc.register(selector, SelectionKey.OP_READ);</div><div class="line"> <span class="comment">//将sk对应的Channel设置成准备接受其他请求</span></div><div class="line"> sk.interestOps(SelectionKey.OP_ACCEPT);</div><div class="line"> }</div><div class="line"> <span class="comment">//sk对应的Channel有数据需要读取</span></div><div class="line"> <span class="keyword">if</span> (sk.isReadable()) {</div><div class="line"> <span class="comment">//获取SelectionKey对应的Channel,该Channel中有可读的数据</span></div><div class="line"> SocketChannel sc = (SocketChannel) sk.channel();</div><div class="line"> <span class="comment">//定义准备执行读取数据的ByteBuffer</span></div><div class="line"> ByteBuffer buff = ByteBuffer.allocate(<span class="number">1024</span>);</div><div class="line"> String content = <span class="string">""</span>;</div><div class="line"> <span class="comment">//开始读取数据</span></div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="keyword">while</span> (sc.read(buff) > <span class="number">0</span>) {</div><div class="line"> buff.flip();</div><div class="line"> content += charset.decode(buff);</div><div class="line"> }</div><div class="line"> System.out.println(<span class="string">"读取的数据: "</span> + content);</div><div class="line"> <span class="comment">//将sk对应的Channel设置成准备下一次读取</span></div><div class="line"> sk.interestOps(SelectionKey.OP_READ);</div><div class="line"> }<span class="keyword">catch</span> (Exception e) {</div><div class="line"> <span class="comment">//如果捕获到了sk对应的Channel出现的异常,即表明该Channel对应的Client出现了问题,所以从Selector中取消sk的注册</span></div><div class="line"> sk.cancel();</div><div class="line"> <span class="keyword">if</span> (sk.channel() != <span class="keyword">null</span>) {</div><div class="line"> sk.channel().close();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (content.length() > <span class="number">0</span>) {</div><div class="line"> <span class="keyword">for</span> (SelectionKey key : selector.keys()) {</div><div class="line"> Channel targetChannel = key.channel();</div><div class="line"> <span class="keyword">if</span> (targetChannel <span class="keyword">instanceof</span> SocketChannel) {</div><div class="line"> SocketChannel dest = (SocketChannel) targetChannel;</div><div class="line"> dest.write(charset.encode(content));</div><div class="line"> }</div><div class="line"></div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> }</div><div class="line"> }</div><div class="line"> System.out.println(<span class="string">"--------server while下面--------------"</span>);</div><div class="line"> }</div><div class="line"></div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="keyword">new</span> NServer().init();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NClient</span> </span>{</div><div class="line"> <span class="comment">//用于检测所有Channel状态的Selector</span></div><div class="line"> <span class="keyword">private</span> Selector selector = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> PORT = <span class="number">30000</span>;</div><div class="line"> <span class="keyword">private</span> Charset charset = Charset.forName(<span class="string">"UTF-8"</span>);</div><div class="line"> <span class="keyword">private</span> SocketChannel sc = <span class="keyword">null</span>;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> selector = Selector.open();</div><div class="line"> InetSocketAddress isa = <span class="keyword">new</span> InetSocketAddress(<span class="string">"127.0.0.1"</span>, PORT);</div><div class="line"> <span class="comment">//调用open静态方法创建连接到指定主机的SocketChannel</span></div><div class="line"> sc = SocketChannel.open(isa);</div><div class="line"> <span class="comment">//设置sc以非阻塞方式工作</span></div><div class="line"> sc.configureBlocking(<span class="keyword">false</span>);</div><div class="line"> <span class="comment">//将SocketChannel对象注册到指定的Selector</span></div><div class="line"> sc.register(selector, SelectionKey.OP_READ);</div><div class="line"> <span class="comment">//启动读取服务端数据的线程</span></div><div class="line"> <span class="keyword">new</span> ClientThread().start();</div><div class="line"> <span class="comment">//获取键盘输入流</span></div><div class="line"> Scanner scan = <span class="keyword">new</span> Scanner(System.in);</div><div class="line"> <span class="keyword">while</span> (scan.hasNextLine()) {</div><div class="line"> String line = scan.nextLine();</div><div class="line"> sc.write(charset.encode(line));</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">ClientThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="keyword">while</span> (selector.select() > <span class="number">0</span>) {</div><div class="line"> <span class="keyword">for</span> (SelectionKey sk : selector.selectedKeys()) {</div><div class="line"> selector.selectedKeys().remove(sk);</div><div class="line"> <span class="keyword">if</span> (sk.isReadable()) {</div><div class="line"> SocketChannel sc = (SocketChannel) sk.channel();</div><div class="line"> ByteBuffer buff = ByteBuffer.allocate(<span class="number">1024</span>);</div><div class="line"> String content = <span class="string">""</span>;</div><div class="line"> <span class="keyword">while</span> (sc.read(buff) > <span class="number">0</span>) {</div><div class="line"> sc.read(buff);</div><div class="line"> buff.flip();</div><div class="line"> content += charset.decode(buff);</div><div class="line"> }</div><div class="line"> System.out.println(<span class="string">"聊天信息:"</span> + content);</div><div class="line"> sk.interestOps(SelectionKey.OP_READ);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }<span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="keyword">new</span> NClient().init();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<!--### 使用AIO实现异步网络通信-->
<h3 id="UDP协议基础"><a href="#UDP协议基础" class="headerlink" title="UDP协议基础"></a>UDP协议基础</h3><p>UDP协议是一种不可靠的网络协议,它在通信两端各自建立一个Socket,这两个Socket只是发送、接受数据报的对象。Java提供了DatagramSocket对象作为基于UDP协议的Socket,使用DatagramPacket代表DatagramSocket发送、接受数据报;</p>
<h3 id="使用DatagramSocket发送-接受数据报-DatagramPacket"><a href="#使用DatagramSocket发送-接受数据报-DatagramPacket" class="headerlink" title="使用DatagramSocket发送/接受数据报(DatagramPacket)"></a>使用DatagramSocket发送/接受数据报(DatagramPacket)</h3><p>Java使用DatagramSocket代表UDP协议的Socket,DatagramSocket不维护状态不能产生IO流,唯一作用就是接受和发送数据报,Java使用DatagramPacket来代表数据报,DatagramSocket接受和发送的数据都是通过DatagramPacket对象完成的;<br>DatagramSocket的构造器:</p>
<blockquote>
<ol>
<li>DatagramSocket(): 创建一个DatagramSocket实例,并将该对象绑定本机默认ip,本机随机端口</li>
<li>DatagramSocket(int port): 创建一个DatagramSocket实例,绑定本机默认ip,指定端口</li>
<li>DatagramSocket(int port, InetAddress laddr): 创建一个DatagramSocket实例,绑定指定ip、端口</li>
</ol>
</blockquote>
<p>通过如下方法来接收和发送数据:</p>
<blockquote>
<ol>
<li>receive(DatagramPacket p): 从该DatagramSocket中接收数据报</li>
<li>send(DatagramPacket p): 以该DatagramSocket对象向外发送数据报</li>
</ol>
</blockquote>
<p>DatagramPacket的构造器:</p>
<blockquote>
<ol>
<li>DatagramPacket(byte[] buf, int length): 以一个空数组来创建DatagramPacket对象,该对象的作用是接收DatagramSocket中的数据;</li>
<li>DatagramPacket(byte[] buf, int length, InetAddress addr, int port): 以一个包含数据的数组来创建DatagramPacket对象,创建该DatagramPacket对象指定IP和端口——这就决定了该数据报的目的地</li>
<li>DatagramPacket(byte[] buf, int offset, int length): 以一个空数组来创建DatagramPacket对象,并指定接收到的数据放入buf数组中时从offset开始,最多放length个字节</li>
<li>DatagramPacket(byte[] buf, int offset, int length, InetAddress addr, int port): 创建一个用于发送的DataPacket对象,指定发送buf数组中从offset开始,总共length个字节;</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//接受数据用上面的1、3创建接受对象</span></div><div class="line"><span class="comment">//创建一个接受数据的DatagramPacket对象</span></div><div class="line">DatagramPacket packet = <span class="keyword">new</span> DatagramPacket(buf, <span class="number">256</span>);</div><div class="line"><span class="comment">//接受数据报</span></div><div class="line">socket.receive(packet);</div><div class="line"></div><div class="line"></div><div class="line"><span class="comment">//发送数据报用上面2、4创建发送对象</span></div><div class="line"><span class="comment">//创建一个发送数据报的DatagramPacket对象</span></div><div class="line">DatagramPacket packet = <span class="keyword">new</span> DatagramPacket(buf, length,address, port);</div><div class="line"><span class="comment">//发送数据报</span></div><div class="line">socket.send(packet);</div></pre></td></tr></table></figure>
<!--### 使用MulticastSocket实现多点广播-->
<h3 id="通过Proxy使用代理服务器"><a href="#通过Proxy使用代理服务器" class="headerlink" title="通过Proxy使用代理服务器"></a>通过Proxy使用代理服务器</h3><p>Java5开始提供Proxy和ProxySelector两个类,Proxy代表一个代理服务器,可以在URLConnection连接时指定Proxy,创建Socket连接时也可以指定Proxy;<br>Proxy有一个构造器:Proxy(Proxy.Type type, SocketAddress sa),用于创建Proxy对象,sa参数指定代理服务器的地址,type代表代理服务器的类型,类型有如下三种:</p>
<blockquote>
<ol>
<li>Proxy.Type.DIRECT: 表示直接连接,不使用代理</li>
<li>Proxy.Type.HTTP: 表示支持高级代理协议,如HTTP或FTP</li>
<li>Proxy.TYpe.SOCKS: 表示SOCKS(V4或V5)代理</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProxyTest</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">final</span> String PROXY_ADDR = <span class="string">"129.82.12.188"</span>;</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> PROXY_PORT = <span class="number">3124</span>;</div><div class="line"></div><div class="line"> String urlStr = <span class="string">"http://www.crazyit.org"</span>;</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>{</div><div class="line"> URL url = <span class="keyword">new</span> URL(urlStr);</div><div class="line"> Proxy proxy = <span class="keyword">new</span> Proxy(Proxy.Type.HTTP, <span class="keyword">new</span> InetSocketAddress(PROXY_ADDR, PROXY_PORT));</div><div class="line"></div><div class="line"> URLConnection conn = url.openConnection(proxy);</div><div class="line"></div><div class="line"> conn.setConnectTimeout(<span class="number">3000</span>);</div><div class="line"></div><div class="line"> Scanner scan = <span class="keyword">new</span> Scanner(conn.getInputStream());</div><div class="line"> PrintStream ps = <span class="keyword">new</span> PrintStream(<span class="string">"index.html"</span>);</div><div class="line"> <span class="keyword">while</span> (scan.hasNextLine()) {</div><div class="line"> String line = scan.nextLine();</div><div class="line"> System.out.println(line);</div><div class="line"> ps.println(line);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="keyword">new</span> ProxyTest().init();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="通过ProxySelector使用代理服务器"><a href="#通过ProxySelector使用代理服务器" class="headerlink" title="通过ProxySelector使用代理服务器"></a>通过ProxySelector使用代理服务器</h3><p>Java5开始提供Proxy和ProxySelector两个类,ProxySelector代表一个代理选择器,他提供了对代理服务器更加灵活地控制,可以分别对HTTP、HTTPS、FTP、SOCKS等进行分别设置,还可以设置不需要通过代理服务器的主机和地址;<br>使用Proxy对象每次都要显示指定代理服务器,比较麻烦。如果希望每次打开总是具有默认的代理服务器,则要借助于ProxySelector来实现。<br>ProxySelector代表一个代理选择器,它是一个抽象类,程序无法创建他的实例,可以考虑继承ProxySelector来实现自己的代理选择器。创建一个继承ProxySelector的类,并让该类实现如下两个抽象方法:</p>
<blockquote>
<ol>
<li>List<proxy> select(URI uri): 根据业务需要返回搭理服务器列表,如果该方法返回的集合中只包含一个Proxy,该Proxy将会作为默认的代理服务器;</proxy></li>
<li>connectFailed(URI uri, SocketAddress sa, IOException ioe): 连接代理服务器失败时回调该方法;</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProxySelectorTest</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">final</span> String PROXY_ADDR = <span class="string">"139.82.12.188"</span>;</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> PROXY_PORT = <span class="number">3124</span>;</div><div class="line"> String urlStr = <span class="string">"http://www.crazyit.org"</span>;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> ProxySelector.setDefault(<span class="keyword">new</span> ProxySelector() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> List<Proxy> <span class="title">select</span><span class="params">(URI uri)</span> </span>{</div><div class="line"> List<Proxy> result = <span class="keyword">new</span> ArrayList<Proxy>();</div><div class="line"> result.add(<span class="keyword">new</span> Proxy(Proxy.Type.HTTP, <span class="keyword">new</span> InetSocketAddress(PROXY_ADDR, PROXY_PORT)));</div><div class="line"> <span class="keyword">return</span> result;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">connectFailed</span><span class="params">(URI uri, SocketAddress sa, IOException ioe)</span> </span>{</div><div class="line"> System.out.println(<span class="string">"无法连接到指定代理服务器"</span>);</div><div class="line"> }</div><div class="line"> }); </div><div class="line"> URL url = <span class="keyword">new</span> URL(urlStr);</div><div class="line"> URLConnection conn = url.openConnection();</div><div class="line"> <span class="comment">//....</span></div><div class="line"> } </div><div class="line">}</div></pre></td></tr></table></figure>
<p>上面打开连接没有指定代理服务器,但是实际上还是使用了代理服务器。除此之外,Java为ProxySelector提供了一个实现类,sun.net.spi.DefaultProxySelector(这是一个未公开的API,应尽量避免使用该API)</p>
]]></content>
<categories>
<category> Java </category>
</categories>
<tags>
<tag> Java </tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java基础(八)]]></title>
<url>/2017/06/07/Java%E5%9F%BA%E7%A1%80-%E5%85%AB/</url>
<content type="html"><![CDATA[<h2 id="输入-输出"><a href="#输入-输出" class="headerlink" title="输入/输出"></a>输入/输出</h2><p>IO(输入/输出)是比较乏味的事情,因为看不到明显的运行效果,但输入/输出是所有程序都必须使用的部分——输入机制,允许程序去读外部数据(存储设备)、用户输入数据;输出机制,允许程序记录运行状态,将程序数据输出到存储设备中。<br>Java的IO通过java.io包下的类和接口支持,java.io包括输入、输出两种IO流,每种输入、输出流又可分为字节流和字符流。Java7在java.nio及其子包下同一系列全新的API,这些API对原有新IO的升级,因此也被称为NIO2,更高效的进行输入、输出操作。</p>
<h3 id="使用File类访问本地文件系统"><a href="#使用File类访问本地文件系统" class="headerlink" title="使用File类访问本地文件系统"></a>使用File类访问本地文件系统</h3><p>File类是java.io包下进行操作文件和目录。File能新建、删除、重命名和目录,FIle不能访问文件内容。如果要访问文件内容则需要使用输入/输出流。<br>File类可以通过文件路径创建File实例,文件路径可以是绝对路径也可以是相对路径。File类提供了很多方法操作文件和目录,下面列出一些常用方法:</p>
<blockquote>
<ol>
<li>String getName():返回File对象所表示的文件名或路径名</li>
<li>String getPath(): 返回File对象对应的路径名</li>
<li>File getAbsoluteFile(): 返回File对象的绝对路径</li>
<li>String getAbsolutePath():返回File对象所对应的绝对路径</li>
<li>String getParent(): 返回File对象所对应目录的父目录名</li>
<li>boolean renameTo(File newName): 重命名此File对象所对应的文件或目录</li>
</ol>
</blockquote>
<p>文件检测相关的方法</p>
<blockquote>
<ol>
<li>boolean exists(): 判断文件或目录是否存在</li>
<li>boolean canWrite(): 判断文件和目录是否可写</li>
<li>boolean canRead(): 判断文件和目录是否可读</li>
<li>boolean isFile(): 判断是否是文件</li>
<li>boolean isDiretory(): 判断是否是目录</li>
<li>boolean isAbsolute(): 判断是否是绝对路径</li>
</ol>
</blockquote>
<p>获取常规文件信息</p>
<blockquote>
<ol>
<li>long lastModified(): 返回文件的最后修改时间</li>
<li>long length(): 返回文件内容的长度</li>
</ol>
</blockquote>
<p>文件操作相关的方法</p>
<blockquote>
<ol>
<li>boolean createNewFile(): 当文件不存在时创建文件</li>
<li>boolean delete(): 删除文件或路径</li>
<li>static File createTempFile(String prefix, String suffix): 在默认临时文件目录创建一个临时空文件,使用给定前缀、系统生成的随机数和给定后缀作为文件名</li>
<li>static File createTempFile(String prefix, String suffix, File directory):在directory目录创建一个临时空文件,使用给定前缀、系统生成的随机数和给定后缀作为文件名</li>
<li>void deleteOnExit(): 注册一个删除钩子,当Java虚拟机退出时,删除File对象所对应的文件和目录 </li>
</ol>
</blockquote>
<p>目录操作相关方法</p>
<blockquote>
<ol>
<li>boolean mkdir(): 创建目录</li>
<li>String[] list(): 返回子文件名和路径名</li>
<li>File[] listFiles(): 返回所有子文件和路径</li>
<li>static File[] listRoots(): 返回系统所有的根路径</li>
</ol>
</blockquote>
<h3 id="使用文件过滤器"><a href="#使用文件过滤器" class="headerlink" title="使用文件过滤器"></a>使用文件过滤器</h3><p>File类的list()方法可以接受FilenameFilter参数,该参数可以过滤出只有符合条件的文件。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">File file = <span class="keyword">new</span> File(<span class="string">"."</span>);</div><div class="line"><span class="comment">//筛选出文件名以.java结尾或者是路径的</span></div><div class="line">String [] nameList = file.list((dir, name) -> name.endsWith(<span class="string">".java"</span>) || <span class="keyword">new</span> File(name).isDirectory());</div><div class="line">System.out.println(nameList);</div></pre></td></tr></table></figure></p>
<h3 id="理解IO流的模型和处理方式"><a href="#理解IO流的模型和处理方式" class="headerlink" title="理解IO流的模型和处理方式"></a>理解IO流的模型和处理方式</h3><p>Java的IO流是实现输入/输出的基础,在Java中把不同的输入/输出源的抽象表述为”流”。流(stream)是从起源(source)到接受(sink)的有序数据。<br>按照不同的分类方式,可以将流分为不同的类型。</p>
<blockquote>
<ol>
<li>输入和输出流: 输入流只能从中读取数据,不能写入数据; 输出流只能写入数据,不能读取数据</li>
<li>字节流和字符流: 字节流操作的数据单元是8位字节,字符流操作的数据单元是16为的字符</li>
<li>节点流和处理流: 从/向一个特定的IO设备读/写数据的流称为节点流也称低级流;处理流则用于对于一个已经存在的流进行连接或者封装,通过封装后的流来实现数据读/写功能。</li>
</ol>
</blockquote>
<p>Java的IO流共涉及40多个类,这些类都是从如下4个抽象基类派生的:</p>
<blockquote>
<ol>
<li>InputStream/Reader: 所有输入流的基类,前者是字节流,后者是字符流</li>
<li>OutputStream/Writer: 所有输出流的基类,前者是字节流,后者是字符流</li>
</ol>
</blockquote>
<p>InputStream和Reader是所有输入流的抽象基类,它们的方法是所有输入流都可使用的方法:<br>在InputStream里包含如下三个方法:</p>
<blockquote>
<ol>
<li>int read(): 从输入流中读取单个字节,返回读取的字节数据(字节数据可直接转换int类型)</li>
<li>int read(byte[] b):从输入流中最多读取b.length个字节数据,并将其存储在字节数组中,返回实际读取的字节数</li>
<li>int read(byte[] b, int off, int len):从输入流中最多读取len个字节数据,放在字节数组中,并不是从数组起点开始,而是从off位置开始</li>
</ol>
</blockquote>
<p>在Reader里包含如下三个方法:</p>
<blockquote>
<ol>
<li>int read(): 从输入流中读取单个字符</li>
<li>int read(char[] cbuf):从输入流中最多读取cbuf.length个字符数据,并将其放在cbuf字符数组中,返回字符数</li>
<li>int read(char[] b, int off, int len):从输入流中最多读取len个字符数据,放在字符数组中,并不是从数组起点开始,而是从off位置开始,返回字符数<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//创建字节输入流</span></div><div class="line">FileInputStream fis = <span class="keyword">new</span> FileInputStream(<span class="string">"FileInputStreamTest.java"</span>);</div><div class="line"><span class="keyword">byte</span>[] bbuf = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">1024</span>];</div><div class="line"><span class="keyword">int</span> hasRead = <span class="number">0</span>;</div><div class="line"><span class="keyword">while</span> ((hasRead = fis.read(bbuf)) > <span class="number">0</span>) {</div><div class="line"> System.out.println(<span class="keyword">new</span> String bbuf, <span class="number">0</span>, hasRead);</div><div class="line">}</div><div class="line">fis.close();</div></pre></td></tr></table></figure>
</li>
</ol>
</blockquote>
<p>OutputStream和Writer是所有输出流的抽象基类,它们的方法是所有输出流都可使用的方法:<br>在OutputStream里包含如下三个方法:</p>
<blockquote>
<ol>
<li>void write(int c): 将指定字节/字符输出到输出流中, 其中c既可以为字符也可以为字节</li>
<li>void write(byte[]/char[] b):将字节数组/字符数据输出到自定输出流中</li>
<li>void write(byte[]/char[] b, int off, int len):将字节数组/字符数据从off位置开始,长度为len输出到指定输出流中</li>
</ol>
</blockquote>
<p>在Writer里包含如下三个方法:</p>
<blockquote>
<ol>
<li>void write(Sring str): 将str字符串里包含的字符输出到指定输出流中</li>
<li>void write(String, int off, int len):将str字符串从off位置开始,len长度输出到指定输出流中</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">FileInputStream fis = <span class="keyword">new</span> FileInputStream(<span class="string">"FileInputStreamTest.java"</span>);</div><div class="line">FileOutputStream fos = <span class="keyword">new</span> FileOutputStream(<span class="string">"newFile.txt"</span>);</div><div class="line"><span class="keyword">byte</span>[] bbuf = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">32</span>];</div><div class="line"><span class="keyword">int</span> hasRead = <span class="number">0</span>;</div><div class="line"><span class="keyword">while</span>((hasRead = fis.read(bbuf)) > <span class="number">0</span>) {</div><div class="line"> fos.write(bbuf, <span class="number">0</span>, hasRead);</div><div class="line">}</div><div class="line"></div><div class="line">fis.close();</div><div class="line">fos.close();</div></pre></td></tr></table></figure>
<p>Java的输入/输出流体系提供了进40个类,这些类看上去没有规律,如果将其按功能进行分类,则不难发现其是非常规律的。</p>
<table>
<thead>
<tr>
<th style="text-align:left">分类</th>
<th style="text-align:left">字节输入流</th>
<th style="text-align:left">字节输出流</th>
<th style="text-align:left">字符输入流</th>
<th style="text-align:left">字符输出流</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">抽象基类</td>
<td style="text-align:left">InputStream</td>
<td style="text-align:left">OutputStream</td>
<td style="text-align:left">Read</td>
<td style="text-align:left">Writer</td>
</tr>
<tr>
<td style="text-align:left">访问文件</td>
<td style="text-align:left">FileInputStream</td>
<td style="text-align:left">FileOutputStream</td>
<td style="text-align:left">FileRead</td>
<td style="text-align:left">FileWriter</td>
</tr>
<tr>
<td style="text-align:left">访问数组</td>
<td style="text-align:left">ByteArrayInput<br>Stream</td>
<td style="text-align:left">ByteArrayOutput<br>Stream</td>
<td style="text-align:left">CharArrayRead</td>
<td style="text-align:left">CharArrayWriter</td>
</tr>
<tr>
<td style="text-align:left">访问管道</td>
<td style="text-align:left">PipedInputStream</td>
<td style="text-align:left">PipedOutputStr<br>eam</td>
<td style="text-align:left">PipedRead</td>
<td style="text-align:left">PipedWriter</td>
</tr>
<tr>
<td style="text-align:left">访问字符串</td>
<td style="text-align:left">-</td>
<td style="text-align:left">-</td>
<td style="text-align:left">StringRead</td>
<td style="text-align:left">StringWriter</td>
</tr>
<tr>
<td style="text-align:left">缓冲流</td>
<td style="text-align:left">BufferedInputStr<br>eam</td>
<td style="text-align:left">BufferedOutputS<br>tream</td>
<td style="text-align:left">BufferedRead</td>
<td style="text-align:left">BufferedWriter</td>
</tr>
<tr>
<td style="text-align:left">转换流</td>
<td style="text-align:left"></td>
<td style="text-align:left"></td>
<td style="text-align:left">InputStreamRead</td>
<td style="text-align:left">OutputWriter</td>
</tr>
<tr>
<td style="text-align:left">对象流</td>
<td style="text-align:left">ObjectInputStream</td>
<td style="text-align:left">ObjectOutputStr<br>eam</td>
<td style="text-align:left"></td>
<td style="text-align:left"></td>
</tr>
<tr>
<td style="text-align:left">抽象基类</td>
<td style="text-align:left">FilterInputStream</td>
<td style="text-align:left">FilterOutputStream</td>
<td style="text-align:left">FilterRead</td>
<td style="text-align:left">FilterWriter</td>
</tr>
<tr>
<td style="text-align:left">打印流</td>
<td style="text-align:left"></td>
<td style="text-align:left">PrintStream</td>
<td style="text-align:left"></td>
<td style="text-align:left">PrintWriter</td>
</tr>
<tr>
<td style="text-align:left">推回输入流</td>
<td style="text-align:left">PushbackInputSt<br>ream</td>
<td style="text-align:left"></td>
<td style="text-align:left">PushbackRead</td>
<td style="text-align:left"></td>
</tr>
<tr>
<td style="text-align:left">特殊流</td>
<td style="text-align:left">DataInputStream</td>
<td style="text-align:left">DataOutputStream</td>
<td style="text-align:left">-</td>
<td style="text-align:left">-</td>
</tr>
</tbody>
</table>
<h3 id="使用转换流将字节流转换成字符流"><a href="#使用转换流将字节流转换成字符流" class="headerlink" title="使用转换流将字节流转换成字符流"></a>使用转换流将字节流转换成字符流</h3><p>转换流用于将字节流转换成字符流,其中InputStreamRead将字节输入流转换成字符输入流,OutputStreamWriter将字节输出流转换成字符输出流。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">try</span>(</div><div class="line"> <span class="comment">//将System.in对象转换成Reader对象</span></div><div class="line"> InputStreamRead reader = <span class="keyword">new</span> InputStreamRead(System.in);</div><div class="line"> BufferedReader br = <span class="keyword">new</span> BufferedReader(reader);</div><div class="line"> )</div><div class="line">{</div><div class="line"> String line = <span class="keyword">null</span>;</div><div class="line"> <span class="keyword">while</span> ((line = br.readLine()) != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">if</span> (line.equals(<span class="string">"exit"</span>)) {</div><div class="line"> System.exit(<span class="number">1</span>);</div><div class="line"> }</div><div class="line"> System.out.println(<span class="string">"输入内容为:"</span> + line);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<!--### 推回流的功能和用法-->
<!--### 重定向标准输入、输出-->
<!--### 访问其他进程的输入、输出-->
<h3 id="RandomAccessFile的功能和用法"><a href="#RandomAccessFile的功能和用法" class="headerlink" title="RandomAccessFile的功能和用法"></a>RandomAccessFile的功能和用法</h3><p>RandomAccessFile是Java输入/输出流功能最丰富的文件内容访问类。他既可以读取文件内容,也可以向文件输出数据。与普通的输入/输出流不同的是RandomAccessFile支持”随机访问”的方式,程序可以直接跳转到文件任意地方来读写数据。RandomAccessFile可以向已存在的文件后追加内容。<br>RandomAccessFile虽然方法多,但是只能读写文件不能读写其他IO节点。<br>RandomAccessFile对象也包含一个记录指针,用于表示当前读写的位置。新创建一个RandomAccessFile对象,文件记录指针位于文件头,当读写n个字节,文件记录指针向后移动n个字节,该指针可以自由移动。RandomAccessFile包含两个方法操作文件记录指针</p>
<blockquote>
<ol>
<li>long getFilePointer():返回文件记录指针的位置</li>
<li>void seek(lng pos): 将文件记录指针定位到pos位置</li>
</ol>
</blockquote>
<p>RandomAccessFile对象有个model参数,指定访问模式:</p>
<blockquote>
<ol>
<li>“r”: 只读方式打开文件,写入将抛出异常</li>
<li>“rw”: 以读、写方式打开文件,如果不存在,则创建文件</li>
<li>“rws”: 以读、写方法打开文件,相当于”rw”模式,还要求对文件的内容或元数据的每个更新都同步到底层存储设备</li>
<li>“rwd”:以读、写方法打开文件,相当于”rw”模式,还要求对文件的内容的每个更新都同步到底层存储设备</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">try</span>(</div><div class="line"> RandomAccessFile raf = <span class="keyword">new</span> RandomAccessFile(<span class="string">"RandomAccessFileTest.java"</span>, <span class="string">"r"</span>)</div><div class="line"> )</div><div class="line">{</div><div class="line"> System.out.println(<span class="string">"记录指针初始位置"</span> + raf.getFilepointer());</div><div class="line"> raf.seek(<span class="number">300</span>);</div><div class="line"> <span class="keyword">byte</span>[] bbuf = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">1024</span>];</div><div class="line"> <span class="keyword">int</span> hasRead = <span class="number">0</span>;</div><div class="line"> <span class="keyword">while</span> ((hasRead = raf.read(bbuf)) > <span class="number">0</span>) {</div><div class="line"> System.out.println(<span class="keyword">new</span> String(bbuf, <span class="number">0</span>, hasRead));</div><div class="line"> }</div><div class="line">}<span class="keyword">catch</span> (IOException ex) {</div><div class="line"> ex.printStackTrace();</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="对象序列化机制和作用"><a href="#对象序列化机制和作用" class="headerlink" title="对象序列化机制和作用"></a>对象序列化机制和作用</h3><p>序列化机制允许将实现序列化的Java对象准换成字节序列,这些字节序列可以保存在磁盘上,或者通过网络传输,以备以后重新恢复成原来的而对象。序列化机制使得对象脱离程序而独立存在。<br>对象的序列化指将一个Java对象写入IO流中,与此对象的是,对象的反序列话则指从IO流中恢复该Java对象。<br>为了让某个类可序列化,该类必须支持如下两个接口之一:</p>
<blockquote>
<ol>
<li>Serializable : Java很多类已经实现该接口,该接口是一个标记接口</li>
<li>Externalizable</li>
</ol>
</blockquote>
<h3 id="通过实现Serializable接口实现序列化"><a href="#通过实现Serializable接口实现序列化" class="headerlink" title="通过实现Serializable接口实现序列化"></a>通过实现Serializable接口实现序列化</h3><p>如果要序列化对象中包含可饮用类型,那么这个引用类型必须是可序列化得,否则拥有该类型成员变量的类也是不可序列化的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//序列化对象</span></div><div class="line"><span class="comment">//创建一个ObjectOutputStream输出流</span></div><div class="line">ObjectOutputStrem oos = <span class="keyword">new</span> ObjectOutputStream(<span class="keyword">new</span> FileOutputStream(<span class="string">"object.txt"</span>));</div><div class="line"><span class="comment">//将一个Person对象输出到输出流中</span></div><div class="line">oos.writeObject(person);</div><div class="line"></div><div class="line"><span class="comment">//反序列化对象</span></div><div class="line">ObjectInputStream ois = <span class="keyword">new</span> ObjectInputStream(<span class="keyword">new</span> FileInputStream(<span class="string">"object.txt"</span>));</div><div class="line">Person p = (Person)ois.readObject();</div></pre></td></tr></table></figure>
<h3 id="实现定制的序列化"><a href="#实现定制的序列化" class="headerlink" title="实现定制的序列化"></a>实现定制的序列化</h3><p>在某些场景下某个类中的某个实例变量不要序列化则需要在修饰符后添加transient修饰;<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">int</span> age;</div></pre></td></tr></table></figure></p>
<p>使用transient关键字修饰虽然简单、方便,但被transient修饰的变量完全隔离在序列化机制之外,这样导致在发序列化恢复Java对象是无法取得该实例的变量。Java还提供了自定义序列化机制,通过这种序列化机制可让程序控制如何序列化各实例变量。在序列化和反序列化过程中需要特殊处理类应提供如下特殊方法,这些特殊方法用以实现自定义序列化:</p>
<blockquote>
<ol>
<li>private void writeObject(java.io.ObjectOutputStream out) throw IOException; 负责写入特定类的实例状态,一边响应的readObject()方法可以恢复,通过重写该方法,程序员可以完全获得对序列化机制的控制,可以自主决定哪些实例变量需要序列化</li>
<li>private void readObject(java.io.ObjectInputStream in)throws IOException, ClassNotFoundException; 负责从流中读取并回复对象实例变量,通过重写该方法,程序员可以完全获的对反序列话机制的控制,可以自主决定需要反序列化哪些实例变量</li>
<li>private void readObjectNoData()throws ObjectStreamException; 当序列化流不完整时,可以用来正确的初始化反序列化对象</li>
</ol>
</blockquote>
<p>writeObject()方法存储实例变量的顺序应该和readObject()方法中恢复实例变量的顺序一致,否则将不能正确恢复该Java对象。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> <span class="keyword">implements</span> <span class="title">java</span>.<span class="title">io</span>.<span class="title">Serializable</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> String name;</div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> age;</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Person</span><span class="params">(String name, <span class="keyword">int</span> age)</span> </span>{</div><div class="line"> System.out.println(<span class="string">"有参数构造器"</span>);</div><div class="line"> }</div><div class="line"> <span class="comment">//省略name与age的setter和getter方法</span></div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">writeObject</span><span class="params">(java.io.ObjectOutputStream out)</span> </span></div><div class="line"><span class="function"> <span class="keyword">throws</span> IOExcetpion </span>{</div><div class="line"> 将name实例变量值反转后写入二进制流</div><div class="line"> out.writeObject(<span class="keyword">new</span> StringBuffer(name).reverse());</div><div class="line"> out.writeInt(age);</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">readObject</span><span class="params">(java.io.ObjectInputStream in)</span> </span></div><div class="line"><span class="function"> <span class="keyword">throws</span> IOException, ClassNotFoundException </span>{</div><div class="line"> </div><div class="line"> <span class="keyword">this</span>.name = ((StringBuffer)in.readObject()).reverse().toString(); </div><div class="line"> <span class="keyword">this</span>.age = in.readInt();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<!--### 通过实现Externalizable接口实现序列化-->
<h3 id="序列化版本"><a href="#序列化版本" class="headerlink" title="序列化版本"></a>序列化版本</h3><p>反序列化Java对象必须提供该对象class文件,随着项目的升级,系统的class文件也会升级,Java如何保证两个class文件的兼容性?<br>Java序列化机制润徐为序列化类提供一个private static final的serialVersionUID值,该类变量的值用于表示Java类的序列化版本。分配serialVersionUID类变量的值非常简单:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>{</div><div class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">512L</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="Java新IO的概念和作用"><a href="#Java新IO的概念和作用" class="headerlink" title="Java新IO的概念和作用"></a>Java新IO的概念和作用</h3><p>前面所讲的输入/输出流都是阻塞式的输入/输出。传统的输入/输出流都是通过字节的移动来处理,效率不高。在JDK1.4,Java提供了额一系列改进的输入/输出处理的新功能,这些功能被称为NIO(New IO)。<br>NIO采用内存映射文件的方式处理输入/输出,新IO将文件或文件的一段映射到内存中。Channel(通道)和Buffer(缓冲)是新IO的两个核心对象。<br>Channel是对传统的输入/输出的模拟,在NIO中所有的数据都是通过通道来传输;Channel与传统的InputStream、OutputStream的最大区别它提供了一个map()方法,通过map()方法直接将”一块数据”映射到内存中。如果说传统的IO是面向流的处理,则NIO是面向块的处理。<br>Buffer可以理解成是一个容器,本质是一个数组,Channel读取写入都要通过Buffer进行,Channel也可以直接将文件的某块数据映射成Buffer。<br>除此之外,NIO还提供用于将Unicode字符串映射成字节序列及逆映射操作的Charset类,也提供了支持非阻塞式的输入/输出Selector类。</p>
<h3 id="使用Buffer和Channel完成输入、输出"><a href="#使用Buffer和Channel完成输入、输出" class="headerlink" title="使用Buffer和Channel完成输入、输出"></a>使用Buffer和Channel完成输入、输出</h3><p>Buffer是一个抽象类,最常用的子类是ByteBuffer,还包括其他基本数据类型的Buffer类:CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。他们可以在底层字节数组上进行get、set操作。这些Buffer类没有提供构造器,通过如下方法得到Buffer对象。</p>
<blockquote>
<p>static XxxBuffer allocate(int capacity):创建一个容量capacity的XxxBuffer对象。</p>
</blockquote>
<p>Buffer中有三个重要概念:容量(capacity)、界限(limit)和位置(position)</p>
<blockquote>
<ol>
<li>容量(capacity):表示该Buffer的最大数据容量。不可为负值,创建后不可改变</li>
<li>界限(limit): 第一个不应该被读出或写入缓冲区位置索引,就是说,limit后的数据既不可读也不可写</li>
<li>位置(position): 用于致命下一个可以被读出或写入缓冲区的位置索引(类似IO流中的记录指针)<br><img src="http://opbae2xuz.bkt.clouddn.com/images/bhlin/2017-06-12-01.png" alt=""></li>
</ol>
</blockquote>
<p>Buffer的主要作用就是装入数据,初始化的时候position为0,limit为capacity,通过put()方法放入数据,position相应后移。装载数据结束,调用filip()方法,将limit设置为position所在的位置为输出做准备;当输出结束后,调用clear()方法,clear不清空数据,仅仅将position为0,limit为capacity,为再次装入数据准备;<br>Buffer包含的常用方法:</p>
<blockquote>
<ol>
<li>int capacity(): 返回Buffer的capactiy的大小</li>
<li>boolean hasRemaining(): 判断当前位置(position)和界限(limit)之间是否还有元素可提供</li>
<li>int limit():返回界面(limit)的位置</li>
<li>Buffer limit(int newLt): 从新设置界限(limit)的值,并返回一个具有新的limit的缓冲区对象</li>
<li>Buffer mark():设置mark的位置,他只能在0和位置(position)之间做mark</li>
<li>int position(): 返回position()值</li>
<li>Buffer position(int newPs): 设置Buffer的position,并返回position修改后的Buffer对象</li>
<li>Buffer reset(): 将position转到mark所在的位置</li>
<li>Buffer rewind():将position设置成0,取消设置的mark</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">CharBuffer buff = CharBuffer.allocate(<span class="number">8</span>);</div><div class="line">System.out.println(buff.capacity()); <span class="comment">//8</span></div><div class="line">System.out.println(buff.limit()); <span class="comment">//8</span></div><div class="line">System.out.println(buff.position()); <span class="comment">//0</span></div><div class="line"></div><div class="line">buff.put(<span class="string">'a'</span>);</div><div class="line">buff.put(<span class="string">'b'</span>);</div><div class="line">buff.put(<span class="string">'c'</span>);</div><div class="line">System.out.println(buff.position()); <span class="comment">//3</span></div><div class="line">buff.flip();</div><div class="line">System.out.println(buff.limit()); <span class="comment">//3</span></div><div class="line">System.out.println(buff.position()); <span class="comment">//0</span></div><div class="line"></div><div class="line"><span class="comment">//取出元素</span></div><div class="line">System.out.println(buff.get());<span class="comment">//get使position+1</span></div><div class="line">System.out.println(buff.position()); <span class="comment">// 0</span></div><div class="line">buff.clear();</div><div class="line">System.out.println(buff.capacity()); <span class="comment">//8</span></div><div class="line">System.out.println(buff.limit()); <span class="comment">//8</span></div><div class="line">System.out.println(buff.position()); <span class="comment">//0</span></div><div class="line">System.out.println(buff.get(<span class="number">2</span>)); <span class="comment">//缓冲区的内容并没有被清除</span></div><div class="line">System.out.println(buff.position()); <span class="comment">// 0</span></div></pre></td></tr></table></figure>
<p>Channel类似传统的流对象,与传统的流对象有两个主要区别</p>
<blockquote>
<ol>
<li>Channel可以直接将文件部分或全部直接映射成Buffer</li>
<li>程序不能直接访问Channel中的数据,只能通过Buffer进行交互</li>
</ol>
</blockquote>
<p>Java为Channel接口提供了DatagramChannel、FileChannel、Pipe.SinkChannel、PipSourceChannel、SelectableChannel、ServerSocketChannel、SocketChannel等实现类。所有的Channel不应该通过构造器创建,而是通过传统节点InputStream/OutpuStream的getChannel()方法获得,不同节点流获得的Channel不一样。Channel最常用的map()、read()和write();map()方法第一个参数执行映射时的模式分别有只读、读写等</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">File f = <span class="keyword">new</span> File(<span class="string">"src/Main.java"</span>);</div><div class="line">System.out.println(f.getAbsolutePath());</div><div class="line"><span class="keyword">try</span> (</div><div class="line"> FileChannel inChannel = <span class="keyword">new</span> FileInputStream(f).getChannel();</div><div class="line"> FileChannel outChannel = <span class="keyword">new</span> FileOutputStream(<span class="string">"a.txt"</span>).getChannel()</div><div class="line">)</div><div class="line">{</div><div class="line"> MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, <span class="number">0</span>, f.length());</div><div class="line"> outChannel.write(buffer);</div><div class="line"> buffer.clear();</div><div class="line"> Charset charset = Charset.forName(<span class="string">"utf-8"</span>);</div><div class="line"> CharsetDecoder decoder = charset.newDecoder();</div><div class="line"> CharBuffer charBuffer = decoder.decode(buffer);</div><div class="line"> System.out.println(charBuffer);</div><div class="line">} <span class="keyword">catch</span> (FileNotFoundException e) {</div><div class="line"> e.printStackTrace();</div><div class="line">} <span class="keyword">catch</span> (IOException e) {</div><div class="line"> e.printStackTrace();</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="Charset的功能和用法"><a href="#Charset的功能和用法" class="headerlink" title="Charset的功能和用法"></a>Charset的功能和用法</h3><p>Charset类处理字节序列和字符系列之间的转换关系,该类包含了用于创建解码器和编码器的方法,还提供了Carset所支持字符集的方法,Charset类是不可变的<br>Carset了提供了一个availableCharsets()静态方法获取当前JDK所支持所有的字符集。<br>Charset类提供将ByteBuffer转换成CharBuffer的功能</p>
<blockquote>
<ol>
<li>CharBuffer decode(ByteBuffer bb): 将ByteBuffer中的字节序列转换成字符序列</li>
<li>ByteBuffer encode(CharBuffer cb): 将CharBuffer中字符序列转换成字节序列</li>
<li>ByteBuffer encode(String str):将字符序列转换成字节序列的便捷方法</li>
</ol>
</blockquote>
<h3 id="FileLock的功能和用法"><a href="#FileLock的功能和用法" class="headerlink" title="FileLock的功能和用法"></a>FileLock的功能和用法</h3><p>如果多个运行程序需要并发修改同一个文件,使用文件锁可以有效的阻止多个进行并发修改同一个文件。文件锁控制文件的全部或部分字节的访问。在NIO中,Java提供了FileLock来支持文件锁定功能,在FileChannel中提供的lock()/tryLock()方法可以获得文件锁FileLock对象,从而锁定文件。lock()和tryLock()方法的区别: lock()试图锁定文件,如果无法得到文件锁,程序一直阻塞。而tryLock()尝试锁定文件,它将直接返回而不阻塞。如故宫FileChannel只想锁住文件的部分内容而不是全部内容,则可以使用如下lock()或tryLock()方法:</p>
<blockquote>
<ol>
<li>lock(long position, long size, boolean shared):对文件从position开始,长度size的内容加锁,阻塞式</li>
<li>tryLock(long position, long size, boolean shared):对文件从position开始,长度size的内容加锁,非阻塞式</li>
</ol>
</blockquote>
<!--### NIO.2的文件IO和文件系统-->
<!--### 通过NIO.2监控文件变化-->
<!--### 通过NIO.2访问、修改文件属性-->
<h2 id="多线程"><a href="#多线程" class="headerlink" title="多线程"></a>多线程</h2><p>几乎所有的操作系统都支持进程的概念,所有运行中的任务通常对应一个进程。进程是处于运行过程中的程序,并具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单元。线程被称作轻量级进程,是进程的执行单元,就像进程在操作系统中的地位一样。线程在程序中是独立的、并发的执行流。 当进程被初始化后,主线程就被创建。<br>线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有父进程。线程可以拥有自己的堆栈、程序计数器和局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程的全部资源。<br>线程可以与其他线程共享父进程的共享变量及部分环境,相互之间可以协同来完成进程的任务<br>线程是独立运行的,它并不知道进程中是否还有其他线程的存在。线程的执行是抢占式的,也就是说,当前运行的线程在任何时候都可能被挂起,以便另一个线程可以运行。<br>一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发。<br>简而言之,一个程序运行后至少有一个进程,一个进程可以包含多个线程,但至少包含一个线程。</p>
<p>多线程编程具有如下几个优点:</p>
<blockquote>
<ol>
<li>进程之间不能共享内存,但线程之间共享内存非常容易</li>
<li>系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务比并发多进程的效率高</li>
<li>Java语言内置了多线程功能支持,而不是单纯地作为地城操作系统的调度方式,从而简化了Java的多线程编程。</li>
</ol>
</blockquote>
<h3 id="两种创建线程的方式"><a href="#两种创建线程的方式" class="headerlink" title="两种创建线程的方式"></a>两种创建线程的方式</h3><p>Java使用Thread类代表线程,所有的线程对象都必须是Thread类或者其子类的实例。<br>继承Thread类创建线程类:</p>
<blockquote>
<ol>
<li>定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务。run()方法称为线程执行体</li>
<li>创建Thread子类的实例</li>
<li>调用线程对象的start()方法启动</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FirstThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> i;</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">for</span> ( ; i < <span class="number">100</span>; i++) {</div><div class="line"> <span class="comment">//getName()返回当前线程的名字</span></div><div class="line"> System.out.println(getName() + <span class="string">" "</span> + i);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div 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>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" "</span> + i);</div><div class="line"></div><div class="line"> <span class="keyword">if</span> (i == <span class="number">20</span>) {</div><div class="line"> <span class="keyword">new</span> FirstThread().start();</div><div class="line"> <span class="keyword">new</span> FirstThread().start();</div><div class="line"> }</div><div class="line"></div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>实现Runnable接口创建线程类</p>
<blockquote>
<ol>
<li>定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体</li>
<li>创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SecondThread</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>{</div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> i;</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">for</span> ( ; i < <span class="number">100</span>; i++) {</div><div class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" "</span> + i);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div 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>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" "</span> + i);</div><div class="line"> <span class="keyword">if</span> (i == <span class="number">20</span>) {</div><div class="line"> SecondThread st = <span class="keyword">new</span> SecondThread();</div><div class="line"> <span class="keyword">new</span> Thread(st, <span class="string">"新线程1"</span>).start();</div><div class="line"> <span class="keyword">new</span> Thread(st, <span class="string">"新线程2"</span>).start();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>使用Callable和Future创建线程:<br>Java5开始,提供了Callable接口,Callable接口提供call()方法作为线程执行提,但call()比run()方法功能更强大。一、call()方法可以有返回值; 二、call()方法可以声明抛出异常; 如何使用Callable接口呢? Java5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供一个FutureTask实现类,该实现类实现Future接口和Runnable接口,可以作为Thread类的target。<br>在Future接口里定义了如下几个公共方法控制它关联的Callable任务:</p>
<blockquote>
<ol>
<li>boolean cancel(boolean mayInterruptIfRunning):视图取消该Future里关联的Callable任务</li>
<li>V get(): 返回Callable任务里call()方法的返回值。调用该方法将阻塞程序,必须等到子线程结束后才会得到返回值</li>
<li>V get(long timeout, TimeUnit unit):返回Callable任务里call()方法的返回值。该方法最多让程序阻塞timeout和unit指定的时间,如果指定时间内没有返回值,将抛出TimeoutException异常</li>
<li>boolean isCancelled(): 如果Callable任务正常完成前被取消,返回true</li>
<li>boolean isDone(): 如果Callable任务已完成,则返回true</li>
</ol>
</blockquote>
<p>创建并启动有返回值的线程的步骤如下</p>
<blockquote>
<ol>
<li>创阿金Callable接口的实现类,并实现call()方法,该call()方法作为线程执行体,且call()方法有返回值,在创建Callble实现类的实例。Java8可以直接使用Lambda表示式创建Callable对象 </li>
<li>使用FutureTask类包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值</li>
<li>使用FutureTask对象作为Thread对象的target创建并启动新线程</li>
<li>调用FutureTask对象的get()方法获得子线程执行结束后的返回值</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThirdThread</span> </span>{</div><div class="line"></div><div 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>{</div><div class="line"></div><div class="line"> ThirdThread rt = <span class="keyword">new</span> ThirdThread();</div><div class="line"> FutureTask<Integer> task = <span class="keyword">new</span> FutureTask<Integer>((Callable<Integer>)() -> {</div><div class="line"> <span class="keyword">int</span> i = <span class="number">0</span>;</div><div class="line"> <span class="keyword">for</span> ( ; i < <span class="number">100</span>; i++) {</div><div class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">"的循环变量i的值"</span> + i);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> i;</div><div class="line"> });</div><div class="line"></div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">"的循环变量i的值"</span> + i);</div><div class="line"> <span class="keyword">if</span> (i == <span class="number">20</span>) {</div><div class="line"> <span class="keyword">new</span> Thread(task, <span class="string">"有返回值的线程"</span>).start();</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> System.out.println(<span class="string">"子线程的返回值:"</span> + task.get());</div><div class="line"> } <span class="keyword">catch</span> (Exception e) {</div><div class="line"> e.printStackTrace();</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>采用继承Thread类的方式创建多线程的优缺点:</p>
<blockquote>
<ol>
<li>缺点: 继承了Thread类,无法继承其他父类</li>
<li>优点: 编写简单,如果访问当前线程直接使用this即可</li>
</ol>
</blockquote>
<p>采用实现Runnable、Callable接口的方式创建多线程的优缺点:</p>
<blockquote>
<ol>
<li>优点: 线程只实现了接口,还可以继承其他类</li>
<li>优点: 多线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的</li>
<li>缺点: 编程稍稍复杂,如果需要访问当前线程则必须Thread.currentThread()方法</li>
</ol>
</blockquote>
<!--### 线程的run()方法和start()方法的区别与联系-->
<h3 id="线程的生命周期"><a href="#线程的生命周期" class="headerlink" title="线程的生命周期"></a>线程的生命周期</h3><p>线程的声明周期:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Dead)5中状态。</p>
<blockquote>
<ol>
<li>新建: 当程序使用new关键字创建线程后,该线程处于新建状态</li>
<li>就绪: 当线程对象调用start()方法后,该线程处于就绪状态,处于这个状态的线程并没有运行,只是表示该线程可以运行了。何时运行? 取决JVM里线程调度器的调度</li>
<li>运行: 处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则线程处于运行状态。</li>
<li>阻塞: 当运行状态的线程发生了变化,会进入阻塞状态。 当阻塞状态解除后,进入就绪状态</li>
<li>死亡: 当线程执行完毕或者抛出未捕获的异常,或者调用该线程的stop()方法结束线程,就进入死亡状态</li>
</ol>
</blockquote>
<p>当发生如下情况,线程将会进入阻塞状态</p>
<blockquote>
<ol>
<li>线程调用sleep()方法,主动放弃所占有的处理器资源</li>
<li>线程调用了一个阻塞时IO方法,在该方法返回之前,该线程被阻塞</li>
<li>线程试图获得一个同步监视器,但该监视器正被其他线程持有。</li>
<li>线程正在等待某个通知</li>
<li>程序调用了线程的suspend()方法将该线程挂起。这个方法容易导致死锁。</li>
</ol>
</blockquote>
<p><img src="http://opbae2xuz.bkt.clouddn.com/images/bhlin/2017-06-12-02.png" alt=""></p>
<h3 id="控制线程的常用方法"><a href="#控制线程的常用方法" class="headerlink" title="控制线程的常用方法"></a>控制线程的常用方法</h3><p>join线程: Thread提供让一个线程等待另一个线程完成的方法——join()方法。当程序执行流中调用其他线程的join()方法,调用线程将被阻塞,知道join()方法加入的join线程执行完毕。join()方法有如下三种重载形式:</p>
<blockquote>
<ol>
<li>join(): 等待join的线程执行完成</li>
<li>join(long millis):等待join的线程最大时间是millis毫秒</li>
<li>join(long millis, int nanos): 等待join的线程时间最长为millis毫秒加nanos毫微秒</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JoinThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">JoinThread</span><span class="params">(String name)</span> </span>{</div><div class="line"> <span class="keyword">super</span>(name);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> System.out.println(getName() + <span class="string">" "</span> + i);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="keyword">new</span> JoinThread(<span class="string">"新线程"</span>).start();</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> <span class="keyword">if</span> (i == <span class="number">20</span>) {</div><div class="line"> JoinThread jt = <span class="keyword">new</span> JoinThread(<span class="string">"被Join的线程"</span>);</div><div class="line"> jt.start();</div><div class="line"> <span class="comment">//必须等待jt执行结束才会向下执行</span></div><div class="line"> jt.join();</div><div class="line"> }</div><div class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" "</span> + i);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>后台线程:有一种线程,他是在后台运行,他的任务是为其他的线程提供服务; JVM的垃圾回收线程就是典型的后台线程。后台线程有个特征: 所有前台线程都死亡,后台线程会自动死亡;调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程。通过isDaemon()方法用于判断指定线程是否是后台线程。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DaemonThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> System.out.println(getName() + <span class="string">" "</span> + i);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> DaemonThread t = <span class="keyword">new</span> DaemonThread();</div><div class="line"> <span class="comment">//将此线程设置为后台线程</span></div><div class="line"> t.setDaemon(<span class="keyword">true</span>);</div><div class="line"> t.start();</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" "</span> + i);</div><div class="line"> }</div><div class="line"> <span class="comment">//前台线程(main线程)结束</span></div><div class="line"> <span class="comment">//后台线程也随之结束</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>线程睡眠:如果要线程暂停一段时间并进入阻塞状态,则可以调用静态方法sleep()方法实现。sleep()方法有两种重载形式:</p>
<blockquote>
<ol>
<li>static void sleep(long millis): 让当前执行的线程暂停millis毫秒,并进入阻塞状态</li>
<li>static void sleep(long millis, int nanos):让当前执行的线程暂停millis毫秒加nanos毫微秒,并进入阻塞状态</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div 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 class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">100</span>; i++) {</div><div class="line"> System.out.println(<span class="string">"当前时间: "</span> + <span class="keyword">new</span> Date());</div><div class="line"> <span class="comment">//当前线程暂停1s</span></div><div class="line"> Thread.sleep(<span class="number">1000</span>);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>线程让步: yield()与sleep()方法相似,但是yield()方法可以让 当前线程暂停一下,不会进入阻塞状态,转而进入就绪状态。当某个线程调用yield()方法让出CPU,只有优先级比它高的或者同等级的才会获得执行机会。</p>
<p>改变线程优先级: 每个线程都有一定的优先级,优先级高的获得比较多的执行机会。每个线程的优先级默认与创建他的父线程的优先级相同。Thread类提供了setPriority(int new Priority)、getPriority()方法来设置获取指定线程的优先级,范围是1~10;也可以使用静态常量设置:</p>
<blockquote>
<ol>
<li>MAX_PRIORITY: 其值是10</li>
<li>MIN_PRIORITY: 其值是1</li>
<li>NORM_PRIORITY: 其值是5</li>
</ol>
</blockquote>
<h3 id="线程同步的概念和必要性"><a href="#线程同步的概念和必要性" class="headerlink" title="线程同步的概念和必要性"></a>线程同步的概念和必要性</h3><p>当多个线程访问同一个数据时,容易出现线程安全问题。为了解决这个问题,Java的多线程支持引入了同步监视器来解决这个问题。</p>
<h3 id="使用synchronized控制线程同步"><a href="#使用synchronized控制线程同步" class="headerlink" title="使用synchronized控制线程同步"></a>使用synchronized控制线程同步</h3><p>使用synchronized同步代码块的语法的语法格式如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">draw</span><span class="params">(<span class="keyword">double</span> drawAmount)</span> </span>{</div><div class="line"> <span class="keyword">synchronized</span>(obj) {</div><div class="line"> <span class="comment">//...</span></div><div class="line"> <span class="comment">//此处的代码就是同步代码块</span></div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">> 任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完后,该线程会释放对该同步监视器的锁定。</div></pre></td></tr></table></figure></p>
<p>与同步代码块对应的还有同步方法,同步方法使用synchronized关键字来修饰某个方法,则该方法称为同步方法。通过使用同步方法可以非常方便的实现线程安全的类,线程安全的类具有如下特征:</p>
<blockquote>
<ol>
<li>该类的对象可以被多个线程安全访问</li>
<li>每个线程调用对象的任一方法之后都将得到正确结果</li>
<li>每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//这个就是同步方法</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">draw</span><span class="params">(<span class="keyword">double</span> drawAmount)</span> </span>{</div><div class="line"> <span class="comment">//....</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>上面的方法使用了synchronized关键字修饰,把该方法变成同步方法,该同步方法的同步监视器视是this,因此任意时刻只能有一个线程进入draw方法进行操作。</p>
<p>任何线程进入同步代码块、同步方法之前,必须先获得对同步监视的锁定,何时释放对同步监视器的锁定呢? 程序无法显示释放对同步监视器的锁定,线程会在如下几种情况释放对同步监视器的锁定:</p>
<blockquote>
<ol>
<li>当前线程的同步方法、同步代码块执行结束</li>
<li>当前线程在同步方法、同步代码块遇到break、return终止执行</li>
<li>当前线程在同步代码块、同步方法出现了未处理的Erro或Exception,导致了该代码块、该方法异常结束</li>
<li>当前线程执行同步方法或代码块时,程序执行了同步监视器对象wait()方法,则当前线程暂停。并释放同步监视器</li>
<li>线程执行同步方法、代码块时,程序调用了Thread.sleep()/Thread.yield()方法来暂停当前线程的执行,当前线程不会释放同步监视器</li>
<li>线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放同步监视器。</li>
</ol>
</blockquote>
<h3 id="使用Lock对象控制线程同步"><a href="#使用Lock对象控制线程同步" class="headerlink" title="使用Lock对象控制线程同步"></a>使用Lock对象控制线程同步</h3><p>Java5开始,Java提供了一种功能更强大的线程同步机制——通过显示定义同步锁来实现同步,同步锁有Lock对象充当。Lock、ReadWriteLock(读写锁)是Java5提供的两个根接口,并未Lock提供了ReentrantLock(可重入锁)实现类,为ReadWriteLock提供了ReentrantReadWriteLock实现类。<br>在实现线程安全的控制中,比较常用的是ReentrantLock(可重入锁).使用Lock对象可以显示的加锁、释放锁。通常使用ReentrantLock的代码格式如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">X</span> </span>{</div><div class="line"> <span class="comment">//定义锁对象</span></div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">new</span> ReentrantLock();</div><div class="line"> <span class="comment">//定义需要保证线程安全的方法</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">m</span><span class="params">()</span> </span>{</div><div class="line"> <span class="comment">//加锁</span></div><div class="line"> lock.lock();</div><div class="line"> <span class="keyword">try</span>{</div><div class="line"> <span class="comment">//需要保证线程安全的代码</span></div><div class="line"> }<span class="keyword">finally</span> {</div><div class="line"> <span class="comment">//使用finally块来保证释放锁</span></div><div class="line"> lock.unlock();</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>ReentrantLock锁具有可重入性,也就是说,一个线程可以对已被加锁的ReentrantLock锁再次加锁,ReentrantLock对象会维持一个计数器来追踪lock()方法的嵌套使用。线程每次调用lock()加锁后,必须显示调用unlock()来释放锁</p>
<h3 id="使用Object提供的方法实现线程通信"><a href="#使用Object提供的方法实现线程通信" class="headerlink" title="使用Object提供的方法实现线程通信"></a>使用Object提供的方法实现线程通信</h3><p>当线程在系统内运行时,线程的调度具有一定的透明性,程序通常无法准去控制线程的轮换执行,但Java也提供了一些机制来保证线程协调运行。为了实现这种功能,Object类提供了wait()、notify()、notifyAll()三个方法,但这三个方法必须有同步监视器对象来调用:同步方法或者同步代码块。</p>
<blockquote>
<ol>
<li>wait(): 导致当前线程等待,知道其他线程调用同步监视器的notify()活notifyAll()方法来唤醒该线程。该wait(): 方法有三种形式——无时间参数、带毫秒参数、带毫秒和毫微妙参数</li>
<li>notify():唤醒再次同步监视器上等待的单个线程,如果所有线程都在同步监视器上等待,则会选择唤醒其中一个线程。选择是任意性的。</li>
<li>notifyAll(): 唤醒在此同步监视器上等待的所有线程。</li>
</ol>
</blockquote>
<h3 id="使用Condition-条件变量-实现线程通信"><a href="#使用Condition-条件变量-实现线程通信" class="headerlink" title="使用Condition(条件变量)实现线程通信"></a>使用Condition(条件变量)实现线程通信</h3><p>如果程序没有使用synchronized关键字保证同步,就无法使用wait()、notify()、notifyAll()方法。当使用Lock对象保证同步,Java提供Condition类保持协调,Condition可以让那些已经得到Lock对象却无法继续执行的线程使用Lock对象,COndition对象也可以唤醒其他处于等待的线程。<br>Condition类提供了如下三个方法:</p>
<blockquote>
<ol>
<li>await(): 类似隐式同步监视器的wait()方法,导致当前线程等待,知道其他线程调用该Condition的signal或者signalAll方法</li>
<li>signal():唤醒在此Lock对象上等待的单个线程。果所有线程都在等待,则会选择唤醒其中一个线程。选择是任意性的。</li>
<li>signalAll():唤醒在此等待的所有线程。</li>
</ol>
</blockquote>
<p>Java使用ThreadGroup来表示线程组,可以对一批线程进行分类管理,Java程序允许直接对线程组进行控制。默认情况下,子线程和创建他的父线程处于同一个线程组内。<br>一旦某个线程加入指定的线程组之后,该线程一直属于该线程组,直到线程死亡,线程中途不能改变它所属的线程组。<br>Thread类提供了如下构造器设置新创建的线程属于哪个线程组:</p>
<blockquote>
<ol>
<li>Thread(ThreadGroup group, Runnable target)</li>
<li>Thread(ThreadGroup group, Runnable target, String name):以target的run()方法作为线程执行体创建新县城,该线程属于group线程组,线程名name</li>
<li>Threag(ThreadGroup group, String name): 创建新线程,线程名name,属于group线程组</li>
</ol>
</blockquote>
<p>ThreadGroup类提供了两个简单的构造器创建实例</p>
<blockquote>
<ol>
<li>ThreadGroup(String name):以指定线程组名字创建</li>
<li>ThreadGroup(ThreadGroup parent, String name): 以指定名字、指定父类创建一个新线程组</li>
</ol>
</blockquote>
<p>ThreadGroup类提供了如下几个常用方法:</p>
<blockquote>
<ol>
<li>int activeCount(): 返回线程组中活动线程数目 </li>
<li>interrupt(): 中断此线程中所有线层</li>
<li>isDaemon():判断线程组中是否是后台线程组</li>
<li>setDaemon(boolean daemon): 把该线程组设置成后台线程组</li>
<li>setMaxPriority(int pri): 设置线程组的最高优先级</li>
</ol>
</blockquote>
<h3 id="线程池的功能和用法"><a href="#线程池的功能和用法" class="headerlink" title="线程池的功能和用法"></a>线程池的功能和用法</h3><p>系统启动一个新线程的成本较高,当程序中需要创建大量生存期短暂的线程时,应该考虑使用线程池;与数据库连接池类似,线程池在系统启动时创建大量空闲的线程,程序将一个Runnable对象或Callable对象传给线程池,线程池会启动一个线程执行run()或者call()方法,执行结束后,线程不会死亡,返回线程池称为空闲状态,等待执行下一个Runnable对象的run()或call()方法;</p>
<p>Java5以前,开发者必须手动实现自己的线程池;Java5开始,Java内建支持线程池,Java5新增了一个Executors工厂类产生线程池,该工厂类包含如下几个静态工厂方法创建线程池:</p>
<blockquote>
<ol>
<li>newCachedThreadPool(): 创建一个具有缓存功能的线程池,系统根据需要创建线程,这些线程将会被缓存在线程池中</li>
<li>newFixedThreadPool(int nThreads): 创建一个可重用的、具有固定线程数的线程池</li>
<li>newSingleThreadExecutor():创建一个只有单线程的线程池,相当于newFixedThreadPool(1)</li>
<li>newScheduledThreadPool(int corePolSize):创建具有指定线程数(corePolSize)的线程池,他可以在指定延迟后执行线程任务。</li>
<li>newSingleThreadScheduleExecutor(): 创建只有一个线程的线程池,他可以在指定延迟后执行线程任务。</li>
<li>ExecutorService newWorkStealingPool(int parallelism):创建持有足够的线程的线程池来支持给定的并行级别,该方法还会使用多个队列减少竞争</li>
<li>ExecutorService newWorkStealingPool():该方法是前一个的简化版本</li>
</ol>
</blockquote>
<p>上面7个方法前三个返回一个ExecutorService对象,该对象代表一个线程池;中间两个方法返回一个ScheduledExecutorService线程池,它是ExecutorService的子类,它可以指定延迟后执行线程任务。最后两个则是Java8新增的,这两个方法可充分利用多CPU并的能力。这两个方法生成work stealing池,都相当于后台线程池,如果所有的前台线程都死亡,work stealing池中的线程会自动死亡。<br>ExecutorService提供了如下三个方法:</p>
<blockquote>
<ol>
<li>Future<?> submit(Runnable task):讲一个Runnable对象提交给指定的线程池。其中Future对象代表Runnable任务的返回值。可以调用Future的isDone()、isCancelled()方法来获得Runnable对象的执行状态</li>
<li><t>Future<t> submit(Runnable task, T reslut): 将一个Runnable对象交给线程池,result显示指定线程执行结束返回值</t></t></li>
<li><t>Future<t> submit(Callable<t> task): 将一个Callable对象提交给线程池,Future是Callable的返回值</t></t></t></li>
</ol>
</blockquote>
<p>ScheduleExecutorService代表可在指定延迟后或周期性执行线程任务的线程池,它提供了如下4个方法:</p>
<blockquote>
<ol>
<li>ScheduleFuture<v> schedule(Callable<v> callable, long delay, TimeUnit unit):指定callable任务将在delay延迟后执行</v></v></li>
<li>ScheduleFuture<v> schedule(Runnable command, long delay, TimeUnit unit): 指定command任务将在delay延迟后执行</v></li>
<li>ScheduleFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit): 指定command任务将在delay延迟后执行,而且以设定频率重复执行</li>
<li>ScheduleFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):创建并执行一个给定初始延迟后首次启用的定期操作,随后在每一次执行终止和下一次执行开始之前都存在给定延迟。如果任务遇到异常,就会取消后续执行;否在,只能通过程序来显示取消或终止该任务</li>
</ol>
</blockquote>
<!--### Java8增强的ForkJoinPool-->
<h3 id="ThreadLocal类的功能和用法"><a href="#ThreadLocal类的功能和用法" class="headerlink" title="ThreadLocal类的功能和用法"></a>ThreadLocal类的功能和用法</h3><p>ThreadLocal代表一个线程局部变量,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副本,从而避免线程安全问题。ThreadLocal类用法非常简单,他只提供了如下三个public方法:</p>
<blockquote>
<ol>
<li>T get(): 返回此线程局部变量中当前线程副本中的值</li>
<li>void remove(): 删除此线程局部变量中当前线程的值</li>
<li>void set(T value): 设置此线程局部变量中当前线程副本中的值</li>
</ol>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>{</div><div class="line"> <span class="comment">/** 顶一个ThreadLocal类型的变量,该变量将是一个线程局部变量,每个线程都会保留该变量的一个副本 */</span></div><div class="line"> <span class="keyword">private</span> ThreadLocal<String> name = <span class="keyword">new</span> ThreadLocal<>();</div><div class="line"> <span class="comment">//定义一个初始化name成员变量的构造器</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Account</span><span class="params">(String str)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.name.set(str);</div><div class="line"> <span class="comment">//下面代码用于访问当前线程的name副本的值</span></div><div class="line"> System.out.println(<span class="keyword">this</span>.name.get());</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">getName</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> name.get();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setName</span><span class="params">(String name)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.name.set(name);</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyTest</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>{</div><div class="line"> <span class="comment">//定义一个Account类型的成员变量</span></div><div class="line"> <span class="keyword">private</span> Account account;</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">MyTest</span><span class="params">(Account account, String name)</span> </span>{</div><div class="line"> <span class="keyword">super</span>(name);</div><div class="line"></div><div class="line"> <span class="keyword">this</span>.account = account;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">10</span> ; i++) {</div><div class="line"> <span class="keyword">if</span> (i == <span class="number">6</span>) {</div><div class="line"> account.setName(getName());</div><div class="line"> }</div><div class="line"> System.out.println(account.getName() + <span class="string">"账户的i值: "</span> + i);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadLocalTest</span> </span>{</div><div 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>{</div><div class="line"> Account at = <span class="keyword">new</span> Account(<span class="string">"初始名"</span>);</div><div class="line"> <span class="keyword">new</span> MyTest(at, <span class="string">"线程甲"</span>).start();</div><div class="line"> <span class="keyword">new</span> MyTest(at, <span class="string">"线程乙"</span>).start();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<!--### 使用线程安全的集合类-->
]]></content>
<categories>
<category> Java </category>
</categories>