-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
1696 lines (1696 loc) · 461 KB
/
search.xml
File metadata and controls
1696 lines (1696 loc) · 461 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
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[Java中的volatile关键字]]></title>
<url>%2F2020%2F04%2F12%2FJava%E4%B8%AD%E7%9A%84volatile%E5%85%B3%E9%94%AE%E5%AD%97%2F</url>
<content type="text"><![CDATA[Volatile [ˈvɑːlətl],中文解释:反复无常的,易变的,不稳定的。volatile的本意是告诉编译器,此变量的值是易变的,每次读写该变量的值时务必从该变量的内存地址中读取或写入,不能为了效率使用对一个“临时”变量的读写来代替对该变量的直接读写。编译器看到了volatile关键字,就一定会生成内存访问指令,每次读写该变量就一定会执行内存访问指令直接读写该变量。若是没有volatile关键字,编译器为了效率,只会在循环开始前使用读内存指令将该变量读到寄存器中,之后在循环内都是用寄存器访问指令来操作这个“临时”变量,在循环结束后再使用内存写指令将这个寄存器中的“临时”变量写回内存。在这个过程中,如果内存中的这个变量被别的因素(其他线程、中断函数、信号处理函数、DMA控制器、其他硬件设备)所改变了,就产生数据不一致的问题。 volatile关键字在用C语言编写嵌入式软件里面用得很多,不使用volatile关键字的代码比使用volatile关键字的代码效率要高一些,但就无法保证数据的一致性。在Java中,volatile 会确保我们对于这个变量的读取和写入,都一定会同步到主内存里,而不是从 Cache 里面读取。 Case 11234567891011121314151617181920212223242526272829303132333435public class VolatileTest { private static volatile int COUNTER = 0; public static void main(String[] args) { new ChangeListener().start(); new ChangeMaker().start(); } static class ChangeListener extends Thread { @Override public void run() { int threadValue = COUNTER; while ( threadValue < 5){ if( threadValue!= COUNTER){ System.out.println("Got Change for COUNTER : " + COUNTER + ""); threadValue= COUNTER; } } } } static class ChangeMaker extends Thread{ @Override public void run() { int threadValue = COUNTER; while (COUNTER <5){ System.out.println("Incrementing COUNTER to : " + (threadValue+1) + ""); COUNTER = ++threadValue; try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }} 在这个程序里,我们先定义了一个 volatile 的 int 类型的变量,COUNTER。然后,我们分别启动了两个单独的线程,一个线程我们叫 ChangeListener。 ChangeListener 这个线程运行的任务很简单。它先取到 COUNTER 当前的值,然后一直监听着这个 COUNTER 的值。一旦 COUNTER 的值发生了变化,就把新的值通过 println 打印出来。直到 COUNTER 的值达到 5 为止。这个监听的过程,通过一个永不停歇的 while 循环的忙等待来实现。 另外一个 ChangeMaker 线程运行的任务同样很简单。它同样是取到 COUNTER 的值,在 COUNTER 小于 5 的时候,每隔 500 毫秒,就让 COUNTER 自增 1。在自增之前,通过 println 方法把自增后的值打印出来。 最后,在 main 函数里,我们分别启动这两个线程,来看一看这个程序的执行情况。 12345678910Incrementing COUNTER to : 1Got Change for COUNTER : 1Incrementing COUNTER to : 2Got Change for COUNTER : 2Incrementing COUNTER to : 3Got Change for COUNTER : 3Incrementing COUNTER to : 4Got Change for COUNTER : 4Incrementing COUNTER to : 5Got Change for COUNTER : 5 程序的输出结果并不让人意外。ChangeMaker 函数会一次一次将 COUNTER 从 0 增加到 5。因为这个自增是每 500 毫秒一次,而 ChangeListener 去监听 COUNTER 是忙等待的,所以每一次自增都会被 ChangeListener 监听到,然后对应的结果就会被打印出来。 终极原因是:volatile保证所有数据的读和写都来自主内存。ChangeMaker 和 ChangeListener 之间,获取到的 COUNTER 值就是一样的。 Case 2如果我们把上面的程序小小地修改一行代码,把我们定义 COUNTER 这个变量的时候,设置的 volatile 关键字给去掉,重新运行后发现:12345Incrementing COUNTER to : 1Incrementing COUNTER to : 2Incrementing COUNTER to : 3Incrementing COUNTER to : 4Incrementing COUNTER to : 5 这个时候,ChangeListener 又是一个忙等待的循环,它尝试不停地获取 COUNTER 的值,这样就会从当前线程的“Cache”里面获取。于是,这个线程就没有时间从主内存里面同步更新后的 COUNTER 值。这样,它就一直卡死在 COUNTER=0 的死循环上了。 Case 3再对程序做一个小的修改。我们不再让 ChangeListener 进行完全的忙等待,而是在 while 循环里面,Sleep 5 毫秒,看看会发生什么情况。123456789101112131415static class ChangeListener extends Thread { @Override public void run() { int threadValue = COUNTER; while ( threadValue < 5){ if( threadValue!= COUNTER){ System.out.println("Sleep 5ms, Got Change for COUNTER : " + COUNTER + ""); threadValue= COUNTER; } try { Thread.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } }} 运行结果:12345678910Incrementing COUNTER to : 1Sleep 5ms, Got Change for COUNTER : 1Incrementing COUNTER to : 2Sleep 5ms, Got Change for COUNTER : 2Incrementing COUNTER to : 3Sleep 5ms, Got Change for COUNTER : 3Incrementing COUNTER to : 4Sleep 5ms, Got Change for COUNTER : 4Incrementing COUNTER to : 5Sleep 5ms, Got Change for COUNTER : 5 解释:虽然没有使用 volatile 关键字强制保证数据的一致性,但是短短 5ms 的 Thead.Sleep 的线程会让出CPU,线程被唤醒后才会去重新加载变量。它也就有机会把最新的数据从主内存同步到自己的高速缓存里面了。于是,ChangeListener 在下一次查看 COUNTER 值的时候,就能看到 ChangeMaker 造成的变化了。 虽然 JMM 只是 Java 虚拟机这个进程级虚拟机里的一个隔离了硬件实现的虚拟机内的抽象模型,但是这个内存模型,和计算机组成里的 CPU、高速缓存和主内存组合在一起的硬件体系非常相似。以上就是一个很好的“缓存同步”问题的示例。也就是说,如果我们的数据,在不同的线程或者 CPU 核里面去更新,因为不同的线程或 CPU 核有着各自的缓存,很有可能在 A 线程的更新,因为数据暂时不一致,在 B 线程里面是不可见的。]]></content>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[XOR异或运算在计算机中的应用]]></title>
<url>%2F2020%2F03%2F28%2FXOR%E5%BC%82%E6%88%96%E8%BF%90%E7%AE%97%E5%9C%A8%E8%AE%A1%E7%AE%97%E6%9C%BA%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8%2F</url>
<content type="text"><![CDATA[1. 什么是异或运算异或,英文为exclusive OR,缩写成xor。异或(xor)是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。 异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位,所以异或常被认作不进位加法。 真值表如下:|A|B|A XOR B|| — | — | — ||0|0|0||0|1|1||1|1|0||1|0|0| 2. 异或运算的特性 交换律:A⊕B = B⊕A 结合律:A⊕(B⊕C) = (A⊕B)⊕C 恒等律:X⊕0 = X 归零律:X⊕X = 0 自反律:A⊕B⊕B = A⊕(B⊕B) = A⊕0 = A 3. XOR在计算机中的应用3.1. 快速比较两值是否想等12345r = a xor bif r==0 print(“a == b”)else print(“a != b") 3.2. 检验一个数字中1的个数的奇偶(奇偶校验) 求10100001中1的个数1 ^ 0 ^ 1 ^ 0 ^ 0 ^ 0 ^ 0 ^ 1 = 1按位进行异或运算得到的结果等于1,结果为奇数,得到的结果为0,结果为偶数 3.3. 不使用中间变量,交换两个变量的值123a = a ^ b;b = a ^ b; //a ^ b ^ b = a ^ 0 = a;a = a ^ b; 3.4. 面试题:一个整型数组里除了一个数字之外,其他的数字都出现了两次,找出这个数字利用异或运算的归零律:X⊕X = 0 和恒等律:X⊕0 = X 对于数组{a, a, b, b, c, c, d},找出只出现了一次的数字da^a^b^b^c^c^d= 0^0^0^d= 0^d= d时间复杂度为O(N), 空间复杂度为O(1) 3.5. 密码学中对称加密解密利用异或运算的自反律: A⊕B⊕B = A⊕(B⊕B) = A⊕0 = A message XOR key // cipherTextcipherText XOR key // message明文 XOR 密钥 –> 密文密文 XOR 密钥 –> 明文 以上特性很好地对应了加密和解密的过程。所以目前很多加密算法都利用了异或算法的这个特性来做最后的密文打乱的工作。]]></content>
</entry>
<entry>
<title><![CDATA[Python实现一个计时功能的装饰器]]></title>
<url>%2F2020%2F03%2F01%2FPython%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E8%AE%A1%E6%97%B6%E5%8A%9F%E8%83%BD%E7%9A%84%E8%A3%85%E9%A5%B0%E5%99%A8%2F</url>
<content type="text"><![CDATA[下面的装饰器clock会打印函数的运行时间 123456789101112131415161718192021222324252627282930313233# descrbe.pyimport timeimport functoolsdef clock(func): """this is outer clock function""" @functools.wraps(func) # --> 4 def clocked(*args, **kwargs): # -- 1 """this is inner clocked function""" start_time = time.time() result = func(*args, **kwargs) # --> 2 time_cost = time.time() - start_time print(func.__name__ + " func time_cost -> {}".format(time_cost)) return result return clocked # --> 3@functools.lru_cache() # --> 5@clock # --> 6def fib(n): """this is fibonacci function""" return n if n < 2 else fib(n - 1) + fib(n - 2)if __name__ == "__main__": # 如果有 @functools.wraps(func) # --> 4 大多数情况下我们希望的输出是这样的 fib(1) # 输出 fib func time_cost -> 9.5367431640625e-07 print(fib.__name__) # 输出 fib print(fib.__doc__) # 输出 this is fibonacci function # 如果没有@functools.wraps(func) # --> 4 fib(1) # 输出 fib func time_cost -> 9.5367431640625e-07 print(fib.__name__) # 输出 clocked print(fib.__doc__) # 输出 this is inner clocked function 定义了一个内部函数clocked,它接受任意定位参数以及关键字参数。 这行代码可用,是因为clocked的闭包中包含了自由变量func。 返回内部的函数,取代被装饰的函数。 functools.wraps是标准库中拿来即用的装饰器之一,它的作用是协助构建行为良好的装饰器。如果不加functools.wraps(func), 会遮盖被装饰函数的__name__和__doc__属性, functools.lru_cache()是非常实用的装饰器,它实现了备忘(memoization)功能。这是一项优化技术,它把耗时的函数的结果保存起来,避免传入相同的参数时重复计算。LRU三个字母是“Least Recently Used”的缩写,表明缓存不会无限制增长,一段时间不用的缓存条目会被扔掉。这样就会显著提高程序的运行效率。lru_cache可以使用两个可选的参数来配置。它的方法签名是lru_cache(maxsize=128, typed=False)。maxsize参数指定存储多少个调用的结果。缓存满了之后,旧的结果会被扔掉,腾出空间。为了得到最佳性能,maxsize应该设为2的幂。typed参数如果设为True,把不同参数类型得到的结果分开保存,即把通常认为相等的浮点数和整数参数(如 1 和 1.0)区分开。顺便说一下,因为lru_cache使用字典存储结果,而且键根据调用时传入的定位参数和关键字参数创建,所以被lru_cache装饰的函数,它的所有参数都必须是可散列的。 实际上该处的工作原理如下: 1234@clock # --> 6def fib(n): """this is fibonacci function""" return n if n < 2 else fib(n - 1) + fib(n - 2) 等价于1fib = clock(fib) fib会作为func参数传给clock。然后,clock函数会返回clocked函数,Python解释器在背后会把 clocked赋值给fib。实际上如果我们没有添加`@functools.wraps(func)`,我们在ipython中导入describe进一步观察: 123In [1]: import describe In [2]: describe.fib.__name__ Out[2]: 'clocked' 所以,现在fib保存的是 clocked 函数的引用。自此之后,每次调用 fib(n),执行的都是 clocked(n)。clocked 大致做了下面几件事。(1) 记录初始时间 t0。(2) 调用原来的 fib 函数,保存结果。(3) 计算经过的时间。(4) 打印出来格式化收集的数据(5) 返回第 2 步保存的结果。这是装饰器的典型行为:把被装饰的函数替换成新函数,二者接受相同的参数,而且(通常)返回被装饰的函数本该返回的值,同时还会做些额外操作。]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[简谈Python3中的闭包]]></title>
<url>%2F2019%2F12%2F09%2F%E7%AE%80%E8%B0%88Python3%E4%B8%AD%E7%9A%84%E9%97%AD%E5%8C%85%2F</url>
<content type="text"><![CDATA[欢迎关注微信公众号:CodeWorks问题或建议,请公众号留言,欢迎非抬杠式讨论 闭包是指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。闭包(closure)是函数式编程的重要的语法结构。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。 当一个内嵌函数引用其外部作用域的变量,我们就会得到一个闭包. 总结一下,创建一个闭包必须满足以下几点: 必须有一个内嵌函数 内嵌函数必须引用外部函数中的变量 外部函数的返回值必须是内嵌函数 闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时虽然定义作用域不可用了,但仍能使用那些绑定。 闭包的概念难以掌握,下面通过示例进行理解。 假设我们要实现一个计算移动平均功能的代码,如何实现呢? 初学者可能会用类来实现,如示例1:123456789# 示例1class Averager(object): def __init__(self): self.series = [] def __call__(self, new_value): self.series.append(new_value) total = sum(self.series) return total/len(self.series) Average的实例是可调用对象:1234567>>> avg = Averager()>>> avg(10)10.0>>> avg(11)10.5>>> avg(12)11.0 下面使用函数式实现,如示例2:12345678910# 示例2def make_averager(): series = [] def averager(new_value): series.append(new_value) total = sum(series) return total/len(series) return averager 调用make_averager时,返回一个averager函数对象。每次调用averager时,该对象会把参数添加到series中,然后计算当前平均值,如下所示:1234567>>> avg = make_averager()>>> avg(10)10.0>>> avg(11)10.5>>> avg(12)11.0 以上两个示例有共通之处:调用Averager()或make_averager()得到一个可调用对象avg,该对象会更新历史值,然后计算当前均值。示例1中,avg是Averager的实例;实例2中是内部函数averager。不管怎样,我们都只需要调用avg(n),把n放入系列值series中,然后重新计算均值。 Averager()类的实例avg在哪里存储历史值很明显,但是第二个示例中的avg函数在哪里寻找series呢? 注意,series是make_averager函数的局部变量,因为那个函数的定义体中初始化了series = []。可是,调用avg(10)时,make_averager函数已经返回了,而他的本地作用域也一去不复返了。 在averager函数中,series是自由变量,指未在本地作用域中绑定的变量,图形化展示如下: averager的闭包延伸到那个函数的作用域之外,包含对自由变量series的绑定 我们可以审查返回的averager对象,发现Python在__code__属性(表示编译后的函数定义体)中保存局部变量和自由变量的名称,如下所示 12345# 审查make_averager创建的函数>>> avg.__code__.co_varnames('new_value', 'total')>>> avg.__code__.co_freevars('series',) series绑定在返回的avg函数的__closure__属性中。avg.__closure__中各个元素对应于avg.__code__.co_freevars中的一个名称。这些元素是cell对象,有个cell_content属性,保存着真正的值。这些属性的值如示例所示:123456>>> avg.__code__.co_freevars('series',)>>> avg.__closure__(<cell at 0x108b89828: list object at 0x108ae96c8>,)>>> avg.__closure__[0].cell_contents[10,11,12] 综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时虽然定义作用域不可用了,但仍能使用那些绑定。 稍微有点编程经验的人会看到,我们实现的计算移动平均值得方法实际上有很大的改进空间,在后面的介绍中会进行改进。]]></content>
<categories>
<category>Python3 进阶</category>
<category>Python3 闭包</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[简述Python中变量作用域的规则]]></title>
<url>%2F2019%2F12%2F07%2F%E7%AE%80%E8%BF%B0Python%E4%B8%AD%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E7%9A%84%E8%A7%84%E5%88%99%2F</url>
<content type="text"><![CDATA[介绍这一题目的目的是引出python中较为高级的话题—闭包和装饰器。 在下面的例子中,定义并测试一个函数,它读取两个变量的值:一个是局部变量a,是函数的参数; 另一个是变量b,这个函数没有定义它。 12345678910>>> def f1(a):... print(a)... print(b)...>>> f1(1)1Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in f1NameError: name 'b' is not defined 出现此结果完全在意料范围内,因为我们压根从来没有定义过变量b。如果我们在调用函数f1前定义变量b,那么不会存在问题,因为此处的b是全局变量。 1234>>> b=2>>> f1(1)12 下面来一个较为特殊的例子123456789101112>>> b=2>>> def f2(a):... print(a)... print(b)... b=3...>>> f2(1)1Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in f2UnboundLocalError: local variable 'b' referenced before assignment 可以看到在赋值之前,第二个print失败了,提示局部变量b在赋值前被引用了。 实际上,Python 编译函数的定义体时,它判断 b 是局部变量,因为在函数中给它赋值了。生成的字节码证实了这种判断(后面会讨论),Python 会尝试从本地环境获取 b。后面调用f2(1)时,f2的定义体会获取并打印局部变量 a 的值,但是尝试获取局部变量 b 的值时,发现 b 没有绑定值。 这是Python的设计选择:Python不要求声明变量类型,但是默认在函数定义体中赋值的变量是局部变量。如果在函数中赋值时想让解释器把 b 当成全局变量,要使用 global 声明:123456789101112131415161718192021>>> b=2>>> def f3(a):... global b... print(a)... print(b)... b=3...>>> f3(1)12>>> b3>>> f3(1)13>>> b=30>>> b30>>> f3(1)130 接下来,我们看一下f1和f2的字节码:123456789101112131415161718192021222324252627>>> dis(f1) 2 0 LOAD_GLOBAL 0 (print) >>> 1 2 LOAD_FAST 0 (a) >>> 2 4 CALL_FUNCTION 1 6 POP_TOP 3 8 LOAD_GLOBAL 0 (print) 10 LOAD_GLOBAL 1 (b) >>> 3 12 CALL_FUNCTION 1 14 POP_TOP 16 LOAD_CONST 0 (None) 18 RETURN_VALUE>>> dis(f2) 2 0 LOAD_GLOBAL 0 (print) 2 LOAD_FAST 0 (a) 4 CALL_FUNCTION 1 6 POP_TOP 3 8 LOAD_GLOBAL 0 (print) 10 LOAD_FAST 1 (b) >>> 4 12 CALL_FUNCTION 1 14 POP_TOP 4 16 LOAD_CONST 1 (3) 18 STORE_FAST 1 (b) 20 LOAD_CONST 0 (None) 22 RETURN_VALUE 对于f1函数的反汇编: LOAD_GLOBAL加载全局名称 print。 LOAD_FAST加载本地名称 a。 LOAD_GLOBAL加载全局名称 b。 对于f2函数的反汇编: LOAD_FAST加载本地名称 b。这表明,编译器把 b 视作局部变量,即使在后面才为 b 赋值。 对于函数f3的反汇编如下图所示,可以看到变量b为全局变量。 123456789101112131415>>> dis(f3) 3 0 LOAD_GLOBAL 0 (print) 2 LOAD_FAST 0 (a) 4 CALL_FUNCTION 1 6 POP_TOP 4 8 LOAD_GLOBAL 0 (print) 10 LOAD_GLOBAL 1 (b) >>> *** 12 CALL_FUNCTION 1 14 POP_TOP 5 16 LOAD_CONST 1 (3) 18 STORE_GLOBAL 1 (b) 20 LOAD_CONST 0 (None) 22 RETURN_VALUE 至此,我们可以基本了解python函数中变量作用域的问题,这将为后面介绍的闭包和装饰器的学习打下基础。]]></content>
<categories>
<category>Python3 进阶</category>
<category>Python3 闭包</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[用Emoji解释编程语言中的map、filter、reduce]]></title>
<url>%2F2019%2F11%2F27%2F%E7%94%A8Emoji%E8%A7%A3%E9%87%8A%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84map%E3%80%81filter%E3%80%81reduce%2F</url>
<content type="text"><![CDATA[12345678map(cook, [🌽, 🥔, 🐥, 🐮])===> [🍿, 🍟, 🍗, 🥩]filter(isMeat, [🍿, 🍟, 🍗, 🥩])===> [🍗, 🥩]reduce(afterEat, [🍿, 🍟, 🍗, 🥩])===> 💩]]></content>
<categories>
<category>Funny</category>
</categories>
<tags>
<tag>Funny</tag>
</tags>
</entry>
<entry>
<title><![CDATA[如何优雅滴在手机上跑Python代码]]></title>
<url>%2F2019%2F11%2F15%2F%E5%A6%82%E4%BD%95%E4%BC%98%E9%9B%85%E6%BB%B4%E5%9C%A8%E6%89%8B%E6%9C%BA%E4%B8%8A%E8%B7%91Python%E4%BB%A3%E7%A0%81%2F</url>
<content type="text"><![CDATA[本文重点围绕苹果设备,给大家描述一下小编自己如何在苹果移动设备上玩转Python代码的。按照推荐指数由高到低给大家推荐相关工具。 Pythonista与一般的python移动端软件不同,它是一款安装在手机上的Python IDE软件,推荐指数4.5星。扣掉的0.5星是因为软件略贵(9.99刀)。 该软件可以在苹果移动设备上提供几乎完整的Python开发环境,软件里有很多的demo程序,包括了游戏、数据处理、图片处理等。说实话,本人觉得光这些demo代码就值这个价格了。软件除了支持标准Python库以外,还支持很多常用的三方库,例如用于科学计算的Numpy和绘图包matplotlib、用于http请求的Requests等。另外,我们还可以在软件中写脚本调用手机接口,获得接切板、联系人、位置信息等,功能狠强大,等待盆友萌前去体验。 Python ai这款软件可以给4.4颗星,也是一款非常优秀的软件。说其优秀,体现在这么几点: 软件免费,没有广告,实在是情怀良心之作 离线IDE环境 支持很多科学计算的三方库 支持pip安装三方库 Pydriod就Python IDE来说,安卓端的软件跟苹果端软件相比还是差的挺多的。此处推荐Pydriod,给4颗星吧。貌似很多应用商店找不到,如果可以顺利FQ,推荐去Google Play下载。 再次强调一下,移动端Python IDE软件有很多,对于苹果端,小编大部分已经下载体验过,自认为还是很有发言权的,此处推荐的都是本地IDE的能如我法眼的Python软件。其实还有一些软件使用的是远程IDE,例如苹果和安卓端的PythonJam,体验还算OK,感兴趣大家可以下载尝试。]]></content>
<categories>
<category>Tools</category>
</categories>
<tags>
<tag>Tools</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python中如何优雅的使用assert断言]]></title>
<url>%2F2019%2F11%2F14%2FPython%E4%B8%AD%E5%A6%82%E4%BD%95%E4%BC%98%E9%9B%85%E7%9A%84%E4%BD%BF%E7%94%A8assert%E6%96%AD%E8%A8%80%2F</url>
<content type="text"><![CDATA[什么是assert断言 Assert statements are a convenient way to insert debugging assertions into a program 断言声明是用于程序调试的一个便捷方式。断言可以看做是一个debug工具,Python的实现也符合这个设计哲学,在Python中assert语句的执行是依赖于__debug__这个内置变量的,其默认值为True。当__debug__为True时,assert语句才会被执行。 对于一般的声明,assert expression等价于12if __debug__: if not expression: raise AssertionError assert可以同时声明两个个expression,例如assert expression1, expression2等价于12if __debug__: if not expression1: raise AssertionError(expression2) 如果执行脚本文件时加上-O参数, __debug__则为False 举一个例子,假设我们有一个脚本testAssert.py,内容为:12print(__debug__)assert 1 > 2 当使用python assert.py运行时,__debug__会输出True,assert 1 > 2语句会抛出AssertionError异常。 当使用python -O assert.py运行时,__debug__会输出False,assert 1 > 2语句由于没有执行不会报任何异常。 断言和异常的使用场景先说结论: 检查先验条件使用断言,检查后验条件使用异常 举个例子来说明一下,在开发中我们经常会遇到读取本地文件的场景。我们定义一个read_file方法。 123def read_file(path): assert is_instance(file_path, str) ... read_file函数要求在开始执行的时候满足一定条件:file_path必须是str类型,这个条件就是先验条件,如果不满足,就不能调用这个函数,如果真的出现了不满足条件的情况,证明代码中出现了bug,这时候我们就可以使用assert语句来对file_path的类型进行推断,提醒程序员修改代码,也可以使用if…raise…语句来实现assert,但是要繁琐很多。在很多优秀的Python项目中都会看到使用assert进行先验判断的情况,平时可以多多留意。 read_file函数在被调用执行后,依然需要满足一定条件,比如file_path所指定的文件需要是存在的,并且当前用户有权限读取该文件,这些条件称为后验条件,对于后验条件的检查,我们需要使用异常来处理。 123456def read_file(file_path): assert isinstance(file_path, str) if not check_exist(file_path): raise FileNotFoundError() if not has_privilege(file_path): raise PermissionError() 文件不存在和没有权限,这两种情况并不属于代码bug,是代码逻辑的一部分,上层代码捕获异常后可能会执行其他逻辑,因此我们不能接受这部分代码在生产环境中被忽略。并且,相比于assert语句只能抛出AssertionError,使用异常可以抛出更详细的错误,方便上层代码针对不同错误执行不同的逻辑。 参考链接]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[神经网络中为什么不能将权重初始值设置为一样的值]]></title>
<url>%2F2019%2F11%2F04%2F%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E4%B8%AD%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E8%83%BD%E5%B0%86%E6%9D%83%E9%87%8D%E5%88%9D%E5%A7%8B%E5%80%BC%E8%AE%BE%E7%BD%AE%E4%B8%BA%E4%B8%80%E6%A0%B7%E7%9A%84%E5%80%BC%2F</url>
<content type="text"><![CDATA[先说结论,如果权重初始值设为0的话,将无法正确进行学习。 这是因为在误差反向传播法中,所有的权重值都会进行相同的更新。比如,在2层神经网络中,假设第1层和第2层的权重为0。这样一来,正向传播时,因为输入层的权重为0,所以第2层的神经元全部会被传递相同的值。第2层的神经元中全部输入相同的值,这意味着反向传播时第2层的权重全部都会进行相同的更新。因此,权重被更新为相同的值,并拥有了对称的值(重复的值)。这使得神经网络拥有许多不同的权重的意义丧失了。为了防止“权重均一化” (严格地讲,是为了瓦解权重的对称结构),必须随机生成初始值。 实际上,考虑一个全连接的神经网络,同一层中的任意神经元是同构的,对于相同的输入他们会有同样的输出,此时如果将参数全部初始化为相同的值,那么无论前向传播还是反向传播,参数的取值还是完全相同,学习将无法打破这种对称性,最终同一网络层中的各个参数仍然是相同的。 综上,必须随机的初始化神经网络参数的值,以打破这种对称性。]]></content>
<categories>
<category>机器学习</category>
</categories>
<tags>
<tag>机器学习</tag>
<tag>神经网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Git分支管理策略及简单操作]]></title>
<url>%2F2019%2F10%2F10%2FGit%E5%88%86%E6%94%AF%E7%AE%A1%E7%90%86%E7%AD%96%E7%95%A5%E5%8F%8A%E7%AE%80%E5%8D%95%E6%93%8D%E4%BD%9C%2F</url>
<content type="text"><![CDATA[前几天整理了一下之前项目的开发代码,当时使用了Git来进行代码管理。虽然本人熟悉常用的Git操作,但是对分支的管理经验非常欠缺。拿这个项目来说,在项目中有不下20个分支,每个分支间的继承关系相当之混乱,非常不利于代码的安全管理。因此,通过在网络上的学习,总结了一下关于Git分支管理的策略方法,供后续回顾学习。 当然必须承认,代码分支管理策略有很多种,不局限于以下介绍。但是下面介绍的这个分支管理策略非常具有工程借鉴意义,几乎适用于所有开发场景。 分支 命名 说明 主分支 master 主分支,所有提供给用户使用的正式版本,都在这个主分支上发布 开发分支 develop 开发分支,永远是功能最新最全的分支 功能分支 feature-* 新功能分支,某个功能点正在开发阶段 发布版本 release-* 发布定期要上线的功能 修复分支 hotfix-* 修复线上代码的 bug 1. 主分支master首先,代码库应该有且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。Git主分支的名字,默认叫做master。它是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。团队成员从主分支(master)获得的都是处于可发布状态的代码。 2. 开发分支develop日常开发应该在另一条分支上完成。我们把开发用的分支,叫做develop分支。开发分支(develop)应该总能够获得最新开发进展的代码。如果想正式对外发布,就在master分支上,对develop分支进行merge。下面介绍常用的几个命令: 12345678# 在master分支上创建develop分支git checkout -b develop master# 切换到master分支git checkout master# 对develop分支合并到当前master分支git merge --no-ff develop 3. 临时分支除了常设分支以外,还有一些临时性分支,用于应对一些特定目的的版本开发。临时性分支主要有三种: 功能(feature)分支 预发布(release)分支 修补bug(bugfix)分支 这三种分支都属于临时性需要,使用完以后,最好删除,使得代码库的常设分支始终只有master和develop。 功能分支feature分支是为了开发某种特定功能,从develop分支上面分出来的。开发完成后,要再并入develop。功能分支的名字,可以采用feature-xxx的形式命名。123456789# 从develop创建一个功能分支git checkout -b feature-x develop# 开发完成后,将功能分支合并到develop分支:git checkout developgit merge --no-ff feature-x# 删除feature分支git branch -d feature-x 预发布分支release分支是指发布正式版本之前(即合并到master分支之前),我们可能需要有一个预发布的版本进行测试而创建的分支。12345678910111213141516# 创建一个预发布分支git checkout -b release-x develop# 确认没有问题后,合并到master分支git checkout mastergit merge --no-ff release-x# 对合并生成的新节点,做一个标签git tag -a 1.2# 再合并到develop分支git checkout developgit merge --no-ff release-x# 最后,删除预发布分支git branch -d release-x bug修补分支软件正式发布以后,难免会出现bug。这时就需要创建一个分支,进行bug修补。 修补bug分支是从Master分支上面分出来的。修补结束以后,再合并进master和develop分支。它的命名,可以采用hotfix-x的形式。 1234567891011121314# 创建一个修补bug分支git checkout -b hotfix-x master# 修补结束后,合并到master分支git checkout mastergit merge --no-ff hotfix-xgit tag -a 0.1# 再合并到develop分支git checkout developgit merge --no-ff hotfix-x# 删除"修补bug分支"git branch -d hotfix-x 总结上面许多指令使用的–no-ff的意思是no-fast-farward的缩写,使用该命令可以保持更多的版本演进的细节。如果不使用该参数,默认使用了fast-farword进行merge。两者的区别如下图所示: 最后分享一下整体的分支管理策略图示: 参考: A successful Git branching model—from Vincent Driessen]]></content>
<categories>
<category>Git</category>
</categories>
<tags>
<tag>Git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[浅析Python中的列表和元组]]></title>
<url>%2F2019%2F10%2F07%2F%E6%B5%85%E6%9E%90Python%E4%B8%AD%E7%9A%84%E5%88%97%E8%A1%A8%E5%92%8C%E5%85%83%E7%BB%84%2F</url>
<content type="text"><![CDATA[区别 列表是动态数组,它们可变且可以重设长度(改变其内部元素的个数)。 元组是静态数组,它们不可变,且其内部数据一旦创建便无法改变。 元组缓存于Python运行时环境,这意味着我们每次使用元组时无须访问内核去 分配内存。 这些区别揭示了两者在设计哲学上的不同:元组用于描述一个不会改变的事物的多个属性,而列表可被用于保存多个互相独立对象的数据集合。 动态数组–列表列表可以改变大小及内容不同,列表的可变性的代价在于存储它们需要额外的内存以及使用它们需要额外的计算。我们在浅析Python中列表操作之*和*=中一起研究了cpython的list对象的源码,看到了list对象的动态分配数组的大体过程(调用resize函数),而且在动态调整数组大小时使用如下的分配公式: 1new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6); 下图是一个列表多次添加元素时分配空间的变化示意图: 静态数组–元组元组的不可改变性使其成为了一个非常轻量级的数据结构。这意味着存储元组不需要很多的内存开销,而且对元组的操作也非常直观。一旦元组被创建,它的内容无法被修改或它的大小也无法被改变。虽然它们不支持改变大小,但是我们可以将两个元组合并成一个新元组。这一操作类似列表的resize操作,但我们不需要为新生成的元组分配任何额外的空间。任意两个元组相加或者元组乘以一个整数进行repeat始终返回一个新分配的元组。其中两个元组相加调用cpython中的tupleconcat方法,而乘法操作调用的是tuplerepeat方法。上述两个方法的实现如下: 12345678910static PyObject *tupleconcat(PyTupleObject *a, PyObject *bb){ ... size = Py_SIZE(a) + Py_SIZE(b); np = tuple_alloc(size); ... return (PyObject *)np;} 12345678910static PyObject *tuplerepeat(PyTupleObject *a, Py_ssize_t n){ ... size = Py_SIZE(a) * n; np = tuple_alloc(size); ... return (PyObject *) np;} 元组的静态特性的另一个好处体现在一些会在Python后台发生的事:资源缓存。Python是一门垃圾收集语言,这意味着当一个变量不再被使用时,Python会将该变量使用的内存释放回操作系统,以供其他程序(或变量)使用。然而,从源码中可以看到,对于长度为1~20的元组,即使它们不再被使用,它们的空间也不会立刻被还给系统,而是留待未来使用。这意味着当未来需要一个同样大小的新元组时,我们不再需要向操作系统申请一块内存来存放数据,因为我们已经有了预留的内存。 我们可以验证资源缓存这一点,可以看到初始化一个列表消耗的时间是初始化一个元组消耗时间的6倍!可以想象一下,某些场景中在一个循环中频繁创建列表,耗时还是非常可观的,此时可以考虑使用元组来提高执行效率。]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
<tag>Python源码</tag>
</tags>
</entry>
<entry>
<title><![CDATA[浅析Python中列表操作之*和*=]]></title>
<url>%2F2019%2F10%2F07%2F%E6%B5%85%E6%9E%90Python%E4%B8%AD%E5%88%97%E8%A1%A8%E6%93%8D%E4%BD%9C%E4%B9%8B-%E5%92%8C%2F</url>
<content type="text"><![CDATA[初学Python时总是会将a*=n理解为a=a*n,稍微深入后就会知道在Python中的不同,其中调用mul ,而=调用imul 。 对于list对象也支持乘法操作,截止到Python3.7版本,上述仍然是成立的。我们知道list是由C实现的,所以真正的底层调用肯定是C的实现。观察list对象的C实现的源码我们会知道乘法*操作调用list_repeat,*=会调用list_inplace_repeat,下面分别看一下两者的C实现方式。 ▍* –> list_repeat123456789101112static PyObject *list_repeat(PyListObject *a, Py_ssize_t n){ ... size = Py_SIZE(a) * n; if (size == 0) return PyList_New(0); np = (PyListObject *) list_new_prealloc(size); ... return (PyObject *) np;}从以上可以看出,list_repeat方法需要多少空间就申请多少空间,该操作返回的一个新的列表对象。 ▍*= –> list_inplace_repeat12345678910111213141516171819202122static PyObject *list_inplace_repeat(PyListObject *self, Py_ssize_t n){ ... size = PyList_GET_SIZE(self); if (list_resize(self, size*n) < 0) return NULL; ...}static intlist_resize(PyListObject *self, Py_ssize_t newsize){ if (allocated >= newsize && newsize >= (allocated >> 1)) { assert(self->ob_item != NULL || newsize == 0); Py_SIZE(self) = newsize; return 0; } ... new_allocated = (size_t)newsize + (newsize >> 3) + (newsize < 9 ? 3 : 6); ...} list_inplace_repeat代码中通过调用list_resize来进行扩容,并告诉它这个列表需要容纳size*n个元素。从list_resize代码来看,当allocated空间足够时,不会进行扩容操作。但是新申请的空间总是比所需要的大的。如果进行pop等减小list元素数量的操作来看,实际上列表的大小也会按照相应策略进行缩减操作。 If the newsize falls lower than half the allocated size, then proceed with the realloc() to shrink the list. — From cpython ▍总结 *=会调用list_resize,可能会引起list空间扩容的情况,而且此时list对象占用空间会比实际list对象中元素占用空间大。 *会按需获取申请空间大小,不会调用list_resize方法。]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
<tag>Python源码</tag>
</tags>
</entry>
<entry>
<title><![CDATA[神经网络的学习为何要设定损失函数?]]></title>
<url>%2F2019%2F10%2F01%2F%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E7%9A%84%E5%AD%A6%E4%B9%A0%E4%B8%BA%E4%BD%95%E8%A6%81%E8%AE%BE%E5%AE%9A%E6%8D%9F%E5%A4%B1%E5%87%BD%E6%95%B0%EF%BC%9F%2F</url>
<content type="text"><![CDATA[这里所说的“学习”是指从训练数据中自动获取最优权重参数的过程。学习的目的就是以该损失函数为基准,找出能使它的值达到最小的权重参数。 可能会有人问:我们想获得的是能提高识别精度的参数,特意再导入一个损失函数不是有些重复劳动吗?既然我们的目标是获得使识别精度尽可能高的神经网络,那不是应该把识别精度作为指标吗? 听起来很有道理! 对于这一疑问,我们可以根据“导数“在神经网络学习中的作用来回答。 在神经网络的学习中,寻找最优参数(权重和偏置)时, 要寻找使损失函数的值尽可能小的参数。为了找到使损失函数的值尽可能小 的地方,需要计算参数的导数(确切地讲是梯度),然后以这个导数为指引, 逐步更新参数的值。 假设有一个神经网络,现在我们来关注这个神经网络中的某一个权重参数。此时,对该权重参数的损失函数求导,此处导数的含义可以理解为“如果稍微改变这个权重参数的值,损失函数的值会如何变化”。如果导数的值为负,通过使该权重参数向正方向改变,可以减小损失函数的值;反过来,如果导数的值为正, 则通过使该权重参数向负方向改变,可以减小损失函数的值。不过,当导数为 0 时,无论权重参数向哪个方向变化,损失函数的值都不会改变,此时该权重参数的更新会停在此处。 在进行神经网络的学习时,不能将识别精度作为指标。因为如果以识别精度为指标,则参数的导数在绝大多数地方都会变为 0,导致参数无法更新。那为什么用识别精度作为指标时,参数的导数在绝大多数地方都会变成0呢? 为了回答这个问题,我们来思考另一个具体例子。假设某个神经网络正确识别出了 100个训练数据中的32笔,此时识别精度为 32 %。如果以识别精度为指标,即使稍微改变权重参数的值,识别精度也仍将保持在 32 %,不会出现变化。也就是说,仅仅微调参数,是无法改善识别精度的。即便识别精度有所改善,它的值也不会像 32.0123 . . . % 这样连续变化,而是变为 33 %、 34 % 这样的不连续的、离散的值。而如果把损失函数作为指标,则当前损失函数的值可以表示为 0.92543 . . . 这样的值。并且,如果稍微改变一下参数 的值,对应的损失函数也会像 0.93432 . . . 这样发生连续性的变化。 作为激活函数的阶跃函数也有同样的情况。出于相同的原因,如果使用阶跃函数作为激活函数,神经网络的学习将无法进行。原因是阶跃函数的导数在绝大多数地方(除了0以外的地方)均为0。 也就是说,如果使用了阶跃函数,那么即便将损失函数作为指标,参数的微小变化也会被阶跃函数抹杀,导致损失函数的值不会产生任何变化。 而 sigmoid 函数,不仅函数的输出(竖轴的值)是连续变化的,曲线的斜率(导数) 也是连续变化的。也就是说,sigmoid 函数的导数在任何地方都不为 0。这对神经网络的学习非常重要。得益于这个斜率不会为 0 的性质,神经网络的学习得以正确进行。 —-参考数据深度学习入门-Deep Learning from Scratch]]></content>
<categories>
<category>机器学习</category>
</categories>
<tags>
<tag>机器学习</tag>
<tag>神经网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[神经网络的激活函数为什么要使用非线性函数]]></title>
<url>%2F2019%2F09%2F29%2F%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E7%9A%84%E6%BF%80%E6%B4%BB%E5%87%BD%E6%95%B0%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E4%BD%BF%E7%94%A8%E9%9D%9E%E7%BA%BF%E6%80%A7%E5%87%BD%E6%95%B0%2F</url>
<content type="text"><![CDATA[▍什么是激活函数在神经元中,输入的inputs通过加权求和,然后被作用了一个函数,这个函数就是激活函数 Activation Function。激活函数在神经网络中的位置如图所示: ▍为什么要用非线性函数要解释这个问题,可以反过来思考一下,为什么激活函数不能使用线性函数。如果使用线性函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合。加深神经网络的层数就没有什么意义了。线性函数的问题在于不管加深层数到多少,总是存在与之等效的「无隐藏层」的神经网络。为了稍微直观的理解这一点,考虑下面一个简单的例子。 存在一个线性函数f(x)=kx(k≠0)作为激活函数,将y=f(f(f(x)))对应三层的神经网络。很明显可以想到同样的处理可以由y=ax(a=k^3),一个没有隐藏层的神经网络来表示。该例子仅仅是一个近似,实际中的神经网络的运算要比这个例子复杂很多,但不影响结论的成立。也就是说,使用线性激活函数时,无法发挥多层网络带来的优势。 相反如果使用非线性函数,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。 以上!]]></content>
<categories>
<category>机器学习</category>
</categories>
<tags>
<tag>机器学习</tag>
<tag>神经网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[服务器中的物理CPU、逻辑CPU和CPU核数]]></title>
<url>%2F2019%2F06%2F20%2F%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%AD%E7%9A%84%E7%89%A9%E7%90%86CPU%E3%80%81%E9%80%BB%E8%BE%91CPU%E5%92%8CCPU%E6%A0%B8%E6%95%B0%2F</url>
<content type="text"><![CDATA[重要概念物理CPU实际Server中插槽上的CPU个数物理cpu数量,可以数不重复的 physical id 有几个,查看方法 123456789101112grep "physical id" /proc/cpuinfo|sort|uniq|wc -l2``` ### CPU核数 单块CPU上面能处理数据的芯片组的数量,如双核、四核等 (cpu cores)。比如现在的i5 760,是双核心四线程的CPU、而 i5 2250 是四核心四线程的CPU 。一般来说,物理CPU个数×每颗核数就应该等于逻辑CPU的个数,如果不相等的话,则表示服务器的CPU支持超线程技术 ```bashcat /proc/cpuinfo |grep "cpu cores"|uniqgrep "cpu cores" /proc/cpuinfo|uniq|awk -F ":" "{print $2}"8 逻辑CPULinux用户对 /proc/cpuinfo 这个文件肯定不陌生. 它是用来存储cpu硬件信息的信息内容分别列出了processor 0 – n 的规格。这里需要注意,如果你认为n就是真实的cpu数的话, 就大错特错了。 一般情况,我们认为一颗cpu可以有多核,加上intel的超线程技术(HT), 可以在逻辑上再分一倍数量的cpu core出来逻辑CPU数量=物理cpu数量 x cpu cores 这个规格值 x 2(如果支持并开启ht)。 如果有一个以上逻辑处理器拥有相同的 core id 和 physical id,则说明系统支持超线程(HT)技术 备注一下:Linux下top查看的CPU也是逻辑CPU个数 12cat /proc/cpuinfo| grep "processor" |wc -l32(支持超线程) 查看方法通过cat /proc/cpuinfocpu来查看相关信息。 vendor id 如果处理器为英特尔处理器,则字符串是 GenuineIntel。 processor 包括这一逻辑处理器的唯一标识符。 physical id 包括每个物理封装的唯一标识符。 core id 保存每个内核的唯一标识符。 siblings 列出了位于相同物理封装中的逻辑处理器的数量。 cpu cores 包含位于相同物理封装中的内核数量。 拥有相同 physical id 的所有逻辑处理器共享同一个物理插座,每个 physical id 代表一个唯一的物理封装。 Siblings 表示位于这一物理封装上的逻辑处理器的数量,它们可能支持也可能不支持超线程(HT)技术。 每个 core id 均代表一个唯一的处理器内核,所有带有相同 core id 的逻辑处理器均位于同一个处理器内核上。简单的说:“siblings”指的是一个物理CPU有几个逻辑CPU,”cpu cores“指的是一个物理CPU有几个核。 如果有一个以上逻辑处理器拥有相同的 core id 和 physical id,则说明系统支持超线程(HT)技术。 如果有两个或两个以上的逻辑处理器拥有相同的 physical id,但是 core id不同,则说明这是一个多内核处理器。cpu cores条目也可以表示是否支持多内核。 top命令关于cpu使用率下面做一个简单的测试,终端中使用如下命令1md5sum /dev/zero & 开启top,如下所示 按数字键1后查看所有核的使用率。 发现使用top命令,左上角显示的是整体负载,即单核的负载数除以核数。%CPU数值代表单个核的使用率,超过100%代表使用其他核的计算资源。 第一行: 10:01:23 — 当前系统时间 126 days, 14:29 — 系统已经运行了126天14小时29分钟(在这期间没有重启过) 2 users — 当前有2个用户登录系统 load average: 1.15, 1.42, 1.44 — load average后面的三个数分别是1分钟、5分钟、15分钟的负载情况。load average数据是每隔5秒钟检查一次活跃的进程数,然后按特定算法计算出的数值。如果这个数除以逻辑CPU的数量,结果高于5的时候就表明系统在超负荷运转了。 第二行: Tasks — 任务(进程),系统现在共有183个进程,其中处于运行中的有1个,182个在休眠(sleep),stoped状态的有0个,zombie状态(僵尸)的有0个。 第三行:cpu状态 6.7% us — 用户空间占用CPU的百分比。 0.4% sy — 内核空间占用CPU的百分比。 0.0% ni — 改变过优先级的进程占用CPU的百分比 92.9% id — 空闲CPU百分比 0.0% wa — IO等待占用CPU的百分比 0.0% hi — 硬中断(Hardware IRQ)占用CPU的百分比 0.0% si — 软中断(Software Interrupts)占用CPU的百分比]]></content>
<categories>
<category>操作系统</category>
</categories>
<tags>
<tag>操作系统</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux查看端口占用情况]]></title>
<url>%2F2019%2F05%2F28%2FLinux%E6%9F%A5%E7%9C%8B%E7%AB%AF%E5%8F%A3%E5%8D%A0%E7%94%A8%E6%83%85%E5%86%B5%2F</url>
<content type="text"><![CDATA[lsof lsof -i 用以显示符合条件的进程情况,lsof(list open files)是一个列出当前系统打开文件的工具。以root用户来执行lsof -i命令 lsof -i:端口号,用于查看某一端口的占用情况,比如查看22号端口使用情况,lsof -i:22 netstatnetstat -tunlp用于显示tcp,udp的端口和进程等相关情况]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java实时读取log日志文件示例代码]]></title>
<url>%2F2019%2F05%2F21%2FJava%E5%AE%9E%E6%97%B6%E8%AF%BB%E5%8F%96log%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81%2F</url>
<content type="text"><![CDATA[需求:正在开发一个监控系统,要求将多台日志信息实时采集出来,然后保存到Kafka中,后期对日志数据进行spark运算、大数据处理分析,日志按大小,时间切分。 运用的技术:RandomAccessFile类中seek方法可以从指定位置读取文件,可以用来实现文件实时读取,JDK文档有对RandomAccessFile的介绍。 思想:在每一次读取后,close一下就不会影响重命名操作了。因为日志是线上机器产生的,我们只需要写实时读取的方法即可,但是这里为了模拟实际情况,也把产生日志的方法出来,在测试的时候,可以手动改变日志的名称,更为方便的处理方式—–将日志mock.log直接删除即可。 原生RandomAccessFile效率较低,后面附有BufferedRandomAccessFile 模拟写日志的类因为日志是按大小和时间切分的,在测试的时候,直接修改日志的名称,或者删除日志。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154package com.inveno.file;import java.io.File;import java.io.FileWriter;import java.io.IOException;import java.io.Writer;import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class LogSvr { private static final Logger logger = LoggerFactory.getLogger(LogSvr.class); private SimpleDateFormat dateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss"); private static ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); public void logMsg(File logFile,String msgInfo) throws IOException{ if(!logFile.exists()) { logFile.createNewFile(); } Writer txtWriter = new FileWriter(logFile,true); txtWriter.write(dateFormat.format(new Date()) + "\t" + msgInfo + "\n"); txtWriter.flush(); txtWriter.close(); } public void stop(){ if(exec != null){ exec.shutdown(); logger.info("file write stop !"); } } public static void main(String[] args) throws Exception { final LogSvr logSvr = new LogSvr(); final File tmpLogFile = new File("pathtolog.log"); final String msgInfo = "test !"; //启动一个线程每5秒向日志文件写一次数据 exec.scheduleWithFixedDelay(new Runnable(){ @Override public void run() { try { logSvr.logMsg(tmpLogFile, msgInfo); //Thread.sleep(1000); } catch (Exception e) { logger.error("file write error !"); } } }, 0, 5, TimeUnit.SECONDS); }}```javaimport org.apache.log4j.Logger;import java.io.File;import java.io.RandomAccessFile;import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;public class LogView { private static Logger logger = Logger.getLogger(Start.class.getName()); private long pointer = 0; // 文件指针位置 private SimpleDateFormat dateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss"); private ScheduledExecutorService exec = Executors.newScheduledThreadPool(1); public void realtimeShowLog(final File logFile) throws Exception { if (logFile == null) { throw new IllegalStateException("logFile can not be null"); } //启动一个线程每2秒读取新增的日志信息 exec.scheduleWithFixedDelay(new Runnable() { public void run() { //获得变化部分 try { long len = logFile.length(); if (len < pointer) { logger.info("Log file was reset. Restarting logging from start of file."); pointer = 0; } else { //指定文件可读可写 RandomAccessFile randomFile = new RandomAccessFile(logFile, "rw"); //获取RandomAccessFile对象文件指针的位置,初始位置是0 logger.info("RandomAccessFile文件指针的初始位置:" + pointer); randomFile.seek(pointer);//移动到文件指针位置 String tmp; while ((tmp = randomFile.readLine()) != null) { System.out.println("info : " + new String(tmp.getBytes("utf-8"))); pointer = randomFile.getFilePointer(); } randomFile.close(); } } catch (Exception e) { //实时读取日志异常,需要记录时间和lastTimeFileSize 以便后期手动补充 logger.error(dateFormat.format(new Date()) + " File read error, pointer: " + pointer); } finally { //将pointer 落地以便下次启动的时候,直接从指定位置获取 } } }, 0, 10, TimeUnit.SECONDS); } public void stop() { if (exec != null) { exec.shutdown(); logger.info("file read stop !"); } } public static void main(String[] args) throws Exception { LogView view = new LogView(); File tmpLogFile = new File("pathtolog.log"); System.out.println(tmpLogFile.getAbsolutePath()); view.pointer = 0; view.realtimeShowLog(tmpLogFile); }} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;import java.util.Arrays;/** * A <code>BufferedRandomAccessFile</code> is like a * <code>RandomAccessFile</code>, but it uses a private buffer so that most * operations do not require a disk access. * <p> * <p> * Note: The operations on this class are unmonitored. Also, the correct * functioning of the <code>RandomAccessFile</code> methods that are not * overridden here relies on the implementation of those methods in the * superclass. * <p> * To describe the above fields, we introduce the following abstractions for * the file "f": * <p> * len(f) the length of the file curr(f) the current position in the file * c(f) the abstract contents of the file disk(f) the contents of f's * backing disk file closed(f) true iff the file is closed * <p> * "curr(f)" is an index in the closed interval [0, len(f)]. "c(f)" is a * character sequence of length "len(f)". "c(f)" and "disk(f)" may differ if * "c(f)" contains unflushed writes not reflected in "disk(f)". The flush * operation has the effect of making "disk(f)" identical to "c(f)". * <p> * A file is said to be *valid* if the following conditions hold: * <p> * V1. The "closed" and "curr" fields are correct: * <p> * f.closed == closed(f) f.curr == curr(f) * <p> * V2. The current position is either contained in the buffer, or just past * the buffer: * <p> * f.lo <= f.curr <= f.hi * <p> * V3. Any (possibly) un-flushed characters are stored in "f.buff": * <p> * (forall i in [f.lo, f.curr): c(f)[i] == f.buff[i - f.lo]) * <p> * V4. For all characters not covered by V3, c(f) and disk(f) agree: * <p> * (forall i in [f.lo, len(f)): i not in [f.lo, f.curr) => c(f)[i] == * disk(f)[i]) * <p> * V5. "f.dirty" is true iff the buffer contains bytes that should be * flushed to the file; by V3 and V4, only part of the buffer can be dirty. * <p> * f.dirty == (exists i in [f.lo, f.curr): c(f)[i] != f.buff[i - f.lo]) * <p> * V6. this.maxHi == this.lo + this.buff.length * <p> * Note that "f.buff" can be "null" in a valid file, since the range of * characters in V3 is empty when "f.lo == f.curr". * <p> * A file is said to be *ready* if the buffer contains the current position, * i.e., when: * <p> * R1. !f.closed && f.buff != null && f.lo <= f.curr && f.curr < f.hi * <p> * When a file is ready, reading or writing a single byte can be performed * by reading or writing the in-memory buffer without performing a disk * operation. * <p> * <p> * !!!This class come from network ,I just adjust code style!!! * * @author zhaofeng * @date 2018 -05-02 */public final class BufferedRandomAccessFile extends RandomAccessFile { /** * 64K buffer */ private static final int LOG_BUFF_SZ = 16; private static final int BUFF_SZ = (1 << LOG_BUFF_SZ); private static final long BUFF_MASK = ~(((long) BUFF_SZ) - 1L); private String path_; /** * This implementation is based on the buffer implementation in Modula-3's * "Rd", "Wr", "RdClass", and "WrClass" interfaces. * <p> * true iff un-flushed bytes exist */ private boolean dirty_; /** * dirty_ can be cleared by e.g. seek, so track sync separately */ private boolean syncNeeded_; /** * current position in file */ private long curr_; /** * bounds on characters in "buff" */ private long lo_, hi_; /** * local buffer */ private byte[] buff_; /** * this.lo + this.buff.length */ private long maxHi_; /** * buffer contains last file block? */ private boolean hitEOF_; /** * disk position */ private long diskPos_; /** * Open a new <code>BufferedRandomAccessFile</code> on <code>file</code> * in mode <code>mode</code>, which should be "r" for reading only, or * "rw" for reading and writing. * * @param file the file * @param mode the mode * @throws IOException the io exception */ public BufferedRandomAccessFile(File file, String mode) throws IOException { this(file, mode, 0); } /** * Instantiates a new Buffered random access file. * * @param file the file * @param mode the mode * @param size the size * @throws IOException the io exception */ public BufferedRandomAccessFile(File file, String mode, int size) throws IOException { super(file, mode); path_ = file.getAbsolutePath(); this.init(size); } /** * Open a new <code>BufferedRandomAccessFile</code> on the file named * <code>name</code> in mode <code>mode</code>, which should be "r" for * reading only, or "rw" for reading and writing. * * @param name the name * @param mode the mode * @throws IOException the io exception */ public BufferedRandomAccessFile(String name, String mode) throws IOException { this(name, mode, 0); } /** * Instantiates a new Buffered random access file. * * @param name the name * @param mode the mode * @param size the size * @throws FileNotFoundException the file not found exception */ public BufferedRandomAccessFile(String name, String mode, int size) throws FileNotFoundException { super(name, mode); path_ = name; this.init(size); } private void init(int size) { this.dirty_ = false; this.lo_ = this.curr_ = this.hi_ = 0; this.buff_ = (size > BUFF_SZ) ? new byte[size] : new byte[BUFF_SZ]; this.maxHi_ = (long) BUFF_SZ; this.hitEOF_ = false; this.diskPos_ = 0L; } /** * Gets path. * * @return the path */ public String getPath() { return path_; } /** * Sync. * * @throws IOException the io exception */ public void sync() throws IOException { if (syncNeeded_) { flush(); getChannel().force(true); syncNeeded_ = false; } } @Override public void close() throws IOException { this.flush(); this.buff_ = null; super.close(); } /** * Flush any bytes in the file's buffer that have not yet been written to * disk. If the file was created read-only, this method is a no-op. * * @throws IOException the io exception */ public void flush() throws IOException { this.flushBuffer(); } /** * Flush any dirty bytes in the buffer to disk. */ private void flushBuffer() throws IOException { if (this.dirty_) { if (this.diskPos_ != this.lo_) { super.seek(this.lo_); } int len = (int) (this.curr_ - this.lo_); super.write(this.buff_, 0, len); this.diskPos_ = this.curr_; this.dirty_ = false; } } /** * Read at most "this.buff.length" bytes into "this.buff", returning the * number of bytes read. If the return result is less than * "this.buff.length", then EOF was read. */ private int fillBuffer() throws IOException { int cnt = 0; int rem = this.buff_.length; while (rem > 0) { int n = super.read(this.buff_, cnt, rem); if (n < 0) { break; } cnt += n; rem -= n; } if ((cnt < 0) && (this.hitEOF_ = (cnt < this.buff_.length))) { // make sure buffer that wasn't read is initialized with -1 Arrays.fill(this.buff_, cnt, this.buff_.length, (byte) 0xff); } this.diskPos_ += cnt; return cnt; } /** * This method positions <code>this.curr</code> at position <code>pos</code>. * If <code>pos</code> does not fall in the current buffer, it flushes the * current buffer and loads the correct one.<p> * <p> * On exit from this routine <code>this.curr == this.hi</code> iff <code>pos</code> * is at or past the end-of-file, which can only happen if the file was * opened in read-only mode. */ @Override public void seek(long pos) throws IOException { if (pos >= this.hi_ || pos < this.lo_) { // seeking outside of current buffer -- flush and read this.flushBuffer(); this.lo_ = pos & BUFF_MASK; // start at BuffSz boundary this.maxHi_ = this.lo_ + (long) this.buff_.length; if (this.diskPos_ != this.lo_) { super.seek(this.lo_); this.diskPos_ = this.lo_; } int n = this.fillBuffer(); this.hi_ = this.lo_ + (long) n; } else { // seeking inside current buffer -- no read required if (pos < this.curr_) { // if seeking backwards, we must flush to maintain V4 this.flushBuffer(); } } this.curr_ = pos; } @Override public long getFilePointer() { return this.curr_; } /** * max accounts for the case where we have written past the old file length, but not yet flushed our buffer * * @return * @throws IOException */ @Override public long length() throws IOException { return Math.max(this.curr_, super.length()); } @Override public int read() throws IOException { if (readEnd()) { return -1; } byte res = this.buff_[(int) (this.curr_ - this.lo_)]; this.curr_++; return ((int) res) & 0xFF; // convert byte -> int } @Override public int read(byte[] b) throws IOException { return this.read(b, 0, b.length); } @Override public int read(byte[] b, int off, int len) throws IOException { if (readEnd()) { return -1; } len = Math.min(len, (int) (this.hi_ - this.curr_)); int buffOff = (int) (this.curr_ - this.lo_); System.arraycopy(this.buff_, buffOff, b, off, len); this.curr_ += len; return len; } private boolean readEnd() throws IOException { if (this.curr_ >= this.hi_) { // test for EOF // if (this.hi < this.maxHi) return -1; if (this.hitEOF_) { return true; } // slow path -- read another buffer this.seek(this.curr_); if (this.curr_ == this.hi_) { return true; } } return false; } @Override public void write(int b) throws IOException { if (this.curr_ >= this.hi_) { if (this.hitEOF_ && this.hi_ < this.maxHi_) { // at EOF -- bump "hi" this.hi_++; } else { // slow path -- write current buffer; read next one this.seek(this.curr_); if (this.curr_ == this.hi_) { // appending to EOF -- bump "hi" this.hi_++; } } } this.buff_[(int) (this.curr_ - this.lo_)] = (byte) b; this.curr_++; this.dirty_ = true; syncNeeded_ = true; } @Override public void write(byte[] b) throws IOException { this.write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { while (len > 0) { int n = this.writeAtMost(b, off, len); off += n; len -= n; this.dirty_ = true; syncNeeded_ = true; } } /** * Write at most "len" bytes to "b" starting at position "off", and return * the number of bytes written. */ private int writeAtMost(byte[] b, int off, int len) throws IOException { if (this.curr_ >= this.hi_) { if (this.hitEOF_ && this.hi_ < this.maxHi_) { // at EOF -- bump "hi" this.hi_ = this.maxHi_; } else { // slow path -- write current buffer; read next one this.seek(this.curr_); if (this.curr_ == this.hi_) { // appending to EOF -- bump "hi" this.hi_ = this.maxHi_; } } } len = Math.min(len, (int) (this.hi_ - this.curr_)); int buffOff = (int) (this.curr_ - this.lo_); System.arraycopy(b, off, this.buff_, buffOff, len); this.curr_ += len; return len; }}]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java多线程读写HashMap遇到的坑]]></title>
<url>%2F2019%2F03%2F29%2FJava%E5%A4%9A%E7%BA%BF%E7%A8%8B%E8%AF%BB%E5%86%99HashMap%E9%81%87%E5%88%B0%E7%9A%84%E5%9D%91%2F</url>
<content type="text"></content>
</entry>
<entry>
<title><![CDATA[Python中如何表示正负无穷大]]></title>
<url>%2F2019%2F03%2F15%2FPython%E4%B8%AD%E5%A6%82%E4%BD%95%E8%A1%A8%E7%A4%BA%E6%AD%A3%E8%B4%9F%E6%97%A0%E7%A9%B7%E5%A4%A7%2F</url>
<content type="text"><![CDATA[cmath.infFloating-point positive infinity. Equivalent to float('inf').负无穷-float(‘-inf’) New in version 3.6. cmath.piThe mathematical constant π, as a float. cmath.eThe mathematical constant e, as a float. cmath.tauThe mathematical constant τ, as a float. New in version 3.6. cmath.infjComplex number with zero real part and positive infinity imaginary part. Equivalent to complex(0.0, float('inf')). New in version 3.6. cmath.nanA floating-point “not a number” (NaN) value. Equivalent to float('nan'). New in version 3.6. cmath.nanjComplex number with zero real part and NaN imaginary part. Equivalent to complex(0.0, float(‘nan’)). New in version 3.6.]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[进程地址空间]]></title>
<url>%2F2019%2F02%2F21%2F%E8%BF%9B%E7%A8%8B%E5%9C%B0%E5%9D%80%E7%A9%BA%E9%97%B4%2F</url>
<content type="text"><![CDATA[]]></content>
<categories>
<category>操作系统</category>
<category>多任务处理</category>
</categories>
<tags>
<tag>操作系统</tag>
</tags>
</entry>
<entry>
<title><![CDATA[异常的分类]]></title>
<url>%2F2019%2F02%2F20%2F%E5%BC%82%E5%B8%B8%E7%9A%84%E5%88%86%E7%B1%BB%2F</url>
<content type="text"><![CDATA[异常可以分为四类:中断(interrupt)、陷阱(trap)、故障(fault)、终止(abort). 类别 原因 异步/同步 返回行为 中断 来自I/O设备的信号 异步 总是返回下一条指令 陷阱 有意的异常 同步 总是返回下一条指令 故障 潜在可恢复的错误 同步 可能返回到当前指令 终止 不可恢复的错误 同步 不会返回 异步异常时由处理器外部的I/O设备中的事件产生的。同步异常是执行一条指令的直接产物。 中断中断时异步发生的,来自处理器外部的I/O设备的信号结果。剩下的异常类型是同步发生的,是执行当前指令的结果,我们把这类指令叫做故障指令。 陷阱陷阱最重要的用途是用在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用从程序员的角度看,系统调用和普通的函数调用是一样的。然而,它们的实现非常不同。普通函数运行在用户模式中,用户模式限制了函数可以执行的指令的类型,而且它们只能访问与调用函数相同的栈。系统调用在内核模式中,内核模式允许系统调用执行特权指令,并访问定义在内核中的栈。 故障故障由错误引起,它可能能够被故障处理程序修正。根据故障是否能够被修复,故障处理程序要么重新执行引起故障的指令,要么终止。 一个经典的故障示例是缺页异常,当指令引用一个虚拟地址,而与该地址相对应的物理页面不在内存中,因此必须从磁盘中读取时,就会发生故障。一个页面就是虚拟内存中的一个连续的块(典型的是4KB),缺页处理程序从磁盘加载适当的页面,然后将控制返回给引起故障的指令。当指令再次执行是,相应的物理页面已经驻留在内存中了,指令就可以没有故障的运行完成了。 终止终止是不可恢复的致命错误造成的结果,通常是一些硬件错误,比如DRAM或者SRAM位被损坏时发生的奇偶错误。终止处理程序从不将控制返回给应用程序,处理程序将控制返回给一个abort例程,该例程会终止这个应用程序。 参考资料:《深入理解计算机系统》第三版P504]]></content>
<categories>
<category>操作系统</category>
</categories>
<tags>
<tag>操作系统</tag>
</tags>
</entry>
<entry>
<title><![CDATA[布尔环]]></title>
<url>%2F2019%2F02%2F15%2F%E5%B8%83%E5%B0%94%E7%8E%AF%2F</url>
<content type="text"><![CDATA[布尔环与整数运算有很多相同属性。例如整数运算的一个属性是每个值x都有一个加法逆元(additive-inverse)-x, 使得x+(-x)=0。布尔环也有类似的属性,这里的加法运算时^,不过这时每个元素的加法逆元是它自己本身。也就是说,对于任何值a来说,a^a=0, 这里用0表示全0的位向量。 相同道理还有一个有意思的属性,即(a^b)^a=b,该属性可以用来交换两个元素的值,而不需要第三方变量。 123a = a ^ bb = a ^ ba = a ^ b]]></content>
<tags>
<tag>计算机基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[搭建Git服务器的一种方法]]></title>
<url>%2F2019%2F02%2F11%2F%E6%90%AD%E5%BB%BAGit%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%9A%84%E4%B8%80%E7%A7%8D%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[1. 确保自己的服务器上安装有Git和ssh12yum install sshyum install git 2. 配置git用户新建git用户主要是为了让大家在上传代码时登录使用,一般使用:git1adduser git 配置git用户的ssh登录123cd /home/git //进入git用户文件夹sudo mkdir .ssh //创建 .ssh 文件夹sudo touch .ssh/authorized_keys //创建authorized_keys文件,用以保存公钥 authorized_keys 是公钥保存文件,客户端的私钥与服务器的公钥配对成功,则可以登录。之后将需要使用这个git服务器的成员的公钥复制粘贴到这个文件中(每个占一行)。 客户端电脑进入 /users/用户名/.ssh 文件夹,如果已经有自己的秘钥,直接打开 pub 文件,复制里面的公钥信息,进入服务器,粘贴到 authorized_keys 文件中,如果没有,使用ssh-keygen命令生成后复制粘贴即可。 3. 初始化裸仓库来保存项目在/home/git下123sudo mkdir repos //创建repos文件夹,用于保存git仓库,名字随各人喜好,这里使用reposcd repos //进入repos文件夹sudo git init --bare sample.git //创建一个裸仓库,名字按自己需要选择,这里使用sample repos文件夹中会创建 sample.git 文件夹。那么,剩下的事情,就是将本地代码上传到服务器的仓库中。 如果本地没有初始代码,可以直接从服务器克隆仓库到本地: 1git clone git@server:repos/sample.git git@server是登录服务器使用的用户名(git)和IP地址(server),登录之后有默认进入用户文件夹(/home/git),后面的路径就是用户文件夹下的仓库路径,也就是 repos/sample.git 。 如果本地有一些初始代码,需要直接同步到服务器的仓库,可以进入本地代码文件夹,创建并将代码保存到git仓库后同步至服务器仓库: 12345git init //创建git仓库git add . //添加所有文件git commit -m "your remark" //将代码提交到本地仓库git remote add origin git@server:repos/sample.git //添加远程仓库地址git push --set-upstream origin master //将代码上传到远程仓库并把本地上传的代码设为master分支 有时会出现git远程仓库配置写错的情况,或者需要修改远程仓库,可以使用以下命令删除原有的远程仓库配置后重新配置:1git remote rm origin]]></content>
<categories>
<category>Git</category>
</categories>
<tags>
<tag>Git</tag>
<tag>Github</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python中如何生成一定格式的日期字符串]]></title>
<url>%2F2018%2F12%2F03%2FPython%E4%B8%AD%E5%A6%82%E4%BD%95%E7%94%9F%E6%88%90%E4%B8%80%E5%AE%9A%E6%A0%BC%E5%BC%8F%E7%9A%84%E6%97%A5%E6%9C%9F%E5%AD%97%E7%AC%A6%E4%B8%B2%2F</url>
<content type="text"><![CDATA[12345678910import timetimeStamp = time.time()timeArray = time.localtime(time.time())print(timeArray)# time.struct_time(tm_year=2018, tm_mon=12, tm_mday=3, tm_hour=16, tm_min=23, tm_sec=55, tm_wday=0, tm_yday=337, tm_isdst=0)formatTime = time.strftime("%Y%m%d-%H:%M:%S", timeArray)print(formatTime)# 20181203-16:23:55]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python中append和extend效率]]></title>
<url>%2F2018%2F11%2F27%2FPython%E4%B8%ADappend%E5%92%8Cextend%E6%95%88%E7%8E%87%2F</url>
<content type="text"><![CDATA[python提供了一个名为extend的方法,该方法将一个列表中的所有元素添加到另一个列表的结尾。在作用上,调用data.extend(other)的输出结果与以下代码的输出结果相同。12for elem in other: data.append(elem) 在任何情况下,运行时间都正比于另一张列表的长度,并且之所以摊销,是因为第一张列表的底层数组需要调整打下以容纳增添的元素。 在实践中,相对于重复调用append方法,我们更倾向于选择extend方法。原因是渐进分析中隐含的常数明显更小。extend方法效率更高体现在三个方面: 首先,使用合适的python方法总会有一些优势,因为这些方法通常使用本地编译语言执行 与调用很多独立的方法相比,调用一个函数完成所有工作的开销更小。 extend提升的效率来源更新列表的最终大小能提前计算出。例如第二个数据集是非常大的,当重复调用append时,底层动态数组会有很多次调整大小的风险,如果调用一次extend方法,最多执行一次调整工作。 我们所熟悉的构造新列表的语法,在几乎所有情况下,该行为的渐进效率在创建列表的长度方面是线性的。但是不同的方法在实际效率上会有不同。 在python中经常使用一个诸如squares = [k*k for k in range(1, n+1)]的例子作为123squares = []for k in range(1, n+1) squares.append(k*k) 的一种速记,并由此引入了列表推导式。实验可以证明用列表推导式语法比不断增添数据来创建列表速度更快。 类似的,使用乘法操作初始化一个固定的列表,也是一种很常见的python风格,例如[0]*100生成一张长度为100的列表,这样做不但语法简单,而且比逐步构造这样的表效率更高]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[IP地址的分类]]></title>
<url>%2F2018%2F11%2F07%2FIP%E5%9C%B0%E5%9D%80%E7%9A%84%E5%88%86%E7%B1%BB%2F</url>
<content type="text"><![CDATA[IP地址分类(A类 B类 C类 D类 E类) IP地址由两部分组成,即网络地址和主机地址。网络地址表示其属于互联网的哪一个网络,主机地址表示其属于该网络中的哪一台主机。二者是主从关系。 IP地址的四大类型标识的是网络中的某台主机。IPv4的地址长度为32位,共4个字节,但实际中我们用点分十进制记法。 IP地址根据网络号和主机号来分,分为A、B、C三类及特殊地址D、E。 全0和全1的都保留不用。 A类:(1.0.0.0-126.0.0.0)(默认子网掩码:255.0.0.0或 0xFF000000) 第一个字节为网络号,后三个字节为主机号。该类IP地址的最前面为“0”,所以地址的网络号取值于1~126之间。 一般用于大型网络。 B类:(128.1.0.0-191.255.0.0)(默认子网掩码:255.255.0.0或0xFFFF0000) 前两个字节为网络号,后两个字节为主机号。该类IP地址的最前面为“10”,所以地址的网络号取值于128~191之间。 一般用于中等规模网络。 C类:(192.0.1.0-223.255.255.0)(子网掩码:255.255.255.0或 0xFFFFFF00) 前三个字节为网络号,最后一个字节为主机号。该类IP地址的最前面为“110”,所以地址的网络号取值于192~223之间。 一般用于小型网络。 D类:是多播地址。该类IP地址的最前面为“1110”,所以地址的网络号取值于224~239之间。一般用于多路广播用户。 E类:是保留地址。该类IP地址的最前面为“1111”,所以地址的网络号取值于240~255之间。 回送地址:127.0.0.1。 也是本机地址,等效于localhost或本机IP。 一般用于测试使用。例如:ping 127.0.0.1来测试本机TCP/IP是否正常。]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java内部类]]></title>
<url>%2F2018%2F11%2F05%2FJava%E5%86%85%E9%83%A8%E7%B1%BB%2F</url>
<content type="text"><![CDATA[类中的定义成员有三种,分别是 字段 方法 内部类 内部类:定义在类结构中的另一个类 为什么要使用内部类? 增强封装,把内部类隐藏在外部类之内,不允许其他类访问该类 内部类能提高代码的可读性和可维护性,把小型类嵌入到外部类中结构上代码会更清晰。 内部类可以直接访问外部类的成员 内部类根据使用不同的修饰符或者存在的位置不同,可以分为四种: 实例内部类 静态内部类 局部内部类 匿名内部类(仅适合于一次使用的类) 实例内部类 静态内部类 局部内部类 主要特征 内部类的实例引用特定的外部类的实例 内部类的实例不与外部类的任何实例关联 可见范围是所在的方法 可用修饰符 访问控制修饰符,abstract,final 访问控制修饰符,static,abstract,final abstract,final 可以访问外部类的哪些成员 可以直接访问外部类的所有成员 只能直接访问外部类的静态成员 可以直接访问外部类的所有成员,并且能访问所在方法的final类型的变量和参数 拥有成员类型 只能拥有实例成员 可以拥有静态成员和实例成员 只能拥有实例成员 外部类如何访问内部类的成员 必须通过内部类的实例来访问 对于静态成员,可以通过内部类的完整类名来访问 必须通过内部类的实例来访问]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java中的接口和抽象类]]></title>
<url>%2F2018%2F11%2F04%2FJava%E4%B8%AD%E7%9A%84%E6%8E%A5%E5%8F%A3%E5%92%8C%E6%8A%BD%E8%B1%A1%E7%B1%BB%2F</url>
<content type="text"><![CDATA[相同点: 都位于继承的顶端,用于被其他实现或继承 都不能实例化 都可以定义抽象方法,其子类/实现类都必须复写这些抽象方法。 不同点: 接口没有构造方法,抽象类有构造方法。这是因为子类继承抽象类后,在实例化一个子类时,需要检查父类的是否有构造方法。 抽象类可包含骗人通方法和抽象方法,接口只能包含抽象方法(java8之前) 一个类只能继承一个直接父类(可能是抽象类),接口是多继承并且支持一个类实现多个接口(弥补了Java的单继承) 成员变量:接口里默认是public static final,抽象类默认包权限 方法:接口里默认public abstract,抽象类默认包访问权限 内部类:接口里默认public static,抽象类默认包访问权限 如果接口和实现类可以完成与其他方法实现的其他功能,尽量使用接口,面向接口编程。 设计模式:接口和抽象类集合使用的(适配器模式) 面向接口编程:多态的好处:把实现类对象付给接口类型变量,屏蔽了不同实现类之间的差异,从而可以做到通用编程 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950package com.wkx.jedis;interface IUSB{ void swapData();}class Mouse implements IUSB{ public void swapData(){ System.out.println("i am moving..."); }}class Printer implements IUSB{ public void swapData(){ System.out.println("i am printing, dididi..."); }}class MotherBoard{ private static IUSB[] usbs = new IUSB[6]; private static int num = 0; public static void pluginIn(IUSB usb){ if (usbs.length == num) return; usbs[num] = usb; num ++; } public static void doWork(){ for (IUSB usb : usbs){ if(usb != null) usb.swapData(); } }}public class IUSBDemo { public static void main(String[] args){ MotherBoard.pluginIn(new Mouse()); MotherBoard.pluginIn(new Mouse()); MotherBoard.pluginIn(new Mouse()); MotherBoard.pluginIn(new Printer()); MotherBoard.pluginIn(new Printer()); MotherBoard.pluginIn(new Printer()); MotherBoard.pluginIn(new Printer()); MotherBoard.doWork(); }}]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>面试</tag>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Redis之数据类型的内部编码]]></title>
<url>%2F2018%2F11%2F01%2FRedis%E4%B9%8B%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%86%85%E9%83%A8%E7%BC%96%E7%A0%81%2F</url>
<content type="text"><![CDATA[哈希哈希类型内部编码有两种形式: ziplist(压缩列表,感觉与python中的zip方法类似,有待验证。。。):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512)、同时所有值都小于hash-max-ziplist-value配置(默认64字节),Redis会使用ziplist作为哈希的内部实现。ziplist使用更加紧凑结构实现多个元素的连续存储,所以在节省内存方面比hashtable更有优势。 hashtable(哈希表):当哈希类型无法满足ziplist条件时,Redis会使用hashtable作为哈希的内部实现,因此此时ziplist读写效率会下降,hashtable读写的时间复杂度为O(1). 列表从Redis3.2之后开始提供了quicklist内部编码,它是一种将ziplist和linkedlist结合的一种编码方式。 ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素值都小于list-max-ziplist-value配置(默认64字节),Redis会选用ziplist来作为列表内部的实现来减小内存使用。 linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。 Redis3.2之后提供了quicklist内部编码,简单的说它是一个ziplist为节点的linkedlist,它结合了两者的优势,为列表类型提供了一种更为优秀的内部编码实现。 集合集合类型的内部编码也有两种: intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个),Redis会选用intset来作为集合的内部实现,从而减小内存的使用。 hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。]]></content>
<categories>
<category>Redis</category>
<category>数据类型</category>
</categories>
<tags>
<tag>Redis</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java包装类中的缓存设计]]></title>
<url>%2F2018%2F10%2F30%2FJava%E5%8C%85%E8%A3%85%E7%B1%BB%E4%B8%AD%E7%9A%84%E7%BC%93%E5%AD%98%E8%AE%BE%E8%AE%A1%2F</url>
<content type="text"><![CDATA[包装类中的缓存设计(享元模式-FlyWeight),本质就是缓存设计。 Byte/Short/Integer/Long:缓存[-128, 127]区间的数据;Character:缓存[0,127]区间的数据.首先来看一个有意思的面试题 1234567891011121314151617181920212223242526272829303132333435public class Test { public static void main(String[] args) { //case 1 Integer i1 = new Integer(123); Integer i2 = new Integer(123); System.out.println(i1 == i2); //false //case 2 Integer i3 = Integer.valueOf(123); Integer i4 = Integer.valueOf(123); System.out.println(i3 == i4); //true //case 3 Integer i5 = 123; //自动装箱操作,底层编译后代码为Integer.valueOf(123); 和case2相同 Integer i6 = 123; System.out.println(i5 == i6); // true System.out.println(">>>>>>>>>>>>>>>>>>>"); //case 4 Integer ii1 = new Integer(250); Integer ii2 = new Integer(250); System.out.println(ii1 == ii2); //false //case 5 Integer ii3 = Integer.valueOf(250); // 250不在缓存区间内,就得new新对象 Integer ii4 = Integer.valueOf(250); System.out.println(ii3 == ii4); // false // case 6 Integer ii5 = 250; Integer ii6 = 250; System.out.println(ii5 == ii6); // false }} 查看一下integer源码,如下所示。可以看到Integer的缓存空间在[-128, 127]之间。当传入的数值在此区间内时,之间调用缓存内的数据。反之,重新return new Integer(i);在堆空间返回一个新整形对象。 那么我们应该如何比较两个对象的值得大小呢?正确的方法是使用equals来比较,equals方法来自于Object根对象,按照官方的建议,在构造子类对象时需要复写父类中的equals方法,来比较我们关心的数据,而不是内存地址(==比较的是内存地址)。现将Integer中的equals拿出来检查一下复写情况 ((Integer)obj).intValue()拆箱操作,value == ((Integer)obj).intValue();使用基本的数据类型进行比较。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); // 拆箱操作,比较基本数据类型 } return false;}public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // 初始化操作 // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java中的单例模式]]></title>
<url>%2F2018%2F10%2F30%2FJava%E4%B8%AD%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%2F</url>
<content type="text"><![CDATA[1. 饿汉模式123456789101112131415161718192021222324252627public class SingletonDemo { public static void main(String[] args) { ArrayUtil.getInstance().sort(null); }}class ArrayUtil { //1. 在该类中,实例化一个实例 private static final ArrayUtil instance = new ArrayUtil(); //2. 私有化自身的构造器,防止外界通过构造器new对象 private ArrayUtil() { } //3. 对外开放一个静态公共方法,用于获取对象 public static ArrayUtil getInstance(){ return instance; } public void sort(int[] array) { //此处编写排序代码 System.out.println("I'm sorting " + array); }}]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java中的代码块]]></title>
<url>%2F2018%2F10%2F30%2FJava%E4%B8%AD%E7%9A%84%E4%BB%A3%E7%A0%81%E5%9D%97%2F</url>
<content type="text"><![CDATA[什么是代码块:在类或者方法中,直接使用{}括起来的一段代码,表示一块带啊区域。 代码在块中属于局部变量,只在自己所在的花括号区域内有效。根据代码块所定义的位置不同,代码块分为三种形式 局部代码块:直接定义在方法内部的代码块。一般的,不会直接使用局部代码块,只不过会结合if、while等关键字,表示一块代码区域。 初始化代码块(构造代码块):直接定义在类中。每次创建对象的时候都会执行初始化代码块:每次创建对象的时候都会调用构造器,在调用构造器之前,会先执行本类中的初始化代码块。PS:通过反编译,可以看到,初始化代码也作为构造器最初的语句。一般不使用这种用法,因为不够优雅美观。即使要初始化操作,一般在构造器中进行初始化即可。或者专门定义一个方法做初始化操作,方法哦构造器中进行调用。 静态代码块:使用static修饰的初始化代码块。静态代码块在主方法执行之前进行调用,而且只会执行一次。main方法是程序的入口,静态代码块由于main方法执行。静态成员随着字节码的加载也加载进JVM,此时main还没执行,因为方法需要JVM调用,先把字节码加载进JVM,然后JVM再调用main方法。一般的,我们使用静态代码块来做初始化操作,加载资源,加载配置文件等。 1234567891011121314151617181920212223242526272829303132class CodeBlockDemo{ { System.out.println("初始化代码块"); } CodeBlockDemo(){ System.out.println("构造器。。。"); } static{ System.out.println("静态代码块"); } public static void main(String[] args){ System.out.println("进入main方法"); //创建三个匿名对象 new CodeBlockDemo(); new CodeBlockDemo(); new CodeBlockDemo(); }}/*执行结果如下静态代码块进入main方法初始化代码块构造器。。。初始化代码块构造器。。。初始化代码块构造器。。。*/ 一道Java面试题1234567891011121314151617181920212223242526272829303132333435363738394041424344public class App { private static App d = new App(); private SuperClass t = new SubClass(); //先确定依赖,由此开始作为入口 static{ System.out.println(4); } App(){ System.out.println(3); } public static void main(String[] args){ System.out.println("Hello"); }}class SuperClass{ SuperClass(){ System.out.println("构造SuperClass"); }}class SubClass extends SuperClass{ static { System.out.println(1); } SubClass(){ //super(); System.out.println(2); }}//执行结果1构造SuperClass234Hello 分析: 首先在执行子类subclass构造器的时候先执行隐藏的super();来执行父类的构造器,也就是构造SuperClass在2之前打印. 为什么不先打印4而是先打印子类的1?原因是class App依赖于依赖于subclass,会优先编译subclass,也就是说会优先编译被依赖的优先存在的类。所以首先把subclass加载到虚拟机,因此首先打印1.编译的时候首先确定依赖!!! 非static字段的初始化都在构造器中执行,也就是说App类反编译后如下。可以看出,打印1后,在APP构造器中实例化subclass,此时,先打印父类构造器中的构造SuperClass,然后打印子类的2。紧接着继续执行APP构造器的代码块,打印app构造器中的3。 123456789101112131415161718public class App { private static App d = new App(); private SuperClass t = null; //先确定依赖,由此开始作为入口 static{ System.out.println(4); } App(){ t = new SubClass(); //非static字段的初始化都在构造器中执行 System.out.println(3); } public static void main(String[] args){ System.out.println("Hello"); }} 以上依赖关系确认完毕,然后开始将App加载进虚拟机,执行由于静态代码块优先于main,静态代码块执行,打印4,最后执行main方法,打印hello.]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java编译类型和运行类型]]></title>
<url>%2F2018%2F10%2F29%2FJava%E7%BC%96%E8%AF%91%E7%B1%BB%E5%9E%8B%E5%92%8C%E8%BF%90%E8%A1%8C%E7%B1%BB%E5%9E%8B%2F</url>
<content type="text"><![CDATA[12345678910111213class Animal{}class Dog extends Animal{ public void eat(){}}----------Animal a = new Dog();a.eat()//编译报错((Dog) a).run()//编译通过 如上的简易代码,编译时会报错。编译类型:Animal a运行类型:new Dog() 编译时,编译类型会根据a.eat()查找eat方法,找不到就会报错。使用强制类型转换可以解决该问题,即((Dog) a).run()]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java之Object根类]]></title>
<url>%2F2018%2F10%2F29%2FJava%E4%B9%8BObject%E6%A0%B9%E7%B1%BB%2F</url>
<content type="text"><![CDATA[Object本身就是指对象的意思。开发时发现对象具有一些共通的行为,因此抽象出一个类:Object,来表示对象类,其他都会继承于Object类,也就是Object中的方法。 引用数据类型:类/接口/数组,引用类型又称为对象类,所谓的数组变量名称应该指代数组对象。Object类常用方法: protected void finalize():当垃圾回收器确定不存在对该对象的更多引用时,由对象回收期调用此方法。垃圾回收器在回收某一个对象之前会调用该方法,做扫尾操作。 Class getClass:返回当前对象的真实类型。 int hashCode():返回该对象的哈希值,hashCode决定了对象在哈希表中的存储位置,不同对象的hashCode是不一样的。 boolean equals(Object obj): 拿当前对象(this)和参数obj比较。在Object类中的equals方法,本身和’==’符号相同,都是比较的内存地址。官方建议:每个类都应该赋写equals方法,不要比较内存地址,而是比较我们关心的数据。 String toString():表示把一个对象转换为字符串表示。在调用打印时,其实打印的就是对象的toString方法。System.out.println(obj);等价于System.out.println(obj.toString);(决堤可以查看Java源代码)。默认情况下打印对象,打印的是对象的十六进制hashCode,但我们更关心对象中的真实存储数据。官方建议:每个类最好赋写toString方法,返回我们关心的数据。]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java之super和this]]></title>
<url>%2F2018%2F10%2F29%2FJava%E4%B9%8Bsuper%E5%92%8Cthis%2F</url>
<content type="text"><![CDATA[this: 当前的对象,哪一个对象调用this,this就代指哪一个对象。使用在某一个对象中。super: 当前对象的父类方法或构造器。使用在继承关系中。 super 当new一个子类对象的时候,会先创建一个父类对象。可以认为,在调用子类构造器之前,在子类构造器中会先调用父类的构造器,默认调用的是父类无参数的构造器。调用父类构造器后悔创建一个父类的对象。 如果父类不存在可以被子类访问的构造器,则不能存在子类,即子类不会被创建成功。 如果父类没有提供无参数的构造器,则此时子类必须显式的通过super语句去掉用父类带参数的构造器。 子类构建的所有的行为建立在第一条规则基础之上。 1234567891011121314151617181920212223242526class Animal { private String name; private int age; Animal(String name, int age){ this.name = name; this.age = age; System.out.println("animal constructor"); } Animal(String color){ System.out.println("this is a " + color +" color"); } public void say(){ System.out.println("Animals Say sth..."); }}class Fish extends Animal{ private String color; Fish(){ //构造器中的第一句必须为super,如果不写,则默认调用super() super("RED"); //子类没有提供无参数的构造器,必须显示super滴啊用,否则会编译失败 System.out.println("Fish Constructor"); }} 一个简单的示意图如下: this存在于位置 在构造器中,表示当前创建的对象 在方法中,哪一个对象调用this所在的方法,那么此时this就表示哪一个对象。 this的使用 解决成员变量的参数(局部变量)之间的二义性,必须使用this区分 同类中实例方法互相调用时可以省略this关键字,但是不建议省略 将this作为参数传递给另外一个方法 将this作为方法的返回值(链式方法编程) 构造器函数的互相调用,this([参数])必须写在构造器内的第一行,与super类似。 this、super不能static一起使用。原因是当字节码被加载进jvm时,static成员已经存在了,但是此时对象还没有被创建,没有对象,也就没有this。 123456789101112131415161718User(String name){ this.name = name;}User(String name, int age){ this(name); //表示在调用参数为string类型的构造器 this.age = age;}//上述调用方式为多参数构造器调用少参数构造器,一般工程经验为少参数构造器调用多参数构造器//对于多余的参数,使用默认值赋值即可,如下User(String name, int age){ this.name = name; this.age = age;}User(String name){ this(name, 0);//此处对int age赋值为0即可} 是]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java方法的值传递机制]]></title>
<url>%2F2018%2F10%2F26%2FJava%E6%96%B9%E6%B3%95%E7%9A%84%E5%80%BC%E4%BC%A0%E9%80%92%E6%9C%BA%E5%88%B6%2F</url>
<content type="text"><![CDATA[对于基本数据类型(八种基本数据类型)来说,方法形参传递的是值的副本; 对于引用数据类型,方法的形参传递的是引用的地址值的副本; 对于引用数据类型,JVM中的存储图如下:]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[四种方法实现交换两个变量的值]]></title>
<url>%2F2018%2F10%2F25%2F%E5%9B%9B%E7%A7%8D%E6%96%B9%E6%B3%95%E5%AE%9E%E7%8E%B0%E4%BA%A4%E6%8D%A2%E4%B8%A4%E4%B8%AA%E5%8F%98%E9%87%8F%E7%9A%84%E5%80%BC%2F</url>
<content type="text"><![CDATA[方法1 位运算12345a = a ^ bb = a ^ ba = a ^ b原理:a^b^b == a 方法2 栈实现123456stack SS.push(x)S.push(y)x = S.pop()y = S.pop() 方法3 借助第三变量方法4 算术运算]]></content>
<tags>
<tag>Funny</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java中的+=赋值运算]]></title>
<url>%2F2018%2F10%2F24%2FJava%E4%B8%AD%E7%9A%84-%E8%B5%8B%E5%80%BC%E8%BF%90%E7%AE%97%2F</url>
<content type="text"><![CDATA[1234567891011int a = 10;a += 5;System.out.println(a); //15short s = 30;s = s + 5;//编译报错//Error:(23, 15) java: incompatible types: possible lossy conversion from int to shortshort s = 30;s += 5;//等价于 s = short(s+5),该表达式自带隐式类型转换 上述代码中,变量s为short类型,s+5的结果为int类型,int类型赋值给short类型,编译报错。错误信息为如下:Error:(23, 15) java: incompatible types: possible lossy conversion from int to short 关于自动类型转换,也称为隐式类型转换当把小数据范围类型的数值或变量赋值给另一个大数据范围类型变量时,系统可以自动完成类型转换。boolean类型是不可以转换为其他数据类型。 强制类型转换,也称为显示类型转换当把大范围类型的数值或变量赋值给另一个小范围类型变量时,此时系统不能自动完成转换,需要加上强制转换符,但这样的操作可能在成数据精度的降低或溢出,使用时需要格外注意。]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[C语言中的'if(n==1)'和'if(1==n)'的区别]]></title>
<url>%2F2018%2F09%2F29%2FC%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84-if-n-1-%E5%92%8C-if-1-n-%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[C语言中两种if语句判断方式。请问哪种写法更好?为什么?123int n;if (n == 1) // 第一种判断方式if (1 == n) // 第二种判断方式 第一种写法缺点 在写判断语句的时候很容易漏写一个“=” ,比如把if (n == 1) { } 写成 if (n = 1) { }。而在C中 “n = 1” 是有返回值的, 这个返回值是1, 于是原语句相当于if ( 1 ) { } ,即条件永远为真,{ } 内的代码始终能够得到执行,当这样的错误深深埋藏在代码当中,非常难以debug。 优点 主要目的是防止写成if(n = 1)而导致错误,但现在的编译器一般会给出警告信息所以现在不常用了.第1种更符合习惯,只要把相应的编译选项打开,一般不会出问题。 第二种写法缺点 反人类优点 if ( 1 == n ) { } 及时少写一个”=”, 编译器就会报错,省得在运行期调试得死去活来。 会省掉很多 debug 的时间的。]]></content>
<categories>
<category>面试</category>
</categories>
<tags>
<tag>面试</tag>
</tags>
</entry>
<entry>
<title><![CDATA[如何理解http是无连接,无状态的?]]></title>
<url>%2F2018%2F09%2F28%2F%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3http%E6%98%AF%E6%97%A0%E8%BF%9E%E6%8E%A5%EF%BC%8C%E6%97%A0%E7%8A%B6%E6%80%81%E7%9A%84%EF%BC%9F%2F</url>
<content type="text"><![CDATA[HTTP 是一个属于应用层的面向对象的协议,HTTP 协议一共有五大特点: 支持客户/服务器模式; 简单快速; 灵活; 无连接; 无状态。 无连接无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 ps:早期这么做的原因是 HTTP 协议产生于互联网,因此服务器需要处理同时面向全世界数十万、上百万客户端的网页访问,但每个客户端(即浏览器)与服务器之间交换数据的间歇性较大(即传输具有突发性、瞬时性),并且网页浏览的联想性、发散性导致两次传送的数据关联性很低,大部分通道实际上会很空闲、无端占用资源。因此 HTTP 的设计者有意利用这种特点将协议设计为请求时建连接、请求完释放连接,以尽快将资源释放出来服务其他客户端 随着时间的推移,网页变得越来越复杂,里面可能嵌入了很多图片,这时候每次访问图片都需要建立一次 TCP 连接就显得很低效。后来,Keep-Alive 被提出用来解决这效率低的问题。 Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接。市场上的大部分 Web 服务器,包括 iPlanet、IIS 和 Apache,都支持 HTTP Keep-Alive。对于提供静态内容的网站来说,这个功能通常很有用。但是,对于负担较重的网站来说,这里存在另外一个问题:虽然为客户保留打开的连接有一定的好处,但它同样影响了性能,因为在处理暂停期间,本来可以释放的资源仍旧被占用。当Web服务器和应用服务器在同一台机器上运行时,Keep-Alive 功能对资源利用的影响尤其突出。 这样一来,客户端和服务器之间的 HTTP 连接就会被保持,不会断开(超过 Keep-Alive 规定的时间,意外断电等情况除外),当客户端发送另外一个请求时,就使用这条已经建立的连接。 无状态协议的状态是指下一次传输可以“记住”这次传输信息的能力,http是不会为了下一次连接而维护这次连接所传输的信息的.HTTP无状态是指,当浏览器发送请求给服务器的时候,服务器响应,但是同一个浏览器再发送请求给服务器的时候,它会响应,但是他不知道你就是刚才那个浏览器,简单地说,就是服务器不会去记得你,所以是无状态协议。 无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求,会给我们发送数据过来,但是,发送完,不会记录任何信息。HTTP 是一个无状态协议,这意味着每个请求都是独立的,Keep-Alive 没能改变这个结果。HTTP 协议这种特性有优点也有缺点,优点在于解放了服务器,每一次请求“点到为止”不会造成不必要连接占用,缺点在于每次请求可能会传输大量重复的内容信息。 ps:客户端与服务器进行动态交互的 Web 应用程序出现之后,HTTP 无状态的特性严重阻碍了这些应用程序的实现,毕竟交互是需要承前启后的,简单的购物车程序也要知道用户到底在之前选择了什么商品。于是,两种用于保持 HTTP 连接状态的技术就应运而生了,一个是 Cookie,而另一个则是 Session。 本文来自 Mike__Jiang 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/tennysonsky/article/details/44562435?utm_source=copy]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JSP相关概念]]></title>
<url>%2F2018%2F09%2F28%2FJSP%E7%9B%B8%E5%85%B3%E6%A6%82%E5%BF%B5%2F</url>
<content type="text"><![CDATA[JSP内置对象: request:负责得到客户端请求的信息,对应类型:javax.servlet.http.HttpServletRequest response:负责向客户端发出响应,对应类型:javax.servlet.http.HttpServletResponse session:负责保存同一客户端一次会话过程中的一些信息,对应类型:javax.servlet.http.httpsession out: 负责管理对客户端的输出,对应类型:javax.serlvet.jsp.jspwriter application:表示整个应用环境的信息,对应类型:javax.servlet.servletcontext config:表示ServletConfig,对应类型:javax.servlet.servletconfig exception:表示页面中发生的异常,可以通过它获得页面异常信息,对应类型:java.lang.exception pagecontext:表示这个JSP页面上下文,对应类型:javax.servlet.jsp.pagecontext page:表示当前JSP页面本身。 JSP的四种作用域 page是代表一个页面相关的对象和属性。一个页面由一个编译好的java servlet类(可以带有include指令,但不可以带有include动作)表示。这既包括servlet又包括编译成servlet的jsp页面。 request是代表与web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个web组件(由于forward指令和include动作的关系) session是代表与用于某个web客户机的一个用户体验相关的对象和属性。一个web回话也可以经常跨域多个客户机请求。 application是代表与整个web应用程序相关的对象和属性。这实质上是跨域整个web应用程序,包括多个页面、请求和回话的一个全局作用域]]></content>
</entry>
<entry>
<title><![CDATA[面试总结之Java Web篇(持续更新)]]></title>
<url>%2F2018%2F09%2F27%2F%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93%E4%B9%8BJava-Web%E7%AF%87-%E6%8C%81%E7%BB%AD%E6%9B%B4%E6%96%B0%2F</url>
<content type="text"></content>
<categories>
<category>面试</category>
<category>Java Web</category>
</categories>
<tags>
<tag>面试</tag>
<tag>Java Web</tag>
</tags>
</entry>
<entry>
<title><![CDATA[数据库范式]]></title>
<url>%2F2018%2F09%2F26%2F%E6%95%B0%E6%8D%AE%E5%BA%93%E8%8C%83%E5%BC%8F%2F</url>
<content type="text"><![CDATA[第一范式(1NF)所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分。简而言之,第一范式就是无重复的域。说明:在任何一个关系数据库中,第一范式(1NF)是对关系模式的设计基本要求,一般设计中都必须满足第一范式(1NF)。不过有些关系模型中突破了1NF的限制,这种称为非1NF的关系模型。换句话说,是否必须满足1NF的最低要求,主要依赖于所使用的关系模型。 第二范式(2NF)在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。例如在员工表中的身份证号码即可实现每个一员工的区分,该身份证号码即为候选键,任何一个候选键都可以被选作主键。在找不到候选键时,可额外增加属性以实现区分,如果在员工关系中,没有对其身份证号进行存储,而姓名可能会在数据库运行的某个时间重复,无法区分出实体时,设计辟如ID等不重复的编号以实现区分,被添加的编号或ID选作主键。(该主键的添加是在ER设计时添加,不是建库时随意添加)第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。 第三范式(3NF)在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。可以理解为消除冗余。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性,也就是在满足2NF的基础上,任何非主属性不得传递依赖于主属性。]]></content>
<categories>
<category>面试</category>
<category>数据库</category>
</categories>
<tags>
<tag>面试</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[数据库事务的四个特性及四个隔离级别]]></title>
<url>%2F2018%2F09%2F26%2F%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1%E7%9A%84%E5%9B%9B%E4%B8%AA%E7%89%B9%E6%80%A7%E5%8F%8A%E5%9B%9B%E4%B8%AA%E9%9A%94%E7%A6%BB%E7%BA%A7%E5%88%AB%2F</url>
<content type="text"><![CDATA[事务的四大特性(ACID) 原子性(atomicity):一个事务必须视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。 一致性(consistency):数据库总数从一个一致性的状态转换到另一个一致性的状态。 隔离性(isolation):一个事务所做的修改在最终提交以前,对其他事务是不可见的。 持久性(durability):一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。 四个隔离级别数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。下面通过事例一一阐述它们的概念与联系。 Read uncommitted读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。 Read committed读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。那怎么解决不可重复读问题?Repeatable read ! Repeatable read重复读,就是在开始读取数据(事务开启)时,不再允许修改操作事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为**幻读问题对应的是插入INSERT操作**,而不是UPDATE操作。 什么时候会出现幻读?事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。那怎么解决幻读问题?Serializable! Serializable 序列化Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。值得一提的是:大多数数据库默认的事务隔离级别是Read committed,比如Sql Server ,Oracle。MySQL的默认隔离级别是Repeatable read。 本文来自 csdnxingyuntian 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/csdnxingyuntian/article/details/57081233?utm_source=copy]]></content>
<categories>
<category>面试</category>
<category>数据库</category>
</categories>
<tags>
<tag>面试</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[PostgreSQL函数、索引和视图操作Demo]]></title>
<url>%2F2018%2F09%2F26%2FPostgreSQL%E5%87%BD%E6%95%B0%E3%80%81%E7%B4%A2%E5%BC%95%E5%92%8C%E8%A7%86%E5%9B%BE%E6%93%8D%E4%BD%9CDemo%2F</url>
<content type="text"><![CDATA[创建演示数据表结构1234567891011121314151617181920212223242526272829303132333435363738394041424344--创建dept表CREATE TABLE dept ( d_no INT PRIMARY KEY, --部门编号 d_name VARCHAR(30), --部门名称 d_location VARCHAR(300) --部门所在地址);--dept表初始化数据INSERT INTO dept VALUES (10, '开发部', '北京市海淀区');INSERT INTO dept VALUES (20, '测试部', '北京市东城区');INSERT INTO dept VALUES (30, '销售部', '上海市');INSERT INTO dept VALUES (40, '财务部', '广州市');INSERT INTO dept VALUES (50, '运维部', '武汉市');INSERT INTO dept VALUES (60, '集成部', '南京市');--创建employee表CREATE TABLE employee ( e_no INT PRIMARY KEY, --雇员编号 e_name VARCHAR(30) NOT NULL, --雇员名称 e_gender CHAR(2) NOT NULL, --性别,f:女,m:男 dept_no INT, --所在部门编号 e_job VARCHAR(50) NOT NULL, --职位 e_salary NUMERIC(9, 2), --工资 e_hireDate DATE, --入职日期 CONSTRAINT fk_emp_deptno FOREIGN KEY (dept_no) REFERENCES dept(d_no));--初始化employee表INSERT INTO employee VALUES (100, '赵志军', 'f', 10, '开发工程师', 5000, '2010-01-01');INSERT INTO employee VALUES (101, '张铭雨', 'f', 10, '开发工程师', 6000, '2012-04-04');INSERT INTO employee VALUES (102, '许锋', 'f', 10, '开发经理', 8000, '2008-01-01');INSERT INTO employee VALUES (103, '王嘉琦', 'm', 20, '测试工程师', 4500, '2013-08-12');INSERT INTO employee VALUES (104, '李江新', 'f', 20, '测试工程师', 5000, '2011-08-16');INSERT INTO employee VALUES (105, '张海影', 'm', 20, '测试经理', 6000, '2009-11-12');INSERT INTO employee VALUES (106, '马恩波', 'f', 30, '销售人员', 3000, '2014-09-01');INSERT INTO employee VALUES (107, '李慧敏', 'm', 30, '销售人员', 5000, '2010-08-14');INSERT INTO employee VALUES (108, '马爽爽', 'm', 30, '销售经理', 9000, '2006-12-02');INSERT INTO employee VALUES (109, '史晓云', 'm', 30, '销售高级经理', 12000, '2003-07-14');INSERT INTO employee VALUES (110, '刘燕凤', 'm', 40, '财务人员', 3000, '2011-06-01');INSERT INTO employee VALUES (111, '王科', 'f', 40, '财务人员', 3500, '2010-05-01');INSERT INTO employee VALUES (112, '李林英', 'm', 40, '财务经理', 5000, '2008-05-07');INSERT INTO employee VALUES (113, '李杨', 'f', 10, '实习工程师', NULL, '2015-05-07');INSERT INTO employee VALUES (114, '李刚', 'f', NULL, '实习工程师', NULL, '2015-05-07');INSERT INTO employee VALUES (115, '王林', 'f', NULL, '实习工程师', NULL, '2015-05-07'); 函数使用:数值函数12345678910--AVG函数SELECT AVG(e_salary) FROM employee;--COUNT函数SELECT COUNT(*) FROM employee;--MAX函数SELECT MAX(e_salary) FROM employee;--MIN函数SELECT MIN(e_salary) FROM employee;--SUM函数SELECT SUM(e_salary) FROM employee; 函数使用:字符串函数1234567891011--LENGTH函数SELECT e_name, LENGTH(e_name) FROM employee;--CONCAT函数SELECT CONCAT(e_no, '|', e_name, '|', e_salary) FROM employee;SELECT e_no, e_name, e_hireDate, CONCAT(e_no, e_name, e_hireDate) FROM employee;--TRIM函数SELECT CONCAT(' ', e_name), TRIM(CONCAT(' ', e_name)) FROM employee;--REPLACE函数SELECT e_name, REPLACE(e_name, '李', '张') FROM employee;--SUBSTRING函数SELECT e_name, SUBSTRING(e_name, 2, 3) FROM employee; 函数使用:日期时间函数1234--EXTRACT函数SELECT e_no, e_name, e_hireDate, EXTRACT(YEAR FROM e_hireDate), EXTRACT(MONTH FROM e_hireDate), EXTRACT(DAY FROM e_hireDate) FROM employee;--CURRENT_DATE、CURRENT_TIME、NOW函数SELECT CURRENT_DATE, CURRENT_TIME, now(); 创建函数示例1234567891011121314151617181920-- 函数示例CREATE FUNCTION add(INTEGER, INTEGER) RETURNS INTEGER AS ' select $1 + $2; 'LANGUAGE SQLRETURNS NULL ON NULL INPUT;--应用--创建函数CREATE OR REPLACE FUNCTION CONCAT_TEST(INTEGER, VARCHAR, DATE) RETURNS VARCHAR AS 'SELECT $1||$2||$3;' LANGUAGE SQLRETURNS NULL ON NULL INPUT;--函数调用select e_no, e_name, e_hireDate, CONCAT_TEST(e_no, e_name, e_hireDate) from employee;--删除函数DROP FUNCTION CONCAT_TEST(INTEGER, VARCHAR, DATE); 索引1234--创建索引CREATE INDEX emp_name_index ON employee (e_name);--删除索引DROP INDEX emp_name_index; 视图12345678910--创建视图CREATE VIEW V_EMP_DEV AS SELECT e_no, e_name, e_salary, e_hireDate FROM employee WHERE dept_no = 10 ORDER BY e_salary DESC;--视图调用SELECT * FROM V_EMP_DEV;--删除视图DROP VIEW V_EMP_DEV;]]></content>
<categories>
<category>面试</category>
<category>数据库</category>
</categories>
<tags>
<tag>面试</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[PostgreSQL数据操作和数据表约束Demo]]></title>
<url>%2F2018%2F09%2F26%2FPostgreSQL%E6%95%B0%E6%8D%AE%E6%93%8D%E4%BD%9C%E5%92%8C%E6%95%B0%E6%8D%AE%E8%A1%A8%E7%BA%A6%E6%9D%9FDemo%2F</url>
<content type="text"><![CDATA[插入数据 INSERT123456789101112131415161718192021222324252627282930313233--创建student数据表CREATE TABLE student ( id INT, --学生编号 name VARCHAR(30), --学生姓名 birthday DATE, --出生日期 score NUMERIC(5, 2) --学分);--向表中所有字段插入数据(顺序不能乱)INSERT INTO student VALUES (1, '张三', '1990-01-01', 3.85);--向表中指定字段插入数据INSERT INTO student(id, name, birthday) VALUES (2, '李四', '1990-01-02');--向表中指定字段插入数据(打乱字段顺序)INSERT INTO student(id, birthday, name) VALUES (3, '1988-05-01', '王五');--使用INSERT语句批量插入多条数据INSERT INTO student(id, name, birthday) VALUES (4, '张三1', '1990-02-01'), (5, '张三2', '1990-02-02'), (6, '张三3', '1990-02-03');--创建student1数据表CREATE TABLE student_new ( id INT, --学生编号 name VARCHAR(30), --学生姓名 birthday DATE, --出生日期 score NUMERIC(5, 2) --学分);--将查询结果插入到表中INSERT INTO student_new SELECT * FROM student;--将查询结果插入到表中INSERT INTO student_new(id, name, birthday) SELECT id, name, birthday FROM student; 更新数据 update12345678--指定条件更新数据update student set name = '李四1' where id = 2;--批量更新数据UPDATE student SET score = 3.76;--将指定结果更新到对应字段update student set score = 1.1+2.3 where id = 1; 删除数据 DELETE TRUNCATE123456--删除数据DELETE FROM student WHERE id = 1;DELETE FROM student WHERE birthday BETWEEN '1988-01-01' AND '1989-12-31';--TRUNCATE清空数据TRUNCATE TABLE student_new; 主键约束 PRIMARY KEY12345678910111213141516171819202122232425--创建emp表CREATE TABLE emp ( id INT PRIMARY KEY, --编号 name VARCHAR(30), --姓名 salary NUMERIC(9, 2) --工资);INSERT INTO emp VALUES (1, '张三', 3000.00);--INSERT INTO emp VALUES (1, '李四', 3000.00); --插入提示错误,主键冲突--创建emp1表CREATE TABLE emp1 ( id INT, --编号 name VARCHAR(30), --姓名 salary NUMERIC(9, 2), --工资 CONSTRAINT pk_emp1 PRIMARY KEY(id));--创建emp2表(联合主键)CREATE TABLE emp2 ( id INT, --编号 name VARCHAR(30), --姓名 salary NUMERIC(9, 2), --工资 CONSTRAINT pk_emp2 PRIMARY KEY(id, name)); 外键约束 FOREIGN KEY(field) REFERENCES123456789101112131415161718192021222324--创建dept表CREATE TABLE dept ( id INT, --编号 name VARCHAR(30), --部门名称 CONSTRAINT pk_dept PRIMARY KEY(id));INSERT INTO dept VALUES(1, '开发部');INSERT INTO dept VALUES(2, '测试部');--创建emp表CREATE TABLE emp3 ( id INT PRIMARY KEY, --编号 name VARCHAR(30), --姓名 salary NUMERIC(9, 2), --工资 deptId INT, CONSTRAINT fk_emp3_dept FOREIGN KEY(deptId) REFERENCES dept(id));--插入数据INSERT INTO emp3 VALUES(1, '张三', 3000.00, 1);--INSERT INTO emp3 VALUES(2, '李四', 3000.00, 3); --插入数据报错,外键关联数据不存在--DELETE FROM dept WHERE id = 1; --删除数据报错,存在外键关联数据DELETE FROM dept WHERE id = 2; --不存在外键关联数据,可以正常删除数据 删除表 DROP12--DROP TABLE dept; --直接删除dept表会报错,与emp3表存在关联DROP TABLE dept CASCADE; --强制递归删除数据 创建非空约束 NOT NULL123456789--创建非空约束CREATE TABLE emp4 ( id INT PRIMARY KEY, --编号 name VARCHAR(30) NOT NULL, --姓名 salary NUMERIC(9, 2) --工资);INSERT INTO emp4 VALUES(1, '张三', 3000.00);--INSERT INTO emp4 VALUES(2, NULL, 3000.00); --插入数据报错,违法非空约束 创建唯一约束 UNIQUE12345678910--创建唯一约束CREATE TABLE emp5 ( id INT PRIMARY KEY, --编号 name VARCHAR(30), --姓名 phone VARCHAR(30) UNIQUE, --电话号码 salary NUMERIC(9, 2) --工资);INSERT INTO emp5 VALUES(1, '张三', '13436652541', 3000.00);--INSERT INTO emp5 VALUES(2, '李四', '13436652541', 3000.00); --插入数据报错,违法唯一约束 创建默认值约束 default123456789--创建默认值约束CREATE TABLE emp6 ( id INT PRIMARY KEY, --编号 name VARCHAR(30), --姓名 salary NUMERIC(9, 2) default 0.0 --工资);insert into emp6 (id, name) values (1,'张三');insert into emp6 (id, name, salary) values (2,'李四', 3000);]]></content>
<categories>
<category>面试</category>
<category>数据库</category>
</categories>
<tags>
<tag>面试</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DROP、DELETE和TRUNCATE区别]]></title>
<url>%2F2018%2F09%2F26%2FDROP%E3%80%81DELETE%E5%92%8CTRUNCATE%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[Diffs DELETE TRUNCATE Drop 执行速度 慢 较快 快 可执行条件 可以 不可以 不可以 语句分类 DML DDL DDL 可以回滚事务 可以 不可以 不可以 删除操作记录日志 记录 不记录 不记录 drop:drop table 表名删除内容和定义,并释放空间。执行drop语句,将使此表的结构一起删除。 truncate (清空表中的数据):truncate table 表名删除内容、释放空间但不删除定义(也就是保留表的数据结构)。与drop不同的是,只是清空表数据而已。truncate不能删除行数据,虽然只删除数据,但是比delete彻底,它只删除表数据。 delete:delete from 表名 (where 列名 = 值)与truncate类似,delete也只删除内容、释放空间但不删除定义;但是delete即可以对行数据进行删除,也可以对整表数据进行删除。 相同点: truncate 和不带 where 子句的 delete,以及 drop 都会删除表内的数据 不同点: truncate 和 delete 只删除数据不删除表的结构(定义)drop 语句将删除表的结构被依赖的约束(constrain)、触发器(trigger)、索引(index);依赖于该表的存储过程/函数将保留,但是变为 invalid 状态。 delete 语句是数据库操作语言(dml),这操作会放到rollback segement 中,事务提交之后才生效;如果有相应的 trigger,执行的时候将被触发。truncate、drop 是数据库定义语言(ddl),操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发 trigger。 delete 语句不影响表所占用的 extent,高水线(high watermark)保持原位置不动 显然 drop 语句将表所占用的空间全部释放。 truncate 语句缺省情况下见空间释放到 minextents个 extent,除非使用reuse storage;truncate 会将高水线复位(回到最开始)。 速度,一般来说: drop> truncate > delete 安全性:小心使用 drop 和 truncate,尤其没有备份的时候.否则哭都来不及 使用上: 想删除部分数据行用 delete,注意带上where子句. 回滚段要足够大.想删除表,当然用 drop;想保留表而将所有数据删除,如果和事务无关,用truncate即可。如果和事务有关,或者想触发trigger,还是用delete。如果是整理表内部的碎片,可以用truncate跟上reuse stroage,再重新导入/插入数据。 语法123Delete from Tablename where 条件Truncate table TablenameDrop table Tablename Mysql的truncate和delete的区别truncate table table_name 和delete from table_name 都是删除表中所有记录。 区别: truncate能够快速清空一个表。并且重置auto_increment的值。而delete只能一行一行的删除。 但对于不同的类型存储引擎需要注意的地方是: A. 对于myisam truncate会重置auto_increment的值为1。而delete后表仍然保持auto_increment。 B. 对于innodb truncate会重置auto_increment的值为1。delete后表仍然保持auto_increment。但是在做delete整个表之后重启MySQL的话,则重启后的auto_increment会被置为1。 也就是说,innodb的表本身是无法持久保存auto_increment。delete表之后auto_increment仍然保存在内存,但是重启后就丢失了,只能从1开始。实质上重启后的auto_increment会从 SELECT 1+MAX(ai_col) FROM t 开始。]]></content>
<categories>
<category>面试</category>
<category>数据库</category>
</categories>
<tags>
<tag>面试</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[PostgreSQL数据查询Demo]]></title>
<url>%2F2018%2F09%2F26%2FPostgreSQL%E6%95%B0%E6%8D%AE%E6%9F%A5%E8%AF%A2Demo%2F</url>
<content type="text"><![CDATA[创建演示数据表结构1234567891011121314151617181920212223242526--创建dept表CREATE TABLE dept ( d_no INT PRIMARY KEY, --部门编号 d_name VARCHAR(30), --部门名称 d_location VARCHAR(300) --部门所在地址);--dept表初始化数据INSERT INTO dept VALUES (10, '开发部', '北京市海淀区');INSERT INTO dept VALUES (20, '测试部', '北京市东城区');INSERT INTO dept VALUES (30, '销售部', '上海市');INSERT INTO dept VALUES (40, '财务部', '广州市');INSERT INTO dept VALUES (50, '运维部', '武汉市');INSERT INTO dept VALUES (60, '集成部', '南京市');--创建employee表CREATE TABLE employee ( e_no INT PRIMARY KEY, --雇员编号 e_name VARCHAR(30) NOT NULL, --雇员名称 e_gender CHAR(2) NOT NULL, --性别,f:女,m:男 dept_no INT, --所在部门编号 e_job VARCHAR(50) NOT NULL, --职位 e_salary NUMERIC(9, 2), --工资 e_hireDate DATE, --入职日期 CONSTRAINT fk_emp_deptno FOREIGN KEY (dept_no) REFERENCES dept(d_no)); 123456789101112-- select 语句顺序SELECT {* | <字段列表>} //查询结果字段内容FROM [ <表1>,<表2>… //查询数据表 [WHERE <表达式>] //where查询条件表达式 [GROUP BY <group by definition>] //group by数据分组 [HAVING <expression> [{<operator> <expression>}…]] [ORDER BY <order by definition>] //查询结果排序 [LIMIT [<offset>,] <row count>] //限制结果显示数量 ] 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111--初始化employee表INSERT INTO employee VALUES (100, '赵志军', 'f', 10, '开发工程师', 5000, '2010-01-01');INSERT INTO employee VALUES (101, '张铭雨', 'f', 10, '开发工程师', 6000, '2012-04-04');INSERT INTO employee VALUES (102, '许锋', 'f', 10, '开发经理', 8000, '2008-01-01');INSERT INTO employee VALUES (103, '王嘉琦', 'm', 20, '测试工程师', 4500, '2013-08-12');INSERT INTO employee VALUES (104, '李江新', 'f', 20, '测试工程师', 5000, '2011-08-16');INSERT INTO employee VALUES (105, '张海影', 'm', 20, '测试经理', 6000, '2009-11-12');INSERT INTO employee VALUES (106, '马恩波', 'f', 30, '销售人员', 3000, '2014-09-01');INSERT INTO employee VALUES (107, '李慧敏', 'm', 30, '销售人员', 5000, '2010-08-14');INSERT INTO employee VALUES (108, '马爽爽', 'm', 30, '销售经理', 9000, '2006-12-02');INSERT INTO employee VALUES (109, '史晓云', 'm', 30, '销售高级经理', 12000, '2003-07-14');INSERT INTO employee VALUES (110, '刘燕凤', 'm', 40, '财务人员', 3000, '2011-06-01');INSERT INTO employee VALUES (111, '王科', 'f', 40, '财务人员', 3500, '2010-05-01');INSERT INTO employee VALUES (112, '李林英', 'm', 40, '财务经理', 5000, '2008-05-07');INSERT INTO employee VALUES (113, '李杨', 'f', 10, '实习工程师', NULL, '2015-05-07');INSERT INTO employee VALUES (114, '李刚', 'f', NULL, '实习工程师', NULL, '2015-05-07');INSERT INTO employee VALUES (115, '王林', 'f', NULL, '实习工程师', NULL, '2015-05-07');--查询所有字段--查询所有字段内容(使用*号通配符)SELECT * FROM employee;--查询所有字段内容(穷举所有字段) --讲解一下使用*号的弊端SELECT e_no, e_name, e_gender, dept_no, e_job, e_salary, e_hireDate FROM employee;--查询指定字段数据内容SELECT e_no, e_name, e_hireDate FROM employee;--使用别名SELECT d_no, d_name, d_location FROM dept;--查询指定记录,带条件查询--指定条件查询数据SELECT e_no, e_name, e_hireDate FROM employee WHERE e_salary = 5000;SELECT e_no, e_name, e_gender FROM employee WHERE e_gender = 'f';SELECT e_no, e_name, e_salary FROM employee WHERE e_salary < 5000;--复杂条件查询--带IN关键字的条件查询SELECT e_no, e_name, dept_no FROM employee WHERE dept_no IN (20, 30);SELECT e_no, e_name, dept_no FROM employee WHERE dept_no NOT IN (20, 30);--带BETWEEN AND关键字的范围查询SELECT e_no, e_name, e_hireDate FROM employee WHERE e_hireDate BETWEEN '2010-01-01' AND '2015-01-01';SELECT e_no, e_name, e_salary FROM employee WHERE e_salary NOT BETWEEN 5000 AND 8000;--带LIKE的字符匹配查询SELECT e_no, e_name FROM employee WHERE e_name LIKE '李%';SELECT e_no, e_name FROM employee WHERE e_name NOT LIKE '李_';--查询空值内容SELECT e_no, e_name, e_salary FROM employee WHERE e_salary IS NULL;SELECT e_no, e_name, e_salary FROM employee WHERE e_salary IS NOT NULL;--带AND的多条件查询SELECT e_no, e_name, e_gender, dept_no FROM employee WHERE e_gender = 'f' AND dept_no = 10;SELECT e_no, e_name, e_gender, dept_no FROM employee WHERE e_gender = 'f' AND dept_no IN (10, 30);--带OR的多条件查询SELECT e_no, e_name, dept_no FROM employee WHERE dept_no = 10 OR dept_no = 20;SELECT e_no, e_name, dept_no FROM employee WHERE dept_no IN (10, 20);--对查询结果进行排序SELECT e_salary FROM employee ORDER BY e_salary ASC;SELECT e_salary FROM employee ORDER BY e_salary DESC;SELECT e_salary, e_hireDate FROM employee ORDER BY e_salary ASC, e_hireDate DESC;--用LIMIT限制查询结果的数量SELECT * FROM employee LIMIT 5;SELECT * FROM employee LIMIT 4 OFFSET 3;--连接查询--内连接查询SELECT e_no, e_name, e_job, d_name, d_location FROM employee, dept WHERE dept_no = d_no;SELECT e_no, e_name, e_job, d_name, d_location FROM employee INNER JOIN dept ON dept_no = d_no WHERE dept_no = 10;--外连接查询--左连接查询SELECT e.e_no, e.e_name, e.dept_no, d.d_name, d.d_location FROM employee e LEFT JOIN dept d ON e.dept_no = d.d_no--右连接查询SELECT e.e_no, e.e_name, e.dept_no, d.d_name, d.d_location FROM employee e RIGHT JOIN dept d ON e.dept_no = d.d_no--子查询--带EXISTS关键字的子查询-- 先在外层查询中取“学生表”的第一行记录,用该记录的相关的属性值(在内层WHERE子句中给定的)处理内层查询,若外层的WHERE子句返回“TRUE”值,则这条记录放入结果表中。然后再取下一行记录;重复上述过程直到外层表的记录全部遍历一次为止。SELECT * FROM employee WHERE EXISTS (SELECT d_no FROM dept WHERE d_name = '开发部');SELECT * FROM employee WHERE EXISTS (SELECT d_no FROM dept WHERE d_name = '开发部' AND dept_no = d_no);SELECT * FROM employee WHERE NOT EXISTS (SELECT d_no FROM dept WHERE d_name = '开发部' AND dept_no = d_no);--带IN关键字的子查询select * from employee WHERE dept_no in (select d_no from dept WHERE d_name = '开发部');select * from employee WHERE dept_no not in (select d_no from dept WHERE d_name = '开发部');--标量子查询SELECT e.e_no, e.e_name, (SELECT d_name || ' ' || d_location FROM dept d WHERE d.d_no = e.dept_no) AS address FROM employee e;SELECT e.e_no, e.e_name, (SELECT concat(d_name, '---', d_location) FROM dept d WHERE d.d_no = e.dept_no) AS address FROM employee e;--合并查询结果--使用UNION ALL合并结果SELECT e_no, e_name, dept_no, e_salary FROM employee WHERE dept_no IN (10, 20)UNION ALLSELECT e_no, e_name, dept_no, e_salary FROM employee WHERE e_salary > 5000;--使用UNION合并结果SELECT e_no, e_name, dept_no, e_salary FROM employee WHERE dept_no IN (10, 20)UNIONSELECT e_no, e_name, dept_no, e_salary FROM employee WHERE e_salary > 5000;--查询结果使用空值占位SELECT e_no, e_name, dept_no, e_salary, e_hireDate FROM employee WHERE dept_no IN (10, 20)UNION ALLSELECT e_no, e_name, dept_no, e_salary, NULL FROM employee WHERE e_salary > 5000;]]></content>
<categories>
<category>面试</category>
<category>数据库</category>
</categories>
<tags>
<tag>面试</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[PostgreSQL数据库、数据表、数类型和运算符Demo案例]]></title>
<url>%2F2018%2F09%2F26%2FPostgreSQL%E6%95%B0%E6%8D%AE%E5%BA%93%E3%80%81%E6%95%B0%E6%8D%AE%E8%A1%A8%E3%80%81%E6%95%B0%E7%B1%BB%E5%9E%8B%E5%92%8C%E8%BF%90%E7%AE%97%E7%AC%A6Demo%E6%A1%88%E4%BE%8B%2F</url>
<content type="text"><![CDATA[数据库对象操作1234567891011121314--最简单创建数据库示例(使用默认参数)CREATE DATABASE db_jikexueyuan1;--带参数创建数据库示例(指定数据库OWNER和字符集)CREATE DATABASE db_jikexueyuan2 WITH OWNER = postgres ENCODING = 'UTF8';--修改数据库名称ALTER DATABASE db_jikexueyuan2 RENAME TO db_jikexueyuan3;--修改数据库连接数ALTER DATABASE db_jikexueyuan2 CONNECTION LIMIT = 20;--删除数据库DROP DATABASE db_jikexueyuan2; 数据表对象操作12345678910111213141516171819202122232425262728--创建student数据表CREATE TABLE studnet ( id INT, --学生编号 name VARCHAR(30), --学生姓名 birthday DATE, --出生日期 score NUMERIC(5, 2) --学分);--修改数据表名称ALTER TABLE studnet RENAME TO studnet1;--修改数据表字段长度ALTER TABLE studnet1 ALTER COLUMN name TYPE VARCHAR(40);--修改数据表字段名称ALTER TABLE studnet1 RENAME id TO bh;--在数据表中新增字段ALTER TABLE studnet1 ADD COLUMN address VARCHAR(100);--删除数据表中字段ALTER TABLE student DROP COLUMN name;--删除数据表对象DROP TABLE studnet1;--删除数据表之前先做判断DROP TABLE IF EXISTS studnet1; 数据类型12345678910111213141516171819202122--数值类型CREATE TABLE temp (x SMALLINT, y INT, z NUMERIC(5, 2));INSERT INTO temp VALUES (1, 100, 3.58);--INSERT INTO temp VALUES (1, 100, 1000.58);--(出现错误,超过存储范围)INSERT INTO temp VALUES (1, 100, 100.58);INSERT INTO temp VALUES (1, 100, 100.588);--(合法,会进行四舍五入)--日期/时间类型CREATE TABLE temp1 (t TIME, d DATE, ts TIMESTAMP);INSERT INTO temp1 VALUES ('02:02:02', '1996-01-01', '1996-01-01 02:02:02');INSERT INTO temp1 VALUES ('101112', '96-01-01', '1996-01-01 02:02:02');--字符串类型CREATE TABLE temp2 (ch CHAR(4), vch VARCHAR(30), t TEXT);INSERT INTO temp2 VALUES ('abcd', '极客学院', '极客学院极客学院极客学院');--比较char类型与varchar类型之间的区别SELECT CONCAT('(', ch, ')'), CONCAT('(', vch, ')'), CONCAT('(', t, ')')FROM temp2; 运算符介绍1234567891011121314151617181920212223242526272829303132--算术运算符SELECT 3+2, 3-2, 3*2, 3/2, 3%2;--比较运算符--比较运算符(一)SELECT 1 = 0, '2' = 2, '2' <> 2, 2 = 2, NULL = NULL, NULL != NULL;--比较运算符(二)SELECT 1 >= 2, 4 >= 4, 2 > 1, 'good' >= 'god', 'good' > 'god', NULL >= NULL;--比较运算符(三)SELECT 4 BETWEEN 2 AND 5, 12 BETWEEN 10 AND 12;--比较运算符(四)SELECT 2 IN (2,3,4), 3 NOT IN (1,2,5,9), 'a' IN ('a','b','c','d'), NULL IN (1,3,NULL);--比较运算符(五)-- % 表示任意0个或多个字符-- _ 表示任意单个字符-- [ ] 表示括号内所列字符中的一个(类似与正则表达式)-- [^ ] 表示不在括号所列之内的单个字符SELECT 'abc' LIKE 'abc', 'abc' LIKE 'ab_', 'abc' LIKE '%c', 'abc' LIKE 't___';--逻辑运算符--逻辑运算符(一)SELECT NOT '1', NOT 'y', NOT '0', NOT NULL, NOT 'n';--逻辑运算符(二)SELECT '1' AND 'y', '1' AND '0', '1' AND NULL, '0' AND NULL;--逻辑运算符(三)SELECT '1' OR 't', '1' OR 'y', '1' OR NULL;]]></content>
<categories>
<category>面试</category>
<category>数据库</category>
</categories>
<tags>
<tag>面试</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[面试总结之数据库篇(持续更新)]]></title>
<url>%2F2018%2F09%2F26%2F%E9%9D%A2%E8%AF%95%E6%80%BB%E7%BB%93%E4%B9%8B%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AF%87-%E6%8C%81%E7%BB%AD%E6%9B%B4%E6%96%B0%2F</url>
<content type="text"><![CDATA[1. SQL语言包含4个部分: DDL(Data Define Language):数据定义语言。其语句包括动词CREATE和DROP。在数据库中创建新表或删除表(CREAT TABLE 或 DROP TABLE);为表加入索引等。DDL包括许多与人数据库目录中获得数据有关的保留字。它也是动作查询的一部分。ALTER TABLE 语句用于在已有的表中添加、修改或删除列。 DML(Data Manipulation Language):数据操作语言。其语句包括动词INSERT,UPDATE和DELETE。它们分别用于添加,修改和删除表中的行。也称为动作查询语言。 DQL(Data Query Language):数据查询语言。也称为“数据检索语句”,用以从表中获得数据,确定数据怎样在应用程序给出。保留字SELECT是DQL(也是所有SQL)用得最多的动词,其他DQL常用的保留字有WHERE,ORDER BY,GROUP BY和HAVING。这些DQL保留字常与其他类型的SQL语句一起使用。 DCL(Data Control Language):数据控制语言。它的语句通过GRANT或REVOKE获得许可,确定单个用户和用户组对数据库对象的访问。某些RDBMS可用GRANT或REVOKE控制对表单个列的访问,COMMIT、ROLLBACK来提交和回滚。 2. DROP、DELETE和TRUNCATE区别 Diffs DELETE TRUNCATE Drop 执行速度 慢 较快 快 可执行条件 可以 不可以 不可以 语句分类 DML DDL DDL 可以回滚事务 可以 不可以 不可以 删除操作记录日志 记录 不记录 不记录 参考另外一篇日志《DELETE和TRUNCATE区别》 3. 主键和外键的作用 主键约束作用: 唯一标识一条记录 提高数据的检索效率 外键约束作用: 保证数据的完整性 提高数据的检索效率 4. UNIQUE和PRIMARY区别5. 索引分类以及使用索引的优缺点分类: 索引类型 使用场景 B-tree 适合处理那些能够按顺序存储数据 Hash 只能处理简单的比较 Gist 一种索引架构 GIN 翻转索引,处理包含多个值的键 优点: 提高数据的查询速度 加速表与表之间的连接 缺点: 创建和维护索引需要耗费时间 需要占用磁盘空间 6. 视图的作用 简单化 安全性 逻辑数据独立性 7. 事务四个特性及四个隔离级别8. 数据库范式 第一范式(1NF)所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分。简而言之,第一范式就是无重复的域。 第二范式(2NF)第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。 第三范式(3NF)在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。可以理解为消除冗余。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。]]></content>
<categories>
<category>面试</category>
<category>数据库</category>
</categories>
<tags>
<tag>面试</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[URL、URI和URN三者之间的区别]]></title>
<url>%2F2018%2F09%2F25%2FURL%E3%80%81URI%E5%92%8CURN%E4%B8%89%E8%80%85%E4%B9%8B%E9%97%B4%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[URL – Uniform Resource Locator,统一资源定位符。即URL可以用来标识一个资源,而且还指明了如何locate这个资源。 URN – Uniform Resource Name,统一资源命名。即通过名字来表示资源的。 URI – Uniform Resource Identifier,即统一资源标志符,用来唯一的标识一个资源。 URL和URN都是URI的子集。 一个用于理解这三者的例子。 关于URL: URL是URI的一种,不仅标识了Web 资源,还指定了操作或者获取方式,同时指出了主要访问机制和网络位置。 关于URN: URN是URI的一种,用特定命名空间的名字标识资源。使用URN可以在不知道其网络位置及访问方式的情况下讨论资源。 下面展示一个例子,非常简单清楚地告诉你在互联网中URI 、URL和URN之间的不同。 这是一个URI:1http://bitpoetry.io/posts/hello.html#intro 其中: http://:是定义如何访问资源的方式。 bitpoetry.io/posts/hello.html是资源存放的位置。 #intro是资源。 URL是URI的一个子集,告诉我们访问网络位置的方式。在我们的例子中,URL应该如下所示:1http://bitpoetry.io/posts/hello.html URN是URI的子集,包括名字(给定的命名空间内),但是不包括访问方式,如下所示: 1bitpoetry.io/posts/hello.html#intro 至少要记住一件事:URI可以被分为URL、URN或两者的组合。如果你一直使用URI这个术语,绝对不会出错。]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[计算机网络常用协议端口归纳]]></title>
<url>%2F2018%2F09%2F25%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E5%B8%B8%E7%94%A8%E5%8D%8F%E8%AE%AE%E7%AB%AF%E5%8F%A3%E5%BD%92%E7%BA%B3%2F</url>
<content type="text"><![CDATA[HTTP协议代理服务器常用端口号:80/8080/3128/8081/9098 SOCKS代理协议服务器常用端口号:1080 FTP(文件传输)协议代理服务器常用端口号:20(控制端口)/tcp、21(数据传输端口)/tcp Telnet(远程登录)协议代理服务器常用端口号:23/tcp DNS(域名解析服务)默认端口号:53/udp SSH(安全登录)、SCP(文件传输)、端口号重定向,默认的端口号:22/tcp HTTP服务器,默认端口号:80/tcp POP3 (邮局协议版本3)使用的端口号:110/tcp HTTPS(securely transferring web pages)服务器,默认端口号:443/tcp TFTP(Trivial File Transfer Protocol),默认端口号:69/udp SMTP (Simple Mail Transfer Protocol),默认端口号:25/tcp POP3 Post Office Protocol(E-mail),默认端口号:110/tcp Webshpere应用程序默认端口号:9080 webshpere管理工具,默认端口号:9090 JBOSS,默认端口号:8080 TOMCAT,默认端口号:8080 WIN2003远程登录,默认端口号:3389 Symantec AV/Filter for MSE,默认端口号:8081 Oracle 数据库默认的端口号:1521/tcp Oracle XDB(XML 数据库),默认的端口号为8080 Oracle XDB FTP服务,默认的端口号为2100 MS SQL*SERVER数据库server,默认的端口号为1433/tcp MS SQL*SERVER数据库monitor,默认的端口号为1434/tcp Microsoft RDP 微软远程桌面使用的端口号:1863/tcp Symantec pcAnywhere 远程控制数据传输时使用的端口号:5631/tcp Symantec pcAnywhere 主控端扫描被控端时使用的端口号:5632/udp MS SQL Server使用的端口号:5000/tcp 腾讯QQ端口号:8000/udp]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[DHCP续租过程]]></title>
<url>%2F2018%2F09%2F25%2FDHCP%E7%BB%AD%E7%A7%9F%E8%BF%87%E7%A8%8B%2F</url>
<content type="text"><![CDATA[计算机网络发展的早些时候,当一台设备想要在网络上通信的时候,它需要被手动分配一个地址。随着网络的发展,手动方式变得烦琐,为了解决这个问题,BOOTY协议(Bootstrap Protocol)被创建出来给链接到网络的设备自动分配地址。BOOTY协议后来被更加复杂的动态主机配置协议DHCP(Dynamic Host Configuration Protocol)取代。 DHCP用的端号是UDP67和UDP68,这两个端口是正常的DHCP服务端口,你可以理解为一个发送,一个接收。客户端向68端口(bootps)广播请求配置,服务器向67端口(bootpc)广播回应请求。 DHCP续租过程 DHCP客户端 DORA过程 DHCP服务器 → 发现Discover → → ← 提供Offer ← ← → 请求Request → → ← 确认Acknowledgement ← ← 1.发现数据包第一个数据包从0.0.0.0的68端口发往255.255.255.255的67端口。客户端使用0.0.0.0,是因为目前还没有IP地址。数据包发往255.255.255.255,是因为这是一个独立于网络的广播地址,从而能够确保这个数据包会被发往网络上的每一台设备。因为这台设备并不知道DHCP服务器的地址,所有它的第一个数据包是为了寻找正在监听的DHCP服务器。 DHCP客户端对请求响应速度要求很高。由于DHCP有其内置的保证可靠性的方法,也就意味着UDP是最合适的协议。 数据包中包括了所请求的IP地址,表示客户端希望得到的IP的地址,通常是之前用过的IP地址。 2.提供数据包这个数据包包含了和前一个数据包相同的事务ID,该ID告诉我们这个响应与原先的请求相对应。该数据包由DHCP服务器发出,用于向客户端提供服务。它提供的信息,包括自己的IP,以及给客户端提供的地址等。 3.请求数据包当客户端收到DHCP服务器提供的数据包之后,它将以一个DHCP请求数据包作为接收确认。该数据包仍然从IP为0.0.0.0的地址发出,因为我们还没有完成获取IP地址的过程。目的地址为255.255.255.255. 值得注意的是在选项域(Option)中所请求的IP地址不在为空,而是上一个提供数据包中提供的客户端IP。DHCP服务器标识域野包含了IP地址。 4.确认数据包该过程是DHCP在确认数据包中给客户端发送其所请求的IP地址,并在数据库中记录相关信息。此时客户端就有了一个IP地址,并且可以用它在网络上通信。 DHCP租约内续租当DHCP给一个客户端分配了一个IP时,它同时给客户端定下了一个租约。也就是说客户端只能在有限的时间内使用该IP地址,否则必须续租。 前面介绍的DORA过程出现在客户端第一次获取IP地址或者其租约时间已经过期的情况下。在这两种情况下们该设备都被视为违约过期。 当一个拥有IP地址的客户端在租约期内重新启动,他必须进行一次精简版的DORA过程来重新认领它的IP地址,该过程被称为租约内续租。 在租约内续租时,发现和提供数据包就变得没有必要了。考虑到其与租约过期时的DORA过程类似,可以发现在租约期内续租并不需要那么做,而只是完成请求和确认两个步骤就好了。]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[计算机网络各层协议]]></title>
<url>%2F2018%2F09%2F19%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E5%90%84%E5%B1%82%E5%8D%8F%E8%AE%AE%2F</url>
<content type="text"><![CDATA[应用层:典型设备:应用程序,如FTP,SMTP ,HTTP DHCP (Dynamic Host Configuration Protocol)动态主机分配协议,使用 UDP 协议工作,主要有两个用途:给内部网络或网络服务供应商自动分配 IP 地址,给用户或者内部网络管理员作为对所有计算机作中央管理的手段。实 现即插即用连网。 BOOTP (BOOTstrapProtocol) 引导程序协议/ 自举协议,使用UDP 来使 一个无盘工作站自动获取配置信息。静态的配置协议 DNS (Domain Name System )域名解析<端口号53> FTP (File Transfer Protocol )文件传输协议<端口号21>减少或消除不同操作系统下处理文件的不兼容性。 Gopher (The Internet Gopher Protocol )网际Gopher 协议 HTTP (Hypertext Transfer Protocol )超文本传输协议 <端口号 80>, 面向事务的应用层协议。 IMAP4 (Internet Message Access Protocol 4) Internet 信息访问协议的第 4 版本 IRC (Internet Relay Chat )网络聊天协议 NNTP (Network News Transport Protocol )网络新闻传输协议 XMPP 可扩展消息处理现场协议 POP3 (Post Office Protocol 3) 即邮局协议的第3 个版本,用于接受邮件。 SIP()信令控制协议 SMTP (Simple Mail Transfer Protocol )简单邮件传输协议 <端口号25> 用于发送邮件。 SNMP (Simple Network Management Protocol),简单网络管理协议 SSH (Secure Shell )安全外壳协议 TELNET 远程登录协议 <端口号23> RPC (Remote Procedure Call Protocol )(RFC- 1831)远程过程调用协 议 RTCP (RTP Control Protocol )RTP 控制协议 RTSP (Real Time Streaming Protocol )实时流传输协议 TLS (Transport Layer Security Protocol )安全传输层协议 SDP( Session Description Protocol )会话描述协议 SOAP (Simple Object Access Protocol )简单对象访问协议 GTP 通用数据传输平台 STUN (Simple Traversal of UDP over NATs ,NAT的UDP 简单穿越) 是一种网络协议 NTP (Network Time Protocol )网络校时协议。 传输层:典型设备: 进程和端口数据单元:数据段 (Segment) TCP (Transmission Control Protocol )传输控制协议提供可靠的面向连接的服务,传输数据前须先建立连接,结束后释放。可靠的全双工信道。可靠、有序、无丢失、不重复。 UDP (User Datagram Protocol )用户数据报协议发送数据前无需建立连接,不使用拥塞控制,不保证可靠交付,最大努力交付。 DCCP (Datagram Congestion Control Protocol )数据报拥塞控制协议 SCTP (STREAM CONTROL TRANSMISSION PROTOCOL )流控制传 输协议 RTP(Real-time Transport Protocol )实时传送协议 RSVP (Resource ReSer Vation Protocol )资源预留协议 PPTP ( Point to Point Tunneling Protocol )点对点隧道协议 网络层典型设备:路由器,防火墙、多层交换机数据单元:数据包(Packet) IP (IPv4 · IPv6) (Internet Protocol) 网络之间互连的协议 ARP (Address Resolution Protocol) 即地址解析协议,实现通过IP 地址得 知其物理地址。 RARP (Reverse Address Resolution Protocol)反向地址转换协议允许局域 网的物理机器从网关服务器的 ARP 表或者缓存上请求其 IP地址。 ICMP (Internet Control Message Protocol )Internet 控制报文协议。它是TCP/IP 协议族的一个子协议,用于在IP 主机、路由器之间传递控制消息。ping、trace、traceroute使用的就是ICMP. ICMPv6 : IGMP (Internet Group Management Protocol) Internet 组管理协议,是因特 网协议家族中的一个组播协议,用于 IP 主机向任一个直接相邻的路由器报 告他们的组成员情况。 RIP (Router information protocol) 路由信息协议是一种在网关与主机之间交换路由选择信息的标准。 OSPF (Open Shortest Path Firs)开放式最短路径优先,分布式链路状态协议。 BGP(Border Gateway Protocol )边界网关协议,用来连接Internet 上独立系统的路由选择协议.采用路径向量路由选择协议。 IS-IS (Intermediate System to Intermediate System Routing Protocol )中间系统到中间系统的路由选择协议. IPsec (IP Secure) “Internet 协议安全性”是一种开放标准的框架结构,通过使用加密的安全服务以确保在 Internet 协议 (IP) 网络上进行保密而安全的通讯。 数据链路层典型设备: 网卡,网桥,交换机数据单元:帧 (Frame) ARQ(Automatic Repeat-reQuest )自动重传请求协议,错误纠正协议之一,包括停止等待ARQ 协议和连续ARQ 协议,错误侦测、正面确认、逾时重传与负面确认继以重传等机制。 停止等待协议:CSMA/CD(Carrrier Sense Multiple Access with Collision Detection)载波监听多点接入/碰撞检测协议。总线型网络,协议的实质是载波监听和碰撞检测。载波监听即发数据前先检测总线上是否有其他计算机在发送数据,如暂时不发数据,避免碰撞。碰撞检测为计算机边发送数据边检测信道上的信号电压大小。 PPP(Point-to-Ponit Protocol)点对点协议面向字节,由三部分组成:一个将IP 数据报封装到串行链路的方法;一个用于建立、配置和测试数据链路连接的链路控制协议 LCP(Link Control Protocol) :一套网络控制协议NCP 。 HDLC (High-Level Data Link Control )高级数据链路控制同步网上传输数据、面向比特的数据链路层协议。 ATM (Asynchronous Transfer Mode )异步传递方式,建立在电路交换和分组交换的基础上的一种面向连接的快速分组交换技术。 “异步”是指将ATM 信元“异步插入”到同步的 SDH 比特流中。如同步插入则用户在每帧中所占的时隙相对位置固定不变。“同步”是指网络中各链路上的比特流都是受同一非常精确的主时钟的控制。Wi-Fi 、WiMAX 、DTM 、令牌环、以太网、FDDI 、帧中继、 GPRS 、 EVDO 、HSPA 、L2TP 、ISDN 物理层典型设备:中继器,集线器、网线、HUB数据单元:比特 (Bit) 以太网物理层、调制解调器、PLC 、SONET/SDH 、G.709 、光导纤维、 同轴电缆、双绞线 最后附上一张各层协议图: 参考:https://www.cnblogs.com/maowang1991/archive/2013/04/16/3024393.html]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[maven约定目录配置]]></title>
<url>%2F2018%2F09%2F19%2Fmaven%E7%BA%A6%E5%AE%9A%E7%9B%AE%E5%BD%95%E9%85%8D%E7%BD%AE%2F</url>
<content type="text"><![CDATA[Maven 提倡使用一个共同的标准目录结构,Maven 使用约定优于配置的原则,大家尽可能的遵守这样的目录结构。 目录 目的 ${basedir} 存放pom.xml和所有的子目录 ${basedir}/src/main/java 项目的java源代码 ${basedir}/src/main/resources 项目的资源,比如说property文件,springmvc.xml ${basedir}/src/test/java 项目的测试类,比如说Junit代码 ${basedir}/src/test/resources 测试用用的资源 ${basedir}/src/main/webapp/WEB-INF web应用文件目录,web项目的信息,比如存放web.xml、本地图片、jsp视图页面 ${basedir}/target 打包输出目录 ${basedir}/target/classes 编译输出目录 ${basedir}/target/test-classes 测试编译输出目录 Test.java Maven只会自动运行符合该命名规则的测试类 ~/.m2/repository Maven默认的本地仓库目录位置]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Mac Dock栏分组]]></title>
<url>%2F2018%2F09%2F18%2FMac-Dock%E6%A0%8F%E5%88%86%E7%BB%84%2F</url>
<content type="text"><![CDATA[方法1终端下输入如下命令123~ defaults write com.apple.dock persistent-apps -array-add '{ "tile-type" = "spacer-tile"; }'~ killall Dock 实际上在docker中添加了空白的icon,如需删除只需右键移除即可。 方法2https://github.com/DeromirNeves/VerticalBar https://github.com/DeromirNeves/VerticalBar.git]]></content>
<categories>
<category>玩转苹果</category>
</categories>
<tags>
<tag>玩转苹果</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java 方法设置默认参数]]></title>
<url>%2F2018%2F09%2F09%2FJava-%E6%96%B9%E6%B3%95%E8%AE%BE%E7%BD%AE%E9%BB%98%E8%AE%A4%E5%8F%82%E6%95%B0%2F</url>
<content type="text"><![CDATA[Java本身不支持设置默认值,需要用重载间接实现。 因为“默认参数”和“方法重载”同时支持的话有二义性的问题,Java可能为了简单就不要“默认参数”了。使用“方法重载”可以间接实现”默认参数“的效果,而且避免了代码过于hack(乱)。 1234567public class A{ public void doA(int a){ } public void doA(){ this.doA(0);//这里默认传入0,可以近似与通过这个方法重载,实现了默认值 }}]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java 关键字this和super]]></title>
<url>%2F2018%2F09%2F07%2FJava-%E5%85%B3%E9%94%AE%E5%AD%97this%E5%92%8Csuper%2F</url>
<content type="text"><![CDATA[关键字this有两个用途: 引用隐式参数 调用该类其他的构造器 关键字super也有两个用途: 调用超类的方法 调用超类的构造器]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux 使用su和su -切换用户的区别]]></title>
<url>%2F2018%2F09%2F06%2FLinux-%E4%BD%BF%E7%94%A8su%E5%92%8Csu-%E5%88%87%E6%8D%A2%E7%94%A8%E6%88%B7%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[假设原来我们以root身份登录在终端,那么环境变量包含/usr/sbin。 若使用su -切换到普通用户后,环境变量不包含/usr/sbin。 若使用su切换到普通用户,环境变量包含/usr/sbin。 简单总结为, su -相当于重新登录,环境变量随用户的身份变为登录后用户的环境变量。登陆后的位置为用户home目录下。 su环境变量不随切换后的用户的环境变量而变化。而且登陆后所在位置与登录前一致,对于从root切换而言,当前目录为/root。]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java类属性和方法的可见性]]></title>
<url>%2F2018%2F09%2F06%2FJava%E7%B1%BB%E5%B1%9E%E6%80%A7%E5%92%8C%E6%96%B9%E6%B3%95%E7%9A%84%E5%8F%AF%E8%A7%81%E6%80%A7%2F</url>
<content type="text"><![CDATA[作用域 当前类 同一package 子孙类 其他package public Y Y Y Y protected Y Y Y N friendly(default) Y Y N N private Y N N N 不写时默认为friendly]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux文件名查找]]></title>
<url>%2F2018%2F09%2F05%2FLinux%E6%96%87%E4%BB%B6%E5%90%8D%E6%9F%A5%E6%89%BE%2F</url>
<content type="text"><![CDATA[通常情况下find不是很常用,因为速度较慢。一般都是先使用whereis或者是locate来检查,如果真找不到,才使用find来查找。因为whereis与locate是利用数据库来查找数据,所以速度相当快,而且并没有实际去查询硬盘,比较节省时间;find直接打查找硬盘,所以查询速度要慢。 whereis whereis [-bmsu] 文件或者目录名 Linux系统会将系统内的所有文件都记录在一个数据库文件里面,而当使用whereis或者下面的locate命令时,都会以此数据库文件内容为准。因此有时候还会发现使用这两个命令查找文件时,会找到已经删掉的文件。而且有可能找不到最新刚刚创建的文件。 locate locate [-ir] keyword locate使用更简单,直接在后面输入文件的部分名称后就能得到结果。被搜索的数据库默认是每天执行一次,该数据库的位置为/var/lib/mlocate/。我们可以选择手动更新数据库,直接命令行输入updatedb 即可。 find find [path] [option] [option] 示例1:将过去24小时内有改动的文件列出123find / -mtime 0find / -mtime 3 <==表示三天前的24小时 示例2:寻找/etc下面的文件,如果文件日期比/etc/passwd新就列出1find /etc -newer /etc/passwd 示例3:查找/home下属于wkx的文件1find /home -user wkx 示例4:查找系统中不属于任何人的文件1find / -nouser 示例5:找出文件名为passwd的文件1find / -name passwd 示例6:找出/var目录下文件类型为socket的文件名有哪些1find /var -type s 示例7:找出含有SGID或者SUID或者SBIT的属性文件12find / -perm +7000# 文件权限只要含有s或者t就列出 -perm mode: 文件权限刚好等于mode的文件 -perm -mode: 文件权限全部包括mode的文件 -perm +mode: 文件权限包含任意mode的文件]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux切割命令split]]></title>
<url>%2F2018%2F09%2F05%2FLinux%E5%88%87%E5%89%B2%E5%91%BD%E4%BB%A4split%2F</url>
<content type="text"><![CDATA[如果文件太大,导致一些携带设备无法复制问题。利用split就可以了。它可以帮助我们将一个大文件依据文件大小或者行数来切割成小文件,快速而有效。 具体使用方法可以通过man split查看。 范例1:一部复仇者联盟的电影,大小为1.7G,现将他们分割为每一份512M。1234567split -b 512m Avengers.mp4 AvengerSplitll-rw-r--r-- 1 Dhyana staff 512M Sep 5 18:19 AvengerSplitaa-rw-r--r-- 1 Dhyana staff 512M Sep 5 18:19 AvengerSplitab-rw-r--r-- 1 Dhyana staff 512M Sep 5 18:19 AvengerSplitac-rw-r--r-- 1 Dhyana staff 171M Sep 5 18:19 AvengerSplitad 如果需要将文件合并,使用命令1cat AvengerSplit* >> AvengerBack.mp4 即可。 范例2:使用ls -al /输出的信息中,每10行记录成一个文件1234567ls -al / | split -l 10 - lsrootwc -l lsroot*10 lsrootaa10 lsrootab 7 lsrootac27 total 重点在那个-。一般来讲,如果需要stdin/stdout,但是没有相应的文件,此时-就代表了stdin/stdout。]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux 命令回传码与 && 或 ||]]></title>
<url>%2F2018%2F09%2F05%2FLinux-%E5%91%BD%E4%BB%A4%E5%9B%9E%E4%BC%A0%E7%A0%81%E4%B8%8E-%E6%88%96%2F</url>
<content type="text"><![CDATA[Linux中,若前一个命令执行的结果是正确的,那么在Linux中会回传一个$?=0,即 echo $? 显示为0。 命令执行情况 说明 cmd1 && cmd2 若cmd1执行完毕且正确执行($?=0),则执行cmd2; 若cmd1执行完毕且为错误($?≠0),则cmd2不被执行 cmd1 11 cmd2 若cmd1执行完毕且正确执行($?=0),则cmd2不被执行; 若cmd1执行完毕且为错误($?≠0),则执行cmd2 示例:以ls测试/tmp/wkx执行后,若存在则显示”exits”,若不存在,则显示”not exits”。 错误答案1ls /tmp/vvv || echo "not exits" && echo "exits" 以上答案执行后的结果同时出现not exits和exits。具体问题可以尝试分析。 正确答案1ls /tmp/vvv && echo "exits" || echo "not exits" 通过该练习,了解到由于命令时一个接着一个去执行的,因此,如果真要使用判断,那么这个&&和||的顺序就不能搞错。一般来说,假设判断公式有三个,也就是:1cmd1 && cmd2 || cmd3 而且顺序通常不会变。因为一般来讲,cmd2和cmd3会放置肯定可以执行成功的命令,因此依据上面的逻辑分析,我们就可以知道为什么要如此放置,这是很有用的,而且考试也很常考。]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux Bash 默认的组合键汇总]]></title>
<url>%2F2018%2F09%2F05%2FLinux-Bash-%E9%BB%98%E8%AE%A4%E7%9A%84%E7%BB%84%E5%90%88%E9%94%AE%E6%B1%87%E6%80%BB%2F</url>
<content type="text"><![CDATA[案件组合 执行结果 Ctrl + C 终止目前命令 Ctrl + D 输入结束(EOF),例如邮件结束的时候 Ctrl + M 就是Enter Ctrl + S 暂停屏幕的输出 Ctrl + Q 恢复屏幕输出 Ctrl + U 在提示符下,将整行命令删除 Ctrl + Z 暂停目前的命令]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux数据流重定向]]></title>
<url>%2F2018%2F09%2F05%2FLinux%E6%95%B0%E6%8D%AE%E6%B5%81%E9%87%8D%E5%AE%9A%E5%90%91%2F</url>
<content type="text"><![CDATA[标准输出(standard output)指的是命令执行所回传的正确的信息,而标准错误输出(standard error output)可以理解为命令执行失败后,所回传的错误信息。一般情况下,不管正确还是错误信息数据默认的是输出到屏幕上,所以屏幕上是很混乱的。可以通过数据流重定向将stdout和stderr分别传送到其他的文件或者设备去,而分别传送所用的特殊字符如下所示: 标准输入(stdin):代码为0,使用<或者<< 标准输出(stdout):代码为1,使用>或者>> 标准错误输出(stderr):代码为2,使用>或者>> 标准输出和标准错误输出如果仅存在>,则代表默认代码为1。 1>:以覆盖的方法将正确的数据输出到指定的文件或者设备上 1>>:以累加的方法将正确的数据输出到指定的文件或者设备上 2>:以覆盖的方法将错误的数据输出到指定的文件或者设备上 2>>:以累加的方法将错误的数据输出到指定的文件或者设备上 示例1:将stdout和stderr分别存到不同的文件中去。1find /home -name .bashrc > list_right 2> list_error 示例2:将错误的数据丢弃,屏幕上显示正确的数据1find /home -name .bashrc 2> /dev/null /dev/null垃圾黑洞设备 示例3:将正确和错误的数据全部写入一个文件12345find /home -name .bashrc >list 2>&1或者find /home -name .bashrc &> list 标准输入<意味着将原本需要由键盘输入的数据改由文件内容来替代。 示例1:利用cat命令来创建一个文件的简单流程,了解一下什么是键盘输入12345cat > catfile123<==此处按下ctrl+c或者ctrl+d来离开 示例2:利用stdin替代键盘的输入来创建新文件的流程123456cat > catfile < ~/.bashrc--------------------------ll catfile ~/.bashrc-rw-r--r-- 1 root root 176 Sep 5 14:10 catfile-rw-r--r--. 1 root root 176 Dec 29 2013 /root/.bashrc# 注意到两个文件大小几一模一样,几乎是使用cp命令复制一份 理解了<后,再来看<<。<<:代表的是结束输入的意思。举例来讲,我要用cat直接将输入的信息输出到catfile中,且当由键盘输入eof时,本次输入就结束,可以按照如下方式进行: 12345cat > catfile << "eof">1>2>3>eof <==输入该关键字后,立刻就结束输入而不需要输入ctrl + d/c 双向重定向teetee命令实现了手动跟踪命令的输出内容,同时又将输出的内容写入文件,确保之后可以用来参考。例如:123456last | tee last.list | cut -d ' ' -f1# last: View the last logged in users.# 该范例可以让我们将last的输出存一份到last.list文件中。ls -l / | tee -a ~/homefile | more# tee 后直接接文件会被覆盖,如果加以 -a 这个参数则能将信息累加 总结,tee可以让standard output转存一份到文件内并将同样的数据继续送到屏幕上去处理显示。这样,除了可以让我们同时分析一份数据并记录下来之外,还可以作为处理一份数据的中间暂存盘记录只用。 另:tee在很多认证考试中有考察。]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[java平台结构]]></title>
<url>%2F2018%2F09%2F04%2Fjava%E5%B9%B3%E5%8F%B0%E7%BB%93%E6%9E%84%2F</url>
<content type="text"><![CDATA[]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[java入门小知识]]></title>
<url>%2F2018%2F09%2F02%2Fjava%E5%85%A5%E9%97%A8%E5%B0%8F%E7%9F%A5%E8%AF%86%2F</url>
<content type="text"><![CDATA[关键字public称为访问修饰符(access modifier),这些修饰符用于控制程序的其他部分对这段代码的访问级别。 Java的命名规范为大驼峰命名。 源代码的文件名必须与公共类的名字相同,并用.java作为扩展名。 根据Java语言规范,main方法必须声明为public,java的main方法必须有一个外壳类。 在一个源文件中,只能有一个公有类,但可以有任意数目的非公有类。 构造器与类同名,在构造类对象时,构造器会运行,以便将实例域初始化为所需要的状态。构造器与一般的对象方法不同,构造器总是伴随着new操作符的执行被调用,而不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的。 构造器与类同名 每个类可以有一个以上的构造器 构造器可以有0个或多个参数 构造器没有返回值 构造器总是伴随着new使用 main方法不对任何对象进行操作。事实上,在启动程序时还没有任何一个对象。静态的main方法将执行并创建程序所需的对象。 如果在构造器中没有显式的给域赋初始值,那么就会被自动地赋为默认值:数值为0,布尔值为false,对象引用为null。然而,只有缺少程序设计经验的人才会这样做。 对于访问修饰符public和private,标记为public的部分可以被任意的类使用,标记为private的部分只能被定义它们的类使用。如果没有指定public或者private,那么这个部分(类、方法、变量)可以被同一个包中的所有方法访问。 final修饰引用类型变量,表示该变量的引用的地址不能变,引用地址里的内容可以修改。 单例设计模式:保证在整个应用中某一个类有且仅有一个实例(一个类在堆内存只存在一个对象),即所以指向该类型实例的引用都指向同一个内存空间。]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Jet Brains(IDE)常用快捷键]]></title>
<url>%2F2018%2F09%2F02%2FJet-Brains-IDE-%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE%2F</url>
<content type="text"><![CDATA[Mac键盘符号和修饰键说明 ⌘ Command ⇧ Shift ⌥ Option ⌃ Control ↩︎ Return/Enter ⌫ Delete ⌦ 向前删除键(Fn+Delete) ↑ 上箭头 ↓ 下箭头 ← 左箭头 → 右箭头 ⇞ Page Up(Fn+↑) ⇟ Page Down(Fn+↓) Home Fn + ← End Fn + → ⇥ 右制表符(Tab键) ⇤ 左制表符(Shift+Tab) ⎋ Escape (Esc) iTerm中的快捷键小知识点,补充到这里 ⌘ + T : 新Tab窗口 ⌘ + D : 新建窗口,横向切屏 Editing(编辑) ⌘⌥ + ← 或 → 光标跳到上一个文件编辑处,很重要!!!!!!!!!!! ⌃Space 基本的代码补全(补全任何类、方法、变量) ⌃⇧Space 智能代码补全(过滤器方法列表和变量的预期类型) ⌘⇧↩ 自动结束代码,行末自动添加分号 ⌘P 显示方法的参数信息 ⌃J, Mid. button click 快速查看文档 ⇧F1 查看外部文档(在某些代码上会触发打开浏览器显示相关文档) ⌘+鼠标放在代码上 显示代码简要信息 ⌘F1 在错误或警告处显示具体描述信息 ⌘N, ⌃↩, ⌃N 生成代码(getter、setter、构造函数、hashCode/equals,toString) ⌃O 覆盖方法(重写父类方法) ⌃I 实现方法(实现接口中的方法) ⌘⌥T 包围代码(使用if..else, try..catch, for, synchronized等包围选中的代码) ⌘/ 注释/取消注释与行注释 ⌘⌥/ 注释/取消注释与块注释 ⌥↑ 连续选中代码块 ⌥↓ 减少当前选中的代码块 ⌃⇧Q 显示上下文信息 ⌥↩ 显示意向动作和快速修复代码 ⌘⌥L 格式化代码 ⌃⌥O 优化import ⌃⌥I 自动缩进线 ⇥ / ⇧⇥ 缩进代码 / 反缩进代码 ⌘X 剪切当前行或选定的块到剪贴板 ⌘C 复制当前行或选定的块到剪贴板 ⌘V 从剪贴板粘贴 ⌘⇧V 从最近的缓冲区粘贴 ⌘D 复制当前行或选定的块 ⌘⌫ 删除当前行或选定的块的行 ⌃⇧J 智能的将代码拼接成一行 ⌘↩ 智能的拆分拼接的行 ⇧↩ 开始新的一行 ⌘⇧U 大小写切换 ⌘⇧] / ⌘⇧[ 选择直到代码块结束/开始 ⌥⌦ 删除到单词的末尾(⌦键为Fn+Delete) ⌥⌫ 删除到单词的开头 ⌘+ / ⌘- 展开 / 折叠代码块 ⌘⇧+ 展开所以代码块 ⌘⇧- 折叠所有代码块 ⌘W 关闭活动的编辑器选项卡 Search/Replace(查询/替换) Double ⇧ 查询任何东西 ⌘F 文件内查找 ⌘G 查找模式下,向下查找 ⌘⇧G 查找模式下,向上查找 ⌘R 文件内替换 ⌘⇧F 全局查找(根据路径) ⌘⇧R 全局替换(根据路径) ⌘⇧S 查询结构(Ultimate Edition 版专用,需要在Keymap中设置) ⌘⇧M 替换结构(Ultimate Edition 版专用,需要在Keymap中设置) Usage Search(使用查询) ⌥F7 / ⌘F7 在文件中查找用法 / 在类中查找用法 ⌘⇧F7 在文件中突出显示的用法 ⌘⌥F7 显示用法 Compile and Run(编译和运行) ⌘F9 编译Project ⌘⇧F9 编译选择的文件、包或模块 ⌃⌥R 弹出 Run 的可选择菜单 ⌃⌥D 弹出 Debug 的可选择菜单 ⌃R 运行 ⌃D 调试 ⌃⇧R, ⌃⇧D 从编辑器运行上下文环境配置 Debugging(调试) F8 进入下一步,如果当前行断点是一个方法,则不进入当前方法体内 F7 进入下一步,如果当前行断点是一个方法,则进入当前方法体内,如果该方法体还有方法,则不会进入该内嵌的方法中 ⇧F7 智能步入,断点所在行上有多个方法调用,会弹出进入哪个方法 ⇧F8 跳出 ⌥F9 运行到光标处,如果光标前有其他断点会进入到该断点 ⌥F8 计算表达式(可以更改变量值使其生效) ⌘⌥R 恢复程序运行,如果该断点下面代码还有断点则停在下一个断点上 ⌘F8 切换断点(若光标当前行有断点则取消断点,没有则加上断点) ⌘⇧F8 查看断点信息 Navigation(导航) ⌘O 查找类文件 ⌘⇧O 查找所有类型文件、打开文件、打开目录,打开目录需要在输入的内容前面或后面加一个反斜杠/ ⌘⌥O 前往指定的变量 / 方法 ⌃← / ⌃→ 左右切换打开的编辑tab页 F12 返回到前一个工具窗口 ⎋ 从工具窗口进入代码文件窗口 ⇧⎋ 隐藏当前或最后一个活动的窗口,且光标进入代码文件窗口 ⌘⇧F4 关闭活动run/messages/find/… tab ⌘L 在当前文件跳转到某一行的指定处 ⌘E 显示最近打开的文件记录列表 ⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方 ⌘⇧⌫ 跳转到最后一个编辑的地方 ⌥F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择(如在代码编辑窗口可以选择显示该文件的Finder) ⌘B / ⌘ 鼠标点击 进入光标所在的方法/变量的接口或是定义处 ⌘⌥B 跳转到实现处,在某个调用的方法名上使用会跳到具体的实现处,可以跳过接口 ⌥ Space, ⌘Y 快速打开光标所在方法、类的定义 ⌃⇧B 跳转到类型声明处 ⌘U 前往当前光标所在方法的父类的方法 / 接口定义 ⌃↓ / ⌃↑ 当前光标跳转到当前文件的前一个/后一个方法名位置 ⌘] / ⌘[ 移动光标到当前所在代码的花括号开始/结束位置 ⌘F12 弹出当前文件结构层,可以在弹出的层上直接输入进行筛选(可用于搜索类中的方法) ⌃H 显示当前类的层次结构 ⌘⇧H 显示方法层次结构 ⌃⌥H 显示调用层次结构 F2 / ⇧F2 跳转到下一个/上一个突出错误或警告的位置 F4 / ⌘↓ 编辑/查看代码源 ⌥ Home 显示到当前文件的导航条 F3选中文件/文件夹/代码行,添加/取消书签 ⌥F3 选中文件/文件夹/代码行,使用助记符添加/取消书签 ⌃0…⌃9 定位到对应数值的书签位置 ⌘F3 显示所有书签 Refactoring(重构) F5 复制文件到指定目录 F6 移动文件到指定目录 ⌘⌫ 在文件上为安全删除文件,弹出确认框 ⇧F6 重命名文件 ⌘F6 更改签名 ⌘⌥N 一致性 ⌘⌥M 将选中的代码提取为方法 ⌘⌥V 提取变量 ⌘⌥F 提取字段 ⌘⌥C 提取常量 ⌘⌥P 提取参数 VCS/Local History(版本控制/本地历史记录) ⌘K 提交代码到版本控制器 ⌘T 从版本控制器更新代码 ⌥⇧C 查看最近的变更记录 ⌃C 快速弹出版本控制器操作面板 Live Templates(动态代码模板) ⌘⌥J 弹出模板选择窗口,将选定的代码使用动态模板包住 ⌘J 插入自定义动态代码模板 General(通用) ⌘1…⌘9 打开相应编号的工具窗口 ⌘S 保存所有 ⌘⌥Y 同步、刷新 ⌃⌘F 切换全屏模式 ⌘⇧F12 切换最大化编辑器 ⌥⇧F 添加到收藏夹 ⌥⇧I 检查当前文件与当前的配置文件 ⌘, 打开IDEA系统设置 ⌘; 打开项目结构对话框 ⇧⌘A 查找动作(可设置相关选项) ⌃⇥ 编辑窗口标签和工具窗口之间切换(如果在切换的过程加按上delete,则是关闭对应选中的窗口) Other(一些官方文档上没有体现的快捷键) ⌘⇧8 竖编辑模式 导航 ⌘O 查找类文件 Ctrl + N ⌘⌥O 前往指定的变量 / 方法 Ctrl + Shift + Alt + N ⌃← / ⌃→ 左右切换打开的编辑tab页Alt←/Alt→ ⎋ 从工具窗口进入代码文件窗口 ESC ⌘L 在当前文件跳转到某一行的指定处 Ctrl + G ⌘E 显示最近打开的文件记录列表 Ctrl + E ⌘⌥←/ ⌘⌥→退回 / 前进到上一个操作的地方 Ctrl + Alt + ←/Ctrl + Alt + → ⌘⇧⌫ 跳转到最后一个编辑的地方 ⌃H 显示当前类的层次结构Ctrl + H ⌘⇧H 显示方法层次结构 ⌃⌥H 显示调用层次结构 F4 /⌘↓ 编辑/查看代码源 ⌘⌥U 显示类UML图 ⌃J 查看注释 编辑 ⌥⌦ 删除到单词的末尾(⌦键为Fn+Delete) ⌥⌫ 删除到单词的开头 ⌘+ /⌘- 展开 / 折叠代码块 ⌘F1 在错误或警告处显示具体描述信息 ⌘⌥L 格式化代码 ⌃⌥O 优化import ⇧↩ 开始新的一行 ⌘⇧↩ 自动结束代码,行末自动添加分号 ⌃I 实现方法(实现接口中的方法) ⇧F6 重命名文件或者变量 ⌘N,⌃↩,⌃N 生成代码(getter、setter、构造函数、hashCode/equals,toString) ⌘P 显示方法的参数信息 查找 Double⇧ 查找任何东西 ⌘⇧F 全局查找(根据路径) ⌘F 文件内查找 ⌘G 查找模式下,向下查找 ⌘⇧G 查找模式下,向上查找 导航 ⌘⌥B 跳转到接口的实现 ⌘U 查看接口定义 ⌘⌥← /⌘⌥→ 退回 / 前进到上一个操作的地方 ⌘B /⌘ 鼠标点击 进入光标所在的方法/变量的接口或是定义处 ⌃⇧B 跳转到类型声明处 ⌥ Space,⌘Y 快速打开光标所在方法、类的定义 ⌘O 查找类文件 ⌘⇧O 查找所有类型文件、打开文件、打开目录,打开目录需要在输入的内容前面或后面加一个反斜杠/ F12 返回到前一个工具窗口 ⎋ 从工具窗口进入代码文件窗口 ⇧⎋ 隐藏当前或最后一个活动的窗口,且光标进入代码文件窗口 F3选中文件/文件夹/代码行,添加/取消书签 ⌥F3 选中文件/文件夹/代码行,使用助记符添加/取消书签 ⌃0…⌃9 定位到对应数值的书签位置 ⌘F3 显示所有书签 ⌥F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择(如在代码编辑窗口可以选择显示该文件的Finder) ⌘F12 弹出当前文件结构层,可以在弹出的层上直接输入进行筛选(可用于搜索类中的方法) 通用 ⌃⌘F 切换全屏模式 自动代码 ⚠注:⌘+J可以调出所有提供的代码补全↩即可,下面仅列举常用的几个 原文地址:https://linmi.cc/836]]></content>
<categories>
<category>IDE</category>
</categories>
<tags>
<tag>IDE</tag>
</tags>
</entry>
<entry>
<title><![CDATA[子网掩码的作用(详细介绍)]]></title>
<url>%2F2018%2F09%2F01%2F%E5%AD%90%E7%BD%91%E6%8E%A9%E7%A0%81%E7%9A%84%E4%BD%9C%E7%94%A8-%E8%AF%A6%E7%BB%86%E4%BB%8B%E7%BB%8D%2F</url>
<content type="text"><![CDATA[子网掩码的作用是什么?掩码用于说明子网域在一个IP地址中的位置。子网掩码主要用于说明如何进行子网的划分。掩码是由32位组成的,很像IP地址。对于三类IP地址来说,有一些自然的或缺省的固定掩码。下面详细来看看吧。 IP地址和网络掩码为了简便起见,通常会被写成无类型域间选路(Classless-Inter-Domain-Routing, CIDR)。在这种形势下,一个完整的IP地址后面会有一个左斜杠/,以及一个用来表示IP地址中网路部分位数的数字。举例说明,IP地址10.10.1.22和网络掩码255.255.0.0,在CIDR表示法下就会被写成10.10.1.22/16的形式。 子网掩码的作用一: 分割网络但由于计算机数量不断增加,人们发展IP地址数量很快要不够用了,而有类IP的IP分类方法会造成许多IP地址的浪费,如某公司申请到一个A类IP地址范围,其有效IP数量可达2^24-2,即1600万之多,而公司中不可能有什么多电脑。于是人们想把着能不能把一个大的网络分割为若干个小网络。那如何来区别不同IP地址是否在同一网络呢?这里就需要使用子网掩码了,子网掩码与IP地址一样也是一个32位的二进制数,每个IP都规定一定子网掩码,把IP地址与子网掩码进行二进位的与,得到的就是网络号,如果网络号相同就表示二台电脑在同一网段,可以直接通讯,不需要路由器帮忙。这时的IP地址由于需要靠子网掩码来计算网络号,已经不再存在A、B、C类网络的概念了,这时的网络叫无类网络。我们也可以这样理解,需要靠IP地址的第一个字节数据的范围来区别网络号的称有类网络,靠子网掩码来计算得到网络号的叫无类网络。目前我的WINXP和LINUX在指定IP的同时都必须要指定一个子网掩码,所以都属于有类网络。 子网掩码的作用二: 计算网络号 例1:A电脑IP地址为192.168.1.1,子网掩码为255.255.255.0;B电脑IP地址为192.168.1.2,子网掩码为255.255.225.0。大家都知道这二台电脑在同一网段,相互能PING通。 例2:A电脑的IP地址为192.168.1.1,子网掩码为255.255.255.0;B电脑的IP地址为192.168.2.1,子网掩码为255.255.0.0。大家分析一下二台电脑能相互PING通吗? 分析:这个问题需要大家理解子网掩码在网络通讯时的作用。不能简单的认为A电脑处在192.168.1.0网段,B电脑处在192.168.0.0网段,所以不能PING通。正确的分析应该如下: ⑴ 每台电脑事先会把自己IP和自己的子网掩码进行“与”操作,得到自己的网段号,如A电脑处在192.168.1.0网段,B电脑处在192.168.0.0网段。 ⑵ B电脑向A电脑发数据包时,会把A电脑的IP与B电脑的子网掩码进行“与”操作,得到网络号是192.168.0.0,B电脑会认为A电脑与自己在同一网段,所以数据包会顺利发出。 ⑶ A电脑由于与B电脑在同一网段,肯定能收到B电脑发出的数据包,由于PING操作要求A电脑回应一个响应包。这样A电脑会把B电脑的IP与A电脑的子网掩码进行“与”操作,得到网络号192.168.2.0,A电脑发现网络号与自己所处的192.168.1.0不在同一网段,由于A电脑目前没有设置默认网关,所以对该数据包将进行丢弃操作,结果B电脑当然就无法收到A电脑的回应包,所以B电脑上会显示“Request timed out”,即网络超时。 ⑷ 如果在A电脑上去PING B电脑,根据前面的分析,A电脑会认为B电脑与A电脑不在网段,而A电脑又没有设置默认网关,所以会显示“Destination host unreachable”,即目标主机不可达。 转载:子网掩码的作用(详细介绍)]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[计算机网络之子网(掩码)]]></title>
<url>%2F2018%2F08%2F31%2F%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E4%B9%8B%E5%AD%90%E7%BD%91-%E6%8E%A9%E7%A0%81%2F</url>
<content type="text"><![CDATA[子网掩码是用来判断任意两台计算机的IP地址是否属于同一子网络的根据。 最为简单的理解就是两台计算机各自的IP地址与子网掩码进行AND运算后,如果得出的结果是相同的,则说明这两台计算机是处于同一个子网络上的,可以进行直接的通讯。 假设IP编址为某个子网分配一个地位:223.1.1.0/24,其中的/24计法,成为子网掩码(network mask),指示了32位比特中的最左侧24比特定义了子网地址。 子网掩码通常有以下2种格式的表示方法: 通过与IP地址格式相同的点分十进制表示如:255.0.0.0 或255.255.255.128 在IP地址后加上”/“符号以及1-32的数字,其中1-32的数字表示子网掩码中网络标识位的长度如:192.168.1.1/24 的子网掩码也可以表示为255.255.255.0子网掩码一般为255.255.255.0 子网掩码——屏蔽一个IP地址的网络部分的“全1”比特模式。对于A类地址来说,默认的子网掩码是255.0.0.0;对于B类地址来说默认的子网掩码是255.255.0.0;对于C类地址来说默认的子网掩码是255.255.255.0。 子网掩码的设定必须遵循一定的规则。与二进制IP地址相同,子网掩码由1和0组成,且1和0分别连续。子网掩码的长度也是32位,左边是网络位,用二进制数字“1”表示,1的数目等于网络位的长度;右边是主机位,用二进制数字“0”表示,0的数目等于主机位的长度。这样做的目的是为了让掩码与ip地址做按位与运算时用0遮住原主机数,而不改变原网络段数字,而且很容易通过0的位数确定子网的主机数(主机号全为1时表示该网络广播地址,全为0时表示该网络的网络号,这是两个特殊地址)。只有通过子网掩码,才能表明一台主机所在的子网与其他子网的关系,使网络正常工作。]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux命令英文缩写]]></title>
<url>%2F2018%2F08%2F30%2FLinux%E5%91%BD%E4%BB%A4%E8%8B%B1%E6%96%87%E7%BC%A9%E5%86%99%2F</url>
<content type="text"><![CDATA[命令缩写 nc: NetCat(网猫(Netcat)是用来进行TCP/UDP连接的工具,最早是由Hobbit先生在1995年为UNIX所写的程序) ls: list(列出目录内容) cd: Change Directory(改变目录) su: switch user 切换用户 rpm: redhat package manager 红帽子打包管理器 pwd: print work directory 打印当前目录 显示出当前工作目录的绝对路径 ps: process status(进程状态,类似于windows的任务管理器) 常用参数: -auxf ps auxf 显示进程状态 df: disk free 其功能是显示磁盘可用空间数目信息及空间结点信息。换句话说,就是报告在任何安装的设备或目录中,还剩多少自由的空间。 rpm: 即RedHat Package Management,是RedHat的发明之一 rmdir: Remove Directory(删除目录) rm: Remove(删除目录或文件) cat: concatenate连锁 cat file1 file2 >> file3把文件1和文件2的内容联合起来放到file3中 insmod: install module,载入模块 ln -s : link -soft 创建一个软链接,相当于创建一个快捷方式 mkdir: Make Directory ps: Process Status uname: Unix name tar: Tape Archive grep: General Regular Expression Print 目录名 /boot: 顾名思义 /root : 同上 /lost+found: 同上 /run: 同上 /home: 同上 /etc: ETCetera /bin: BINaries /dev: DEVices /lib: LIBraries /mnt: MouNT /proc: PROCesses /tmp: TeMPorary /var: VARiable /srv: SeRVices /opt: OPTion /sbin: Super BINaries(又作Superuser BINaries) /sys: SYStem /usr: 一鼓而作Unix System Resources,再而作Unix Software Resources,三而作Unix Shared Resources(这个很重要,很多人会认为这个是user) 软件及软件包管理 man – MANual apt – Advanced Packaging Tool dpkg – Debian PacKaGe yum – Yellow dog Updater, Modified rpm – RPM Package Manager(又作Redhat Package Manager,不过我比较喜欢前者,因为魔性) 其他 SELinux – Security Enhanced Linux, 安全强化的Linux daemon – Disk And Execution MONitor]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[SQL语法练习(一)]]></title>
<url>%2F2018%2F08%2F28%2FSQL%E8%AF%AD%E6%B3%95%E7%BB%83%E4%B9%A0-%E4%B8%80%2F</url>
<content type="text"><![CDATA[查询学习课程”python”比课程 “java” 成绩高的学生的学号;– 思路:– 获取所有有python课程的人(学号,成绩) - 临时表– 获取所有有java课程的人(学号,成绩) - 临时表– 根据学号连接两个临时表:– 学号 | 物理成绩 | 生物成绩– 然后再进行筛选12345select A.s_id from (select s_id, num as python from score left join course on score.c_id = course.c_id where course.c_name = 'python') as A left join (select s_id, num as java from score left join course on score.c_id = course.c_id where course.c_name = 'java') as B on A.s_id = B.s_id where A.python > B.java; 查询平均成绩大于65分的同学的姓名和平均成绩(保留两位小数); 12345SELECT student.s_name as names, round(AVG(score.num), 2) as averageFROM student, scoreWHERE student.s_id = score.s_idGROUP BY student.s_nameHAVING AVG(score.num) > 65; 查询所有同学的姓名、选课数、总成绩 12345SELECT student.s_name, COUNT(score.s_id) as course_num, SUM(score.num) as total_gradesFROM student, scoreWHERE student.s_id = score.s_idGROUP BY student.s_nameORDER BY student.s_name; 查询所有的课程的名称以及对应的任课老师姓名; 123SELECT course.c_name, teacher.t_name FROM course, teacherWHERE course.t_id = teacher.t_id; 查询没学过“alex”老师课的同学的姓名; 1234567SELECT s_nameFROM studentWHERE student.s_id NOT IN ( SELECT DISTINCT score.s_id FROM score, course, teacher WHERE course.c_id = score.c_id AND teacher.t_id = course.t_id AND teacher.t_name = 'alex' ) 查询学过’python’并且也学过编号’java’课程的同学的姓名 1234567SELECT s_name FROM (SELECT score.s_id as sid, score.c_id as cid FROM score, course WHERE score.c_id = course.c_id AND (course.c_name = 'python' OR course.c_name = 'java')) as BLEFT JOIN student ON B.sid = student.s_idGROUP BY s_nameHAVING COUNT(s_name) > 1; 查询学过“alex”老师所教的全部课程的同学的姓名 12345678910SELECT s_name FROM (SELECT score.s_id as sid, score.c_id as cid FROM score, course, teacher WHERE score.c_id = course.c_id AND teacher.t_id = course.t_id AND teacher.t_name = 'alex') as B LEFT JOIN student ON B.sid = student.s_idGROUP BY s_nameHAVING COUNT(s_name) = (SELECT COUNT(course.c_id) FROM course, teacher WHERE teacher.t_id = course.t_id AND teacher.t_name='alex'); 查询挂科超过两门(包括两门)的学生姓名; 1234567SELECT s_name FROM (SELECT score.s_id as sid, score.c_id as cid FROM score, course WHERE score.c_id = course.c_id AND (score.num < 60 OR score.num ISNULL)) as B LEFT JOIN student ON B.sid = student.s_idGROUP BY s_nameHAVING COUNT(s_name) > 1; 查询有课程成绩小于60分的同学的姓名; 123456SELECT s_name FROM (SELECT score.s_id as sid, score.c_id as cid FROM score, course WHERE score.c_id = course.c_id AND score.num < 60) as B LEFT JOIN student ON B.sid = student.s_idGROUP BY s_name; 查询选修了全部课程的学生姓名; 1234567891011121314SELECT s_name FROM (SELECT score.s_id as sid, score.c_id as cid FROM score, course WHERE score.c_id = course.c_id) as B LEFT JOIN student ON B.sid = student.s_idGROUP BY s_nameHAVING COUNT(s_name) = (SELECT COUNT(*) FROM course);SELECT s_name FROMstudentLEFT JOIN score ON student.s_id = score.s_idGROUP BY s_nameHAVING COUNT(s_name) = (SELECT COUNT(*) FROM course) 查询至少有一门课程与“貂蝉”同学所学课程相同的同学姓名; 123456SELECT s_nameFROM student, scoreWHERE student.s_id = score.s_id AND student.s_name != '貂蝉' AND score.c_id IN (SELECT c_id FROM score, student WHERE score.s_id = student.s_id AND student.s_name = '貂蝉')GROUP BY s_name; 查询学过’貂蝉’同学全部课程的其他同学姓名; 123456789101112SELECT s_name FROM (SELECT score.s_id as sid, score.c_id as cid FROM score, course, student WHERE score.c_id = course.c_id AND student.s_id = score.s_id AND student.s_name != '貂蝉' AND score.c_id in (SELECT c_id FROM score, student WHERE student.s_name = '貂蝉' AND student.s_id = score.s_id) ) as BLEFT JOIN student ON B.sid = student.s_idGROUP BY s_nameHAVING COUNT(s_name) >= (SELECT COUNT(score.c_id) FROM score, student WHERE student.s_name = '貂蝉' AND student.s_id = score.s_id); – ——————————————————————————————————————————————————————————————– INSERT INTO student VALUES (1, ‘鲁班’, 12, ‘男’);– INSERT INTO student VALUES (2, ‘貂蝉’, 20, ‘女’);– INSERT INTO student VALUES (3, ‘刘备’, 35, ‘男’);– INSERT INTO student VALUES (4, ‘关羽’, 34, ‘男’); – INSERT INTO student VALUES (5, ‘张飞’, 33, ‘女’);– –– INSERT INTO teacher VALUES (1, ‘大王’);– INSERT INTO teacher VALUES (2, ‘alex’);– INSERT INTO teacher VALUES (3, ‘egon’); – INSERT INTO teacher VALUES (4, ‘peiqi’);– INSERT INTO course VALUES (1, ‘python’, 1);– INSERT INTO course VALUES (2, ‘java’, 2);– INSERT INTO course VALUES (3, ‘linux’, 3);– INSERT INTO course VALUES (4, ‘go’, 2); – INSERT INTO score VALUES (1, 1, 1, 79);– INSERT INTO score VALUES (2, 1, 2, 77);– INSERT INTO score VALUES (3, 1, 3, 58);– INSERT INTO score VALUES (4, 2, 2, 66);– INSERT INTO score VALUES (5, 2, 3, 77);– INSERT INTO score VALUES (6, 3, 1, 61);– INSERT INTO score VALUES (7, 3, 2, 64); – INSERT INTO score VALUES (8, 4, 3, 70);]]></content>
<categories>
<category>SQL</category>
</categories>
<tags>
<tag>SQL</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux 使用useradd命令时发生了什么]]></title>
<url>%2F2018%2F08%2F24%2FLinux-%E4%BD%BF%E7%94%A8useradd%E5%91%BD%E4%BB%A4%E6%97%B6%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88%2F</url>
<content type="text"><![CDATA[使用useradd命令创建用户时首先会参考 /etc/default/useradd /etc/login.defs /etc/skel/* 命令执行后,接下来就会创建 /etc/passwd /etc/shadow /etc/group /etc/gshadow 主文件目录(如果配置有的话) 所以,如果我们自己了解整个系统运行的状态,也是可以自己手动直接修改相关文件。]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux 文件特殊权限之SUID、GUID、SBIT]]></title>
<url>%2F2018%2F08%2F23%2FLinux-%E6%96%87%E4%BB%B6%E7%89%B9%E6%AE%8A%E6%9D%83%E9%99%90%E4%B9%8BSUID%E3%80%81GUID%E3%80%81SBIT%2F</url>
<content type="text"><![CDATA[文件具有SUID的特殊权限时,代表当用户执行此二进制程序时,在执行过程中该用户会暂时具有程序所有者的权限。比如用户修改自己的用户密码 目录具有SGID的特殊权限时,代表用户在这个目录下面新建的文件用户组都会与该目录的用户组名相同 目录就有SBIT的特殊权限时,代表在该目录下用户创建的文件只有自己和root能够删除]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux根目录下目录介绍及各项缩写全称]]></title>
<url>%2F2018%2F08%2F22%2FLinux%E6%A0%B9%E7%9B%AE%E5%BD%95%E4%B8%8B%E7%9B%AE%E5%BD%95%E4%BB%8B%E7%BB%8D%E5%8F%8A%E5%90%84%E9%A1%B9%E7%BC%A9%E5%86%99%E5%85%A8%E7%A7%B0%2F</url>
<content type="text"><![CDATA[bin (binary) : 常见linux命令、系统所有用户命令目录文件 sbin (superuser binary) : root用户命令文件 dev (device) : 设备驱动存储目录文件 sys (system) : 关于系统总线和设备的额外底层信息。 proc (process): 进程信息文件,/proc文件系统,它允许用户模式进程访问内核数据结构的内容。/proc文件系统将许多内核数据结构的内容输出为一个用户程序可以读的文本文件的层次结构。 usr (UNIX Software Resource): 与软件安装/执行相关 var (variable): 变量文件目录,与系统运作过程有关 etc (etcetera): 所有程序所需要的配置文件 lib (library): 系统默认库路径文件 mnt (mount): 挂载目录 tmp (temporary): 临时文件目录 opt (option): 可选目录(可以选择安装应用程序的目录) boot : 引导程序文件 home : 家目录(用户目录) root : root根目录 media : 多媒体及挂载目录]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[TCP 套接字编程]]></title>
<url>%2F2018%2F07%2F25%2FTCP-%E5%A5%97%E6%8E%A5%E5%AD%97%E7%BC%96%E7%A8%8B%2F</url>
<content type="text"><![CDATA[TCPClient.py12345678910111213141516171819202122from socket import *serverName = 'server.kunxiang.wang'serverPort = 12000clientSocket = socket(AF_INET, SOCK_STREAM)# 如果不绑定客户端进程端口,则自动选择端口clientSocket.bind(('', 34567))clientSocket.connect((serverName, serverPort))msg = input('please input lowercase sentence: ').encode()clientSocket.send(msg)modifiedMsg = clientSocket.recv(2048)print(modifiedMsg)clientSocket.close() TCPServer.py1234567891011121314151617181920from socket import *serverPort = 12000serverSocket = socket(AF_INET, SOCK_STREAM)serverSocket.bind(('', serverPort))serverSocket.listen(1)print('the server is ready to receive')while True: connectionSocket, clientAddress = serverSocket.accept() print(connectionSocket, clientAddress) msg = connectionSocket.recv(1024) print(msg) modifiedMsg = msg.upper() connectionSocket.send(modifiedMsg) connectionSocket.close()]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[UDP 套接字编程]]></title>
<url>%2F2018%2F07%2F25%2FUDP-%E5%A5%97%E6%8E%A5%E5%AD%97%E7%BC%96%E7%A8%8B%2F</url>
<content type="text"><![CDATA[UDPClient.py1234567891011121314151617181920from socket import *# serverName = '140.143.38.125'serverName = 'server.kunxiang.wang'serverPort = 12000clientSocket = socket(AF_INET, SOCK_DGRAM)# 如果不绑定客户端进程端口,则自动选择端口clientSocket.bind(('', 34567))msg = input('please input lowercase sentence: ').encode()clientSocket.sendto(msg, (serverName, serverPort))modifiedMsg, serverAddress = clientSocket.recvfrom(2048)print(modifiedMsg, serverAddress, end='\n')clientSocket.close() UDPServer.py1234567891011from socket import *serverPort = 12000serverSocket = socket(AF_INET, SOCK_DGRAM)serverSocket.bind(('', serverPort))print('The server is ready to receive')while True: message, clientAddress = serverSocket.recvfrom(2048) print(message) print(clientAddress) modifiedMessage = message.upper() serverSocket.sendto(modifiedMessage, clientAddress)]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Mac下打开swf文件]]></title>
<url>%2F2018%2F05%2F11%2FMac%E4%B8%8B%E6%89%93%E5%BC%80swf%E6%96%87%E4%BB%B6%2F</url>
<content type="text"><![CDATA[编辑一个HTML文件,写入如下代码:12345<html> <body> <embed src="your-file-name-of-swf.swf" width="500" height="500"></embed> </body></html> 然后使用chrome打开即可,在chrome下使用打印功能,可以将swf文件保存为pdf格式。]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Mac下profile文件]]></title>
<url>%2F2018%2F05%2F08%2FMac%E4%B8%8Bprofile%E6%96%87%E4%BB%B6%2F</url>
<content type="text"><![CDATA[当你的home下面有.bash_profile或者.bash_login的时候,会忽略调.profile. .profile主要有一下几种方式/etc/profile~/.bash_profile~/.bash_login~/.profile~/.bashrc~/.bash_logout /etc/profile登录的时候读入,默认的设定文件.~/.bash_profile登录之后在/etc/profile载入之后载入,十分重要的配置文件~/.bash_login登录之后如果~/.bash_profile不存在的话,载入这个文件~/.profile登录之后~/.bash_login不存在的话,才载入这个文件~/.bashrcbash shell打开的时候载入 etc下的配置是针对系统,~下的主要是针对用户 参考:https://blog.csdn.net/xdonx/article/details/8312938]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Linux之crontab命令]]></title>
<url>%2F2018%2F05%2F05%2FLinux%E4%B9%8Bcrontab%E5%91%BD%E4%BB%A4%2F</url>
<content type="text"><![CDATA[tldr 基本用法1234567891011121314151617181920Schedule cron jobs to run on a time interval for the current user.Job definition format: "(min) (hour) (day_of_month) (month) (day_of_week) command_to_execute".- Edit the crontab file for the current user: crontab -e- View a list of existing cron jobs for current user: crontab -l- Remove all cron jobs for the current user: crontab -r- Sample job which runs at 10:00 every day. * means any value: 0 10 * * * path/to/script.sh- Sample job which runs every minute on the 3rd of April: * * 3 Apr * path/to/script.sh- Sample job which runs at 02:30 every Friday: 30 2 * * Fri path/to/script.sh 时间格式crontab通过固定的的时间设置格式设置任务的执行时间12345678# Example of job definition:# .---------------- minute (0 - 59)# | .------------- hour (0 - 23)# | | .---------- day of month (1 - 31)# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat# | | | | |# * * * * * user-name command to be executed 第一个参数代表分,其次是小时,然后是日期,之后是月份,最后是所在周的周几。各个字段可以使用特殊字符来代表时间逻辑。 123456星号(*): 代表所有可能的值,例如month字段如果是星号,则表示在满足其它字段的制约条件后每月都执行该命令操作。逗号(a,b,c): 可以用逗号隔开的值指定一个列表范围,例如,“1,2,5,7,8,9”中杠(x-y): 可以用整数之间的中杠表示一个整数范围,例如“2-6”表示“2,3,4,5,6”正斜线(/): 可以用正斜线指定时间的间隔频率,例如“0-23/2”表示每两小时执行一次。组合(*/x): 指定时间类型下,每x分钟或每小时执行一次组合(x-y/z): 指定时间类型下,从x到y时间段内,每z分或每z秒执行一次 如果是当前用户设置自己的定时任务,可以直接通过crontab -e编辑任务,命令格式 * command即可。]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[终端格式化打印json数据的方法]]></title>
<url>%2F2018%2F04%2F25%2F%E7%BB%88%E7%AB%AF%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%89%93%E5%8D%B0json%E6%95%B0%E6%8D%AE%E7%9A%84%E6%96%B9%E6%B3%95%2F</url>
<content type="text"><![CDATA[命令行调试API很方便,对于返回数据是JSON格式的,打印出来的内容超级痛苦。有一种解决方法如下:1echo '{"status":200,"data":[{"id":1000,"name":"John"},{"id":1004,"name":"Tom"}]}' | python -m json.tool 即终端打印的json数据通过管道符号经过python json工具格式化输出,搞定!]]></content>
<categories>
<category>Linux/Mac OS</category>
</categories>
<tags>
<tag>Linux/Mac OS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[python3 产生随机字符串]]></title>
<url>%2F2018%2F04%2F19%2Fpython3-%E4%BA%A7%E7%94%9F%E9%9A%8F%E6%9C%BA%E5%AD%97%E7%AC%A6%E4%B8%B2%2F</url>
<content type="text"><![CDATA[123456import randomimport string# 长度为16的随机字符串rand_str = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16))]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[HTML里引入CSS的四种方式]]></title>
<url>%2F2018%2F04%2F09%2FHTML%E9%87%8C%E5%BC%95%E5%85%A5CSS%E7%9A%84%E5%9B%9B%E7%A7%8D%E6%96%B9%E5%BC%8F%2F</url>
<content type="text"><![CDATA[行内式:也称内联式,在标记的style属性中设定CSS样式。这种方式没有体现出CSS的优势; 嵌入式:将CSS样式集中写在网页的标签对的标签对中; 链接式:跟第4个的导入式都称外部式或者外联式,使用link引用外部CSS文件; 导入式:使用@import引用外部CSS文件;]]></content>
<categories>
<category>Frontend</category>
</categories>
<tags>
<tag>Frontend</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python3 解包和压包]]></title>
<url>%2F2018%2F04%2F06%2FPython3-%E8%A7%A3%E5%8C%85%E5%92%8C%E5%8E%8B%E5%8C%85%2F</url>
<content type="text"><![CDATA[Python中的解包可以这样理解:一个可迭代对象是一个整体,想把可迭代对象中每个元素当成一个个个体剥离出来,这个过程就是解包。下面将举例说明。 可迭代对象每个元素赋值给一个变量12345678910111213141516171819202122232425262728293031323334353637383940# 列表>>> name, age, date = ['Bob', 20, '2018-1-1']>>> name'Bob'>>> age20>>> date'2018-1-1'>>> a, b, c = enumerate(['a', 'b', 'c'])>>> a(0, 'a')# 元组>>> a, b, c = ('a', 'b', 'c')>>> a'a'# 字典>>> a, b, c = {'a':1, 'b':2, 'c':3}>>> a'a'>>> a, b, c = {'a':1, 'b':2, 'c':3}.items()>>> a('a', 1)>>> a, b, c = {'a':1, 'b':2, 'c':3}.values()>>> a1# 字符串>>> a, b, c = 'abc'>>> a'a'# 生成器>>> a, b, c = (x + 1 for x in range(3))>>> b2 星号”*”的使用—解包比如我们要计算平均分,去除最高分和最低分,除了用切片,还可以用解包的方式获得中间的数值123>>> small, *new, big = sorted([93,12,33,55,99])>>> new[33, 55, 93] 压包与解包123456789a = ['a', 'b', 'c']b = [1, 2, 3]for i, j in zip(a, b): print(i+j)# 输出135 分析以上代码: zip函数将a, b压包程一个可迭代对象 对可迭代对象的每一个元素((‘a’, 1))进行解包(i, j = (‘a’, 1)) 接下来可以分别调用i, j变量进行计算 再举一个例子:1234567891011l = [ ('Bob', '1991', '60'), ('Bill', '1992', '65'), ('Mike', '1993', '70')]for name, *args in l: print(name, args)Bob ['1991', '60']Bill ['1992', '65']Mike ['1993', '70'] “_”的用法当一些变量不用时,用_表示是更好的写法,可以让读代码的人知道这个元素是不要的123456>>> p = ('Bob', 20, 50, (11,20,2000))>>> name, *_, (*_, year) = p>>> name'Bob'>>> year2000 多变量同时赋值1234>>> a, b = 1, 2>>> a = 1, 2>>> a(1, 2) 下面用法都会报错12*a = 1, 3a,b,c = 1,2 但是这种写法是可以的123>>> *a, = 1,2>>> a[1,2] “*”之可变参数函数定义时,我们使用*的可变参数,其实也是压包解包过程12345def func(*args): print(args)>>> func(1,2,3,4)(1, 2, 3, 4) 参数用num表示,num变量就可以当成元组调用了。其实这个过程相当于num, = 1,2,5,6 “*”之关键字参数12345def func(**kw): print(kw)>>> func(name = 'Bob', age = 10, weight = 60){'name' = 'Bob', 'age' = 10, 'weight' = 60} 键值对传入**kw,kw就可以表示相应字典。 **的用法只在函数定义中使用,不能这样使用1a, **b = {'name' = 'Bob', 'age' = 10, 'weight' = 60} 可变参数与关键字参数的细节问题函数传入实参时,可变参数*之前的参数不能指定参数名123456789101112def func(a, *b): print(a) print(b)>>> func(a=1, 2,3,4)File "<ipython-input-17-978eea76866e>", line 1 func(a=1, 2,3,4) ^SyntaxError: positional argument follows keyword argumentfunc(1,2,3,4)1(2,3,4) 函数传入实参时,可变参数*之后的参数必须指定参数名,否则就会被归到可变参数之中1234567891011121314def func(a, *b, c = None): print(a) print(b) print(c)>>> func(1,2,3,4)1(2,3,4)None>>> func(1,2,3,c=4)1(2,3)4 如果一个函数想要使用时必须明确指定参数名,可以将所有参数都放在可变参数之后,而可变参数不用管它就可以,也不用命名,如下:1234567def func(*, a, b): print(a) print(b)>>> func(a = 1, b = 2)12 可变参数的这两条特性,可以用于将 只需要按照位置赋值的参数 和 需要明确指定参数名的参数区分开来 关键字参数都只能作为最后一个参数,前面的参数按照位置赋值还是名称赋值都可以1234567891011def func(a, *b, c, **d): print(a) print(b) print(c) print(d)>>> func(1,2,3,c=4,m=5,n=6)1(2,3)4{'m': 5, 'n':6} 可变参数与关键词参数共同使用以表示任意参数下面是这一点在装饰器当中的使用 123456789101112131415def mydecorator(func): def wrapper(*args, **kw): print('i am using a decorator') return func(*args, **kw) return wrapper@mydecoratordef myfun(a, b): print(a) print(b)>>> myfun(1, b = 2)i am using a decorator12 wrapper函数使用*args, **kw作为参数,则被修饰的myfun函数需要的参数无论是什么样的,传入wrapper都不会报错,这保证了装饰器可以修饰各种各样函数的灵活性。毕竟我们一般在函数中传入参数时,要么所有参数名都写,要么前面几个不写,后面的会写,这样使用*args, **kw完全没有问题。 解包作为参数传入函数中首先定义一个函数12def myfun(a, b): print(a + b) 列表|元组的解包123>>> n = [1,2]>>> myfun(*n)3 字典的解包12345>>> mydict = {'a':1, 'b':2}>>> myfun(**mydict)3>>> myfun(*mydict)'ab' 一个简单的应用123>>> Bob = {'name': 'Bob', 'age':20}>>> "{name}'s age is {age}".format(**Bob)"Bob's age is 20" 多返回值函数下面过程也涉及到了解包12345678def myfun(a, b): print a+1, b+1>>> m,n = myfun(1,2)>>> m2>>> n3 其本身是一个元组123>>> p = myfun(1, 2)>>> p(2, 3)]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python3 对象操作的时间复杂度总结]]></title>
<url>%2F2018%2F03%2F29%2FPython3-%E5%AF%B9%E8%B1%A1%E6%93%8D%E4%BD%9C%E7%9A%84%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%E6%80%BB%E7%BB%93%2F</url>
<content type="text"><![CDATA[列表 list列表是以数组(Array)实现的。最大的开销发生在超过当前分配大小的增长,这种情况下所有元素都需要移动;或者是在起始位置附近插入或者删除元素,这种情况下所有在该位置后面的元素都需要移动,这种情况可以考虑使用双向队列来解决。 Operation Average Case Amortized Worst Case Copy O(n) O(n) Append[1] O(1) O(1) Pop last O(1) O(1) Pop intermediate O(k) O(k) Insert O(n) O(n) Get Item O(1) O(1) Set Item O(1) O(1) Delete Item O(n) O(n) Iteration O(n) O(n) Get Slice O(k) O(k) Del Slice O(n) O(n) Set Slice O(k+n) O(k+n) Extend[1] O(k) O(k) Sort O(n log n) O(n log n) Multiply O(nk) O(nk) x in s O(n) min(s), max(s) O(n) Get Length O(1) O(1) 双向队列 collections.dequedeque是以双向链表的形式实现的。双向队列的两端都是可达的,但从查找队列中间的元素较为缓慢,增删元素就更慢了。 操作 平均情况 最坏情况 复制 O(n) O(n) append O(1) O(1) appendleft O(1) O(1) pop O(1) O(1) popleft O(1) O(1) extend O(k) O(k) extendleft O(k) O(k) rotate O(k) O(k) remove O(n) O(n) 集合 set Operation Average case Worst Case notes x in s O(1) O(n) Union s\ t O(len(s)+len(t)) Intersection s&t O(min(len(s), len(t)) O(len(s) * len(t)) replace “min” with “max” if t is not a set Multiple intersection s1&s2&..&sn (n-1)*O(l) where l is max(len(s1),..,len(sn)) Difference s-t O(len(s)) s.difference_update(t) O(len(t)) Symmetric Difference s^t O(len(s)) O(len(s) * len(t)) s.symmetric_difference_update(t) O(len(t)) O(len(t) * len(s)) 字典 dict字典的平均情况基于以下假设: 对象的散列函数足够撸棒(robust),不会发生冲突。 字典的键是从所有可能的键的集合中随机选择的。 Operation Average Case Amortized Worst Case Copy[2] O(n) O(n) Get Item O(1) O(n) Set Item[1] O(1) O(n) Delete Item O(1) O(n) Iteration[2] O(n) O(n) 参考 https://wiki.python.org/moin/TimeComplexity]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
<tag>Algorithm</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Python3 for ... else ...陷阱]]></title>
<url>%2F2018%2F03%2F29%2FPython3-for-else-%E9%99%B7%E9%98%B1%2F</url>
<content type="text"><![CDATA[假设有如下代码:12345for i in range(10): if i == 5: print 'found it! i = %s' % ielse: print 'not found it ...' 我们期望的结果是,当找到5时打印出:1found it! i = 5 实际上打印出来的结果为:12found it! i = 5not found it ... 显然这不是我们期望的结果。 根据官方文档说法: When the items are exhausted (which is immediately when the sequence is empty), the suite in the else clause, if present, is executed, and the loop terminates. A break statement executed in the first suite terminates the loop without executing the else clause’s suite. A continue statement executed in the first suite skips the rest of the suite and continues with the next item, or with the else clause if there was no next item. https://docs.python.org/2/reference/compound_stmts.html#the-for-statement 大意是说当迭代的对象迭代完并为空时,位于else的子句将执行,而如果在for循环中含有break时则直接终止循环,并不会执行else子句。 所以正确的写法应该为:123456for i in range(10): if i == 5: print 'found it! i = %s' % i breakelse: print 'not found it ...' 当使用pylint检测代码时会提示:1Else clause on loop without a break statement (useless-else-on-loop) 所以养成使用pylint检测代码的习惯还是很有必要的,像这种逻辑错误不注意点还是很难发现的。 同样的原理适用于while ... else循环 参考 https://www.cnblogs.com/dspace/p/6622799.html]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[What is the difference between “ is None ” and “ ==None ” --- Reprint]]></title>
<url>%2F2018%2F03%2F29%2FWhat-is-the-difference-between-%E2%80%9C-is-None-%E2%80%9D-and-%E2%80%9C-None-%E2%80%9D-Reprint%2F</url>
<content type="text"><![CDATA[Answer 1The answer is explained here. To quote: A class is free to implement comparison any way it chooses, and it can choose to make comparison against None mean something (which actually makes sense; if someone told you to implement the None object from scratch, how else would you get it to compare True against itself?). Practically-speaking, there is not much difference since custom comparison operators are rare. But you should use is None as a general rule. is None is a bit (~50%) faster than == None :) – Nas Banov Answer 2is always returns True if it compares the same object instance Whereas == is ultimately determined by the __eq__() method i.e. 12345678910class Foo: def __eq__(self,other): return Truefoo=Foo()print(foo==None)# Trueprint(foo is None)# False Answer 3In this case, they are the same. None is a singleton object (there only ever exists one None). is checks to see if the object is the same object, while == just checks if they are equivalent. For example:1234p = [1]q = [1]p is q # False because they are not the same actual objectp == q # True because they are equivalent But since there is only one None, they will always be the same, and is will return True. 123p = Noneq = Nonep is q # True because they are both pointing to the same "None" Answer 4(ob1 is ob2) equal to (id(ob1) == id(ob2)) … but (ob is ob2) is a LOT faster. Timeit says “(a is b)” is 0.0365 usec per loop and “(id(a)==id(b))” is 0.153 usec per loop. 4.2x faster! – AKX {} is {} is false and id({}) == id({}) can be (and is in CPython) true. Reference What is the difference between “ is None ” and “ ==None ” Is there any difference between “foo is None” and “foo == None”?]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Differences between `==` and `is` in Python3?---(Reprint)]]></title>
<url>%2F2018%2F03%2F29%2FDifferences-between-and-is-in-Python3---(Reprint)%2F</url>
<content type="text"><![CDATA[Source QuestionMy Google-fu has failed me. In Python, are the following two tests for equality equivalent? 12345678n = 5# Test one.if n == 5: print 'Yay!'# Test two.if n is 5: print 'Yay!' Does this hold true for objects where you would be comparing instances (a list say)? Okay, so this kind of answers my question: 123456789L = []L.append(1)if L == [1]: print 'Yay!'# Holds true, but...if L is [1]: print 'Yay!'# Doesn't. So == tests value where is tests to see if they are the same object? Answers 1is will return True if two variables point to the same object, == if the objects referred to by the variables are equal. 1234567891011>>> a = [1, 2, 3]>>> b = a>>> b is aTrue>>> b == aTrue>>> b = a[:]>>> b is aFalse>>> b == aTrue In your case, the second test only works because Python caches small integer objects, which is an implementation detail. For larger integers, this does not work: 1234>>> 1000 is 10**3False>>> 1000 == 10**3True The same holds true for string literals: 123456789>>> "a" is "a"True>>> "aa" is "a" * 2True>>> x = "a">>> "aa" is x * 2False>>> "aa" is intern(x*2)True Please see this question as well. Answers 2There is a simple rule of thumb to tell you when to use == or is. == is for value equality. Use it when you would like to know if two objects have the same value. is is for reference equality. Use it when you would like to know if two references refer to the same object. In general, when you are comparing something to a simple type, you are usually checking for value equality, so you should use ==. For example, the intention of your example is probably to check whether x has a value equal to 2 (==), not whether x is literally referring to the same object as 2. Something else to note: because of the way the CPython reference implementation works, you’ll get unexpected and inconsistent results if you mistakenly use is to compare for reference equality on integers: 123456>>> a = 500>>> b = 500>>> a == bTrue>>> a is bFalse That’s pretty much what we expected: a and b have the same value, but are distinct entities. But what about this? 123456>>> c = 200>>> d = 200>>> c == dTrue>>> c is dTrue This is inconsistent with the earlier result. What’s going on here? It turns out the reference implementation of Python caches integer objects in the range -5..256 as singleton instances for performance reasons. Here’s an example demonstrating this: 123456789101112>>> for i in range(250, 260): a = i; print "%i: %s" % (i, a is int(str(i)));...250: True251: True252: True253: True254: True255: True256: True257: False258: False259: False This is another obvious reason not to use is: the behavior is left up to implementations when you’re erroneously using it for value equality. References Is there a difference between == and is in Python?]]></content>
<categories>
<category>Python3 进阶</category>
</categories>
<tags>
<tag>Python3</tag>
</tags>
</entry>
<entry>
<title><![CDATA[原码、反码、补码]]></title>
<url>%2F2018%2F03%2F27%2F%E5%8E%9F%E7%A0%81%E3%80%81%E5%8F%8D%E7%A0%81%E3%80%81%E8%A1%A5%E7%A0%81%2F</url>
<content type="text"><![CDATA[机器数和真值在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念 机器数一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号, 正数为0, 负数为1. 比如,十进制中的数 +3 ,假设计算机字长为8位,转换成二进制就是 00000011。如果是 -3 ,就是 10000011 。那么,这里的 >00000011 和 10000011 就是机器数。 真值因为第一位是符号位,所以机器数的形式值就不等于真正的数值。例如上面的有符号数 10000011,其最高位 1 代表负,其真正数值是 -3 而不是形式值131(10000011 转换成十进制等于 131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。 例:0000 0001 的真值 = +000 0001 = +1,1000 0001 的真值 = –000 0001 = –1 在探求为何机器要使用补码之前, 让我们先了解原码, 反码和补码的概念 原码原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是 8 位二进制: [+1]原 = 0000 0001 [-1]原 = 1000 0001 因为第一位是符号位, 所以 8 位二进制数的取值范围就是: [1111 1111 , 0111 1111] 即 [-127 , 127] 原码是人脑最容易理解和计算的表示方式. 反码反码的表示方法是: 正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。 [+1] = [00000001]原 = [00000001]反 [-1] = [10000001]原 = [11111110]反 可见如果一个反码表示的是负数, 人脑无法直观的看出来它的数值. 通常要将其转换成原码再计算. 补码补码的表示方法是: 正数的补码就是其本身, 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后 +1. (即在反码的基础上 +1) [+1] = [00000001]原 = [00000001]反 = [00000001]补 [-1] = [10000001]原 = [11111110]反 = [11111111]补 对于负数, 补码表示方式也是人脑无法直观看出其数值的. 通常也需要转换成原码在计算其数值. 为何要使用原码, 反码和补码现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数三种编码方式的结果都相同: [+1] = [00000001]原 = [00000001]反 = [00000001]补 是对于负数: [-1] = [10000001]原 = [11111110]反 = [11111111]补 可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢? 首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别”符号位”显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了. 于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码: // 计算十进制的表达式: 1 - 1 = 0 1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2 如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数。 为了解决原码做减法的问题, 出现了反码: // 计算十进制的表达式: 1 - 1 = 0 1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0 发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在”0”这个特殊的数值上. 虽然人们理解上 + 0和 -0 是一样的, 但是 0 带符号是没有任何意义的. 而且会有 [0000 0000]原 和 [1000 0000]原 两个编码表示 0. 于是补码的出现, 解决了0的符号以及两个编码的问题: 1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原 这样 0 用 [0000 0000] 表示, 而以前出现问题的 -0 则不存在了.而且可以用[1000 0000]表示 -128: (-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补 -1-127 的结果应该是 -128, 在用补码运算的结果中, [1000 0000]补 就是 -128. 但是注意因为实际上是使用以前的-0的补码来表示 -128, 所以 -128 并没有原码和反码表示 (对 -128 的补码表示 [1000 0000]补 算出来的原码是[0000 0000]原 , 这是不正确的) 使用补码, 不仅仅修复了 0 的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么 8 位二进制, 使用原码或反码表示的范围为 [-127, +127], 而使用补码表示的范围为 [-128, 127]. 故机器的存储是使用补码, 所以对于编程中常用到的 32 位 int 类型, 可以表示范围是: [-2^31, 2^31-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值。 参考 https://www.jianshu.com/p/279d9eba0985]]></content>
<tags>
<tag>计算机基础</tag>
</tags>
</entry>
<entry>
<title><![CDATA[正向代理和反向代理]]></title>
<url>%2F2018%2F03%2F26%2F%E6%AD%A3%E5%90%91%E4%BB%A3%E7%90%86%E5%92%8C%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86%2F</url>
<content type="text"><![CDATA[正向代理(forward proxy)代理客户端,隐藏真实客户端,反向代理(reverse proxy)代理服务器,隐藏真实服务端。 反向代理举例:用户想访问:”http://ooxx.me/readme",但ooxx.me上并不存在readme页面,他是偷偷从另外一台服务器上取回来,然后作为自己的内容吐给用户,但用户并不知情,这很正常,用户一般都很笨。这里所提到的ooxx.me 这个域名对应的服务器就设置了反向代理功能。 结论:对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端,就像这些内容原本就是它自己的一样。 在计算机世界里,由于单个服务器的处理客户端(用户)请求能力有一个极限,当用户的接入请求蜂拥而入时,会造成服务器忙不过来的局面,可以使用多个服务器来共同分担成千上万的用户请求,这些服务器提供相同的服务,对于用户来说,根本感觉不到任何差别。 反向代理用途 保证内网的安全,隐藏和保护原始服务器。可以使用反向代理提供WAF功能,阻止web攻击。大型网站,通常将反向代理作为公网访问地址,Web服务器是内网。 负载均衡,通过反向代理服务器来优化网站的负载 反向代理的实现 需要有一个负载均衡设备来分发用户请求,将用户请求分发到空闲的服务器上 服务器返回自己的服务到负载均衡设备 负载均衡将服务器的服务返回用户 以上的潜台词是:用户和负载均衡设备直接通信,也意味着用户做服务器域名解析时,解析得到的IP其实是负载均衡的IP,而不是服务器的IP,这样有一个好处是,当新加入/移走服务器时,仅仅需要修改负载均衡的服务器列表,而不会影响现有的服务。 正向代理正向代理,也就是传说中的代理,他的工作原理就像一个跳板,简单的说,我是一个用户,我访问不了某网站,但是我能访问一个代理服务器,这个代理服务器呢,他能访问那个我不能访问的网站,于是我先连上代理服务器,告诉他我需要那个无法访问网站的内容,代理服务器去取回来,然后返回给我。从网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。 正向代理是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理。 正向代理的用途 访问原来无法访问的资源,如google 可以做缓存,加速访问资源 对客户端访问授权,上网进行认证 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息 为了便于理解,摘自阿笠博士的回答,正向代理中,proxy和client同属一个LAN,对server透明;反向代理中,proxy和server同属一个LAN,对client透明。实际上proxy在两种代理中做的事都是代为收发请求和响应,不过从结构上来看正好左右互换了下,所以把后出现的那种代理方式叫成了反向代理。 正向代理和反向代理的区别从用途上来讲:正向代理的典型用途是为在防火墙内的局域网客户端提供访问Internet的途径。正向代理还可以使用缓冲特性减少网络使用率。反向代理的典型用途是将防火墙后面的服务器提供给Internet用户访问。反向代理还可以为后端的多台服务器提供负载平衡,或为后端较慢的服务器提供缓冲服务。另外,反向代理还可以启用高级URL策略和管理技术,从而使处于不同web服务器系统的web页面同时存在于同一个URL空间下。 从安全性来讲:正向代理允许客户端通过它访问任意网站并且隐藏客户端自身,因此你必须采取安全措施以确保仅为经过授权的客户端提供服务。反向代理对外都是透明的,访问者并不知道自己访问的是一个代理。 打个比方,a,b,c三个人,正向代理是a通过b向C借钱,a知道c的存在 。反向代理是a向b借钱,b又向C借,a不知道c的存在。 参考 https://www.zhihu.com/question/24723688/answer/160252724 https://blog.csdn.net/andyzhaojianhui/article/details/48247969 https://www.cnblogs.com/Anker/p/6056540.html]]></content>
<categories>
<category>网络</category>
</categories>
<tags>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title><![CDATA[一次完整的HTTP请求流程]]></title>
<url>%2F2018%2F03%2F20%2F%E4%B8%80%E6%AC%A1%E5%AE%8C%E6%95%B4%E7%9A%84HTTP%E8%AF%B7%E6%B1%82%E6%B5%81%E7%A8%8B%2F</url>
<content type="text"><![CDATA[HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤: 1. 建立TCP连接:在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet, 即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更高层协议的连接, 因此,首先要建立TCP连接,一般TCP连接的端口号是80。 2. Web浏览器向Web服务器发送请求命令:一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET/sample/hello.jsp HTTP/1.1。 3. Web浏览器发送请求头信息 :浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。 4. Web服务器应答 :客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。 5. Web服务器发送应答头信息:正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。 6. Web服务器向浏览器发送数据:Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。 7. Web服务器关闭TCP连接 :一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive; TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。]]></content>
<categories>
<category>网络</category>
</categories>
<tags>