RxJava2学习笔记(3)
接上回继续,今天来学习下zip(打包)操作
一、zip操作
@Test
public void zipTest() {
Observable.zip(Observable.create(emitter -> {
for (int i = 0; i < 10; i++) {
emitter.onNext(100 + i);
}
}), Observable.create(emitter -> {
for (int i = 0; i < 5; i++) {
emitter.onNext(new Character((char) (65 + i)));
}
}), (integer, character) -> integer + "" + character).subscribe(s -> System.out.println(s));
}
zip字面意义,就是打包操作,把多个Obserable合并在一起,形成一个新的Obserable,类似文件1、文件2 ... 文件n,合成一个新文件。上面这段代码的输出:
100A
101B
102C
103D
104E
第1个生产者,发射了10个数字(100~109),第1个生产者发射了5个字符(A~E),合并处理时,会把 “数字+字符",变成一个新字符串,然后继续发射。注意:这里有一个类似"木桶原理",即决定一个木桶能盛多少水的,永远是最短的那块木头。10发A型子弹 + 5发B型子弹,按1:1来合成,最终只有得到5发新型子弹。
二、限流
生产者-消费者模型中,有可能会遇到这样一种情况:生产者精力旺盛,狂生产数据,然后消费者力不从心,根本来不及处理,这样上游就堵住了,严重的话,可能导致内存耗尽。最简单的办法,就是把来不及处理的内容给扔掉(即:丢弃策略)。刚刚提到的zip操作中的木桶原理,就可以派上用场了。
@Test
public void zipTest1() throws InterruptedException {
Observable.zip(Observable.create(emitter -> {
for (int i = 0; ; i++) { //一直不停的发
emitter.onNext(i);
}
}).subscribeOn(Schedulers.newThread()), Observable.create(emitter -> {
for (int i = 0; i < 5; i++) {
emitter.onNext(0); //这里技巧性的处理:发1个0过去
}
}).subscribeOn(Schedulers.newThread()),
(BiFunction<Object, Object, Object>) (i1, i2) -> (Integer) i1 + (Integer) i2) //1个数字+0,不影响原值
.subscribe(integer -> System.out.println(integer)); Thread.sleep(200);
}
输出:
0
1
2
3
4
如果是字符串,可以参考下面这样处理:
Observable.zip(Observable.create(emitter -> {
for (int i = 0; ; i++) {
emitter.onNext("A" + i);
}
}).subscribeOn(Schedulers.newThread()), Observable.create(emitter -> {
for (int i = 0; i < 5; i++) {
emitter.onNext("");
}
}).subscribeOn(Schedulers.newThread()),
(BiFunction<Object, Object, Object>) (i1, i2) -> (String) i1 + (String) i2)
.subscribe(s -> System.out.println(s));
Thread.sleep(200);
输出:
A0
A1
A2
A3
A4
三、Flowable
刚才用zip这种"奇淫技巧"实现了限流,但其实rxjava还有更科学的做法(Flowable)。再思考一下“限流”这种场景,生产者太猛,一下喷出来的量太多,而消费者太弱,完全吸收不下。比较温和的方式,最好是生产者喷发前先问下消费者,你1次能接承受多大的量?我根据你的能力来调整(多么体贴)没错!rxjava就是这么体贴,你想到的,它也想到了。
Flowable.create((FlowableOnSubscribe<Integer>) emitter -> {
for (int i = 0; ; i++) {
emitter.onNext(i);
}
}, BackpressureStrategy.DROP) //这里的BackpressureStrategy.DROP是一种丢弃策略
.subscribe(new Subscriber<Integer>() { @Override
public void onSubscribe(Subscription s) {
s.request(2); //只接收2条信息
} @Override
public void onNext(Integer integer) {
System.out.println("onNext->" + integer);
} @Override
public void onError(Throwable t) {
System.out.println("onError!");
} @Override
public void onComplete() {
System.out.println("onComplete");
}
}); Thread.sleep(1000);
注意: onSubscribe 里有一行s.request(2),相当于消费者在订阅时,告诉生产者,只能处理2条记录。然后跑起来,就真的只有2条输出了:
onNext->0
onNext->1
值得一提的是:剩下的消息,虽然消费者不再处理了,但是生产者实际上还会继续发的,大家可以在emitter.onNext(i)这后面,输入一行文字,自行测试。之所以这么设计,大家可以思考一下,因为一个生产者射出来的东西,可能有多个消费者在消费,如果因为某1个消费者说:哎呀,太多了,我消化不了,你赶紧停下! 然后生产者如果真的停下来,其它消费者可能就有意见了。
但是,如果只有一个消费者的情况下,我们就是想让生产者严格按照消费者的处理能力来发送数据,该怎么做呢?先把上面这段代码加几行输出看看:
@Test
public void flowableTest() throws InterruptedException {
Flowable.create((FlowableOnSubscribe<Integer>) emitter -> {
for (int i = 0; i <= 20; i++) {
System.out.println("requested=>" + emitter.requested());
emitter.onNext(i);
}
}, BackpressureStrategy.ERROR)
.subscribe(new Subscriber<Integer>() { @Override
public void onSubscribe(Subscription s) {
s.request(5);
} @Override
public void onNext(Integer integer) {
System.out.println("onNext->" + integer);
} @Override
public void onError(Throwable t) {
System.out.println("onError=>" + t.getMessage());
} @Override
public void onComplete() {
System.out.println("onComplete");
}
});
Thread.sleep(1000);
}
输出:
requested=>5
onNext->0
requested=>4
onNext->1
requested=>3
onNext->2
requested=>2
onNext->3
requested=>1
onNext->4
requested=>0
onError=>create: could not emit value due to lack of requests
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
requested=>0
注意:当消费者设置了request(x)后,生产者里的requested值,就会设置成相应的x值(仅同步模式),然后每emitter.onNext()发一次数据,这个值就减少,第11,12行,当生产者emitter的requested值为0时,下游就开始报错了,也就是说这时已经达到了消费者的处理极限。利用这一点,就可以实现我们刚才说的小目标:
@Test
public void flowableTest() throws InterruptedException {
Flowable.create((FlowableOnSubscribe<Integer>) emitter -> {
for (int i = 0; i <= 20; i++) {
System.out.println("requested=>" + emitter.requested());
// 想想:为什么不用if这种判断方式?
// if (emitter.requested()>0){
// emitter.onNext(i);
// }
while (emitter.requested() <= 0) {
Thread.sleep(10);//防止cpu占用过高
continue;
}
emitter.onNext(i);
}
}, BackpressureStrategy.ERROR)
.subscribe(new Subscriber<Integer>() { @Override
public void onSubscribe(Subscription s) {
s.request(5);
} @Override
public void onNext(Integer integer) {
System.out.println("onNext->" + integer);
} @Override
public void onError(Throwable t) {
System.out.println("onError=>" + t.getMessage());
} @Override
public void onComplete() {
System.out.println("onComplete");
}
});
Thread.sleep(1000);
}
输出:
requested=>5
onNext->0
requested=>4
onNext->1
requested=>3
onNext->2
requested=>2
onNext->3
requested=>1
onNext->4
requested=>0
上面这些都是同步情况下(即:生产者与消费者都在一个线程里)Flowable的处理方法,如果是在异步多线程情况下,我们来看看是否能继续适用:
@Test
public void flowableTest() throws InterruptedException {
Flowable.create((FlowableOnSubscribe<Integer>) emitter -> {
for (int i = 0; i <= 200; i++) {
System.out.println("requested=>" + emitter.requested());
while (emitter.requested() <= 0) {
Thread.sleep(10);//防止cpu占用过高
continue;
}
emitter.onNext(i);
}
}, BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.newThread()) //生产者使用独立线程
.observeOn(Schedulers.newThread()) //消费者使用独立线程
.subscribe(new Subscriber<Integer>() { @Override
public void onSubscribe(Subscription s) {
s.request(5);
} @Override
public void onNext(Integer integer) {
System.out.println("onNext->" + integer);
} @Override
public void onError(Throwable t) {
System.out.println("onError=>" + t.getMessage());
} @Override
public void onComplete() {
System.out.println("onComplete");
}
}); while (true) {
Thread.sleep(1000);
}
}
输出:
requested=>128
requested=>127
requested=>126
requested=>125
requested=>124
requested=>123
requested=>122
requested=>121
requested=>120
requested=>119
requested=>118
requested=>117
requested=>116
requested=>115
requested=>114
requested=>113
requested=>112
requested=>111
requested=>110
requested=>109
requested=>108
requested=>107
requested=>106
requested=>105
requested=>104
onNext->0
requested=>103
onNext->1
requested=>102
onNext->2
requested=>101
onNext->3
requested=>100
onNext->4
requested=>99
requested=>98
requested=>97
requested=>96
requested=>95
requested=>94
requested=>93
requested=>92
requested=>91
requested=>90
requested=>89
requested=>88
requested=>87
requested=>86
requested=>85
requested=>84
requested=>83
requested=>82
requested=>81
requested=>80
requested=>79
requested=>78
requested=>77
requested=>76
requested=>75
requested=>74
requested=>73
requested=>72
requested=>71
requested=>70
requested=>69
requested=>68
requested=>67
requested=>66
requested=>65
requested=>64
requested=>63
requested=>62
requested=>61
requested=>60
requested=>59
requested=>58
requested=>57
requested=>56
requested=>55
requested=>54
requested=>53
requested=>52
requested=>51
requested=>50
requested=>49
requested=>48
requested=>47
requested=>46
requested=>45
requested=>44
requested=>43
requested=>42
requested=>41
requested=>40
requested=>39
requested=>38
requested=>37
requested=>36
requested=>35
requested=>34
requested=>33
requested=>32
requested=>31
requested=>30
requested=>29
requested=>28
requested=>27
requested=>26
requested=>25
requested=>24
requested=>23
requested=>22
requested=>21
requested=>20
requested=>19
requested=>18
requested=>17
requested=>16
requested=>15
requested=>14
requested=>13
requested=>12
requested=>11
requested=>10
requested=>9
requested=>8
requested=>7
requested=>6
requested=>5
requested=>4
requested=>3
requested=>2
requested=>1
requested=>0
可以发现,之前的套路不管用了,生产者还是在一直持续不停的发送,但是并没有发射满200次,而是正好等于缓冲区大小128(关于128这个数字,可参考本文最后的参考文章)。
先来解决异步场景下,生产者为啥不能发送超过128条消息的问题,把上面的问题略改一下:
@Test
public void flowableTest() throws InterruptedException {
Flowable.create((FlowableOnSubscribe<Integer>) emitter -> {
for (int i = 0; i <= 200; i++) {
System.out.println("requested=>" + emitter.requested());
while (emitter.requested() <= 0) {
Thread.sleep(10);
continue;
}
emitter.onNext(i);
}
}, BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<Integer>() { @Override
public void onSubscribe(Subscription s) {
s.request(96); //神奇的96
} @Override
public void onNext(Integer integer) {
System.out.println("onNext->" + integer);
} @Override
public void onError(Throwable t) {
System.out.println("onError=>" + t.getMessage());
} @Override
public void onComplete() {
System.out.println("onComplete");
}
}); while (true) {
Thread.sleep(1000);
}
}
注意:19行,消费者一上来,就设置request(96),这是一个神奇的数字96,低于这个值,生产者仍然只能最多发送128个事件,达到这个值,生产者就可以继续发送(详情分析见本文最后的参考文章)。输出如下:
requested=>128
requested=>127
requested=>126
requested=>125
requested=>124
requested=>123
requested=>122
requested=>121
requested=>120
requested=>119
requested=>118
requested=>117
requested=>116
requested=>115
requested=>114
requested=>113
requested=>112
requested=>111
requested=>110
requested=>109
requested=>108
requested=>107
requested=>106
requested=>105
requested=>104
requested=>103
requested=>102
requested=>101
onNext->0
requested=>100
onNext->1
requested=>99
onNext->2
requested=>98
onNext->3
requested=>97
onNext->4
onNext->5
onNext->6
onNext->7
onNext->8
onNext->9
onNext->10
onNext->11
onNext->12
requested=>96
onNext->13
requested=>95
onNext->14
requested=>94
onNext->15
requested=>93
onNext->16
requested=>92
onNext->17
requested=>91
onNext->18
requested=>90
onNext->19
requested=>89
onNext->20
requested=>88
onNext->21
requested=>87
onNext->22
requested=>86
onNext->23
requested=>85
onNext->24
requested=>84
onNext->25
requested=>83
onNext->26
requested=>82
onNext->27
requested=>81
requested=>80
requested=>79
onNext->28
requested=>78
requested=>77
requested=>76
onNext->29
requested=>75
onNext->30
requested=>74
onNext->31
requested=>73
onNext->32
requested=>72
onNext->33
requested=>71
onNext->34
requested=>70
onNext->35
requested=>69
onNext->36
requested=>68
onNext->37
requested=>67
onNext->38
requested=>66
onNext->39
requested=>65
onNext->40
requested=>64
onNext->41
requested=>63
onNext->42
requested=>62
onNext->43
requested=>61
requested=>60
requested=>59
requested=>58
onNext->44
requested=>57
requested=>56
requested=>55
requested=>54
requested=>53
onNext->45
requested=>52
onNext->46
requested=>51
onNext->47
requested=>50
onNext->48
requested=>49
onNext->49
requested=>48
onNext->50
requested=>47
onNext->51
requested=>46
onNext->52
requested=>45
onNext->53
requested=>44
onNext->54
requested=>43
onNext->55
requested=>42
onNext->56
requested=>41
onNext->57
requested=>40
onNext->58
requested=>39
onNext->59
onNext->60
requested=>38
onNext->61
requested=>37
onNext->62
requested=>36
onNext->63
requested=>35
onNext->64
requested=>34
onNext->65
requested=>33
onNext->66
requested=>32
onNext->67
requested=>31
onNext->68
requested=>30
onNext->69
requested=>29
onNext->70
requested=>28
onNext->71
requested=>27
onNext->72
requested=>26
onNext->73
requested=>25
onNext->74
requested=>24
onNext->75
requested=>23
onNext->76
requested=>22
onNext->77
requested=>21
onNext->78
requested=>20
onNext->79
requested=>19
onNext->80
requested=>18
onNext->81
requested=>17
onNext->82
requested=>16
onNext->83
requested=>15
onNext->84
requested=>14
onNext->85
requested=>13
onNext->86
requested=>12
onNext->87
requested=>11
onNext->88
requested=>10
onNext->89
requested=>9
onNext->90
requested=>8
onNext->91
requested=>7
onNext->92
requested=>6
onNext->93
requested=>5
onNext->94
requested=>4
onNext->95
requested=>3
requested=>98
requested=>97
requested=>96
requested=>95
requested=>94
requested=>93
requested=>92
requested=>91
requested=>90
requested=>89
requested=>88
requested=>87
requested=>86
requested=>85
requested=>84
requested=>83
requested=>82
requested=>81
requested=>80
requested=>79
requested=>78
requested=>77
requested=>76
requested=>75
requested=>74
requested=>73
requested=>72
requested=>71
requested=>70
requested=>69
requested=>68
requested=>67
requested=>66
requested=>65
requested=>64
requested=>63
requested=>62
requested=>61
requested=>60
requested=>59
requested=>58
requested=>57
requested=>56
requested=>55
requested=>54
requested=>53
requested=>52
requested=>51
requested=>50
requested=>49
requested=>48
requested=>47
requested=>46
requested=>45
requested=>44
requested=>43
requested=>42
requested=>41
requested=>40
requested=>39
requested=>38
requested=>37
requested=>36
requested=>35
requested=>34
requested=>33
requested=>32
requested=>31
requested=>30
requested=>29
requested=>28
requested=>27
requested=>26
requested=>25
requested=>24
注意223行,requested值下降后,又开始回升了。另外一个问题,异步多线程中,消费者调用了request(n)方法试图改变生产者的requested值,但是消费者与生产者不在同一个线程中,跨线程实时共享变量是有困难的(当然,也可以借助一些中间件,比如redis来保存共享变量,但这就超出rxjava的范畴了)。
有一些场景,生产者是能够知道发射该什么时候完成的(比如:处理某个文本文件,读到最后一行了,就认为完成了),这种情况下可以借助flowable来改善程序的性能。比如:处理一个超大的文本(体积>1G),如果全都加载到内存中,估计直接OOM了,下面演示了如何优雅的应对这类场景:(代码主要来源于参考文章中的例子,略做调整)
目标:把一个文件的每一行内容,都逆序输出。
@Test
public void translateTest() throws Exception {
Flowable.create((FlowableOnSubscribe<String>) emitter -> {
try {
FileReader reader = new FileReader("/data/application/test.txt");
BufferedReader br = new BufferedReader(reader);
String str;
while ((str = br.readLine()) != null && !emitter.isCancelled()) {
while (emitter.requested() == 0) {
if (emitter.isCancelled()) {
break;
}
}
emitter.onNext(str);
System.out.println("原文=> " + str + " =>" + emitter.requested());
Thread.sleep(50); //模拟耗时处理
}
br.close();
reader.close();
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}, BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<String>() {
Subscription s; @Override
public void onSubscribe(Subscription subscription) {
s = subscription;
s.request(1); //开始先请求1行数据
} @Override
public void onNext(String str) {
System.out.println("\t逆序<= " + StringUtils.reverse(str));
s.request(1);//每处理完1行数据,再请求1行
try {
Thread.sleep(100); //模拟耗时处理
} catch (InterruptedException e) {
e.printStackTrace();
}
} @Override
public void onError(Throwable throwable) {
System.out.println("出错啦:" + throwable.getMessage());
} @Override
public void onComplete() {
System.out.println("完成!");
}
}); while (true) {
Thread.sleep(50);
} }
附:test.txt文本内容如下
青花瓷
词:方文山
曲:周杰伦 素胚勾勒出青花笔锋浓转淡
瓶身描绘的牡丹一如你初妆
冉冉檀香透过窗心事我了然
宣纸上走笔至此搁一半 釉色渲染仕女图韵味被私藏
而你嫣然的一笑如含苞待放
你的美一缕飘散
去到我去不了的地方 天青色等烟雨而我在等你
炊烟袅袅升起隔江千万里
在瓶底书汉隶仿前朝的飘逸
就当我为遇见你伏笔
天青色等烟雨而我在等你
月色被打捞起晕开了结局
如传世的青花瓷自顾自美丽
你眼带笑意 色白花青的锦鲤跃然于碗底
临摹宋体落款时却惦记着你
你隐藏在窑烧里千年的秘密
极细腻犹如绣花针落地 帘外芭蕉惹骤雨门环惹铜绿
而我路过那江南小镇惹了你
在泼墨山水画里
你从墨色深处被隐去 天青色等烟雨而我在等你
炊烟袅袅升起隔江千万里
在瓶底书汉隶仿前朝的飘逸 就当我为遇见你伏笔
天青色等烟雨而我在等你
月色被打捞起晕开了结局
如传世的青花瓷自顾自美丽
你眼带笑意 天青色等烟雨
而我在等你
炊烟袅袅升起
隔江千万里
在瓶底书汉隶仿前朝的飘逸
就当我为遇见你伏笔
天青色等烟雨
而我在等你
月色被打捞起
晕开了结局
如传世的青花瓷自顾自美丽
你眼带笑意 ----------------------------
<茶汤> 山岚像茶杯上的云烟
颜色越来越浅
你越走越远
《茶汤》演唱者郁可唯
《茶汤》演唱者郁可唯
有好多的话还来不及兑现,你就不见
我身后窗外那片梯田
像一段段从前
我站在茶园,抬头望着天,想象你会在山的,那一边
我说再喝一碗我熬的茶汤
你说你现在马上要渡江
渡江到那遥远的寒冷北方
就怕你的手会冻僵
你何时回来喝我熬的茶汤
这次我会多放一些老姜
你寄来的信一直搁在桌上
不知要寄还哪地方
北风它经过多少村落
来来回回绕过
分不清那年,我求天保佑,只见风声大做,却更寂寞
那庄稼已经几次秋收,麦田几次成熟
于是我焚香,安静的难过,你还是一直没有,回来过
我说再喝一碗我熬的茶汤
你说你现在马上要渡江
渡江到那遥远的寒冷北方
就怕你的手会冻僵
你何时回来喝我熬的茶汤
这次我会多放一些老姜
你寄来的信一直搁在桌上
不知要寄还哪地方
我身后窗外那片梯田
像一段段从前
我站在茶园,抬头望着天,想象你会在山的,那一边
我说再喝一碗我熬的茶汤
你说你现在马上要渡江
渡江到那遥远的寒冷北方
就怕你的手会冻僵
你何时回来喝我熬的茶汤
这次我会多放一些老姜
你寄来的信一直搁在桌上
不知要寄还哪地方
我说再喝一碗我熬的茶汤
你说你现在马上要渡江
想问你到底是否有种药方
让热汤永远不会凉
你何时回来喝我熬的茶汤
这次我会多放一些老姜
你寄来的信一直搁在桌上
不知要寄还哪地方 ----------------------------------
<东风破> 一盏离愁 孤单伫立在窗口
我在门后 假装你人还没走
旧地如重游 月圆更寂寞
夜半清醒的烛火 不忍苛责我
一壶漂泊 浪迹天涯难入喉
你走之后 酒暖回忆思念瘦
水向东流 时间怎么偷
花开就一次成熟 我却错过
谁在用琵琶弹奏 一曲东风破
岁月在墙上剥落 看见小时候
犹记得那年我们都还很年幼
而如今琴声幽幽 我的等候你没听过
谁在用琵琶弹奏 一曲东风破
枫叶将故事染色 结局我看透
篱笆外的古道我牵着你走过
荒烟漫草的年头 就连分手都很沉默
一壶漂泊 浪迹天涯难入喉
你走之后 酒暖回忆思念瘦
水向东流 时间怎么偷
花开就一次成熟 我却错过
谁在用琵琶弹奏 一曲东风破
岁月在墙上剥落 看见小时候
犹记得那年我们都还很年幼
而如今琴声幽幽 我的等候你没听过
谁在用琵琶弹奏 一曲东风破
枫叶将故事染色 结局我看透
篱笆外的古道我牵着你走过
荒烟漫草的年头 就连分手都很沉默
谁在用琵琶弹奏 一曲东风破
岁月在墙上剥落 看见小时候
犹记得那年我们都还很年幼
而如今琴声幽幽 我的等候你没听过
谁在用琵琶弹奏 一曲东风破
枫叶将故事染色 结局我看透
篱笆外的古道我牵着你走过
荒烟漫草的年头 就连分手都很沉默 -------------- 结束!
参考文章:
https://www.jianshu.com/p/9b1304435564
https://www.jianshu.com/p/a75ecf461e02
RxJava2学习笔记(3)的更多相关文章
- rxjava2学习笔记(1)
1.什么是RxJava? 简单,清晰的多线程编程框架.可方便的写出维护性高,逻辑清晰的Java程序. 2.什么是观察者模式? 入门教程讲这种高大上抽象概念都是耍流氓! 3.开始 3.1github地址 ...
- RxJava2学习笔记(2)
上一篇已经熟悉了Observable的基本用法,但是如果仅仅只是“生产-消费”的模型,这就体现不出优势了,java有100种办法可以玩这个:) 一.更简单的多线程 正常情况下,生产者与消费者都在同一个 ...
- RxJava2.0学习笔记2 2018年7月3日 周二
摘记: 1.map -- 转换 有些服务端的接口设计,会在返回的数据外层包裹一些额外信息,这些信息对于调试很有用,但本地显示是用不到的.使用 map() 可以把外层的格式剥掉,只留下本地会用到的核心 ...
- ReactiveX 学习笔记(0)学习资源
ReactiveX 学习笔记 ReactiveX 学习笔记(1) ReactiveX 学习笔记(2)创建数据流 ReactiveX 学习笔记(3)转换数据流 ReactiveX 学习笔记(4)过滤数据 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
- JAVA GUI编程学习笔记目录
2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...
随机推荐
- Linux常用命令2(远程文件下载+查看文件内容)
一.远程文件下载的两种方法:ftp命令 + scp命令 ftp命令: 服务器若安装了ftp Server,另外一台Linux可以使用ftp的client程序来进行文件的远程拷贝读取下载和写入上载. 1 ...
- GitHub上优秀的Go开源项目
近一年来,学习和研究Go语言,断断续续的收集了一些比较优秀的开源项目,这些项目都非常不错,可以供我们学习和研究Go用,从中可以学到很多关于Go的使用.技巧以及相关工具和方法.我把他们整理发出来,大家有 ...
- java开发之——[接口回调]
一.回调的含义和用途 1. 什么是回调? 一般来说,模块之间都存在一定的调用关系,从调用方式上看,可以分为三类:同步调用.异步调用和回调.同步调用是一种阻塞式调用,即在函数A的函数体里通过书写函数B的 ...
- java比较两个对象是否相等?
1.判断两个对象是否是同一个引用对象则用==,"=="比的是地址.因为如果地址相同,则就是同一个对象(java中如果两对象(obj1,obj2)相等,那么在修改obj2的时候,ob ...
- linux下各目录的作用
这么久了,一直觉得对于linux的运作情况还是懵懵懂懂的样子,刚才专门又看了一下 linux 下各目录的作用,记下来,以备以后再忘了. 下面内容来自:http://www.linuxidc.com/L ...
- lodash篇之对象深度比较_.isEqual
- PHP替换指定字符串
在PHP中,有两个函数可以实现字符串替换,strtr()和str_repalce()函数. 首先我们简单了解下strtr()函数的定义及语法. strtr:转换指定字符. 两个语法: 第一种语法: s ...
- centos下配置DNS
centos网络配置实例 1,配置DNSvi /etc/resolv.conf加入: 代码如下: nameserver 192.168.0.1 nameserver 8.8.8.8 nameserve ...
- 详解如何进行第三方App接入微信登录
微信登录接入 微信登录遵循协议Aouth2.0中的授权码模式 我们来看一下Aouth2.0中的授权码模式是怎么定义的: 授权码模式(authorization code)是功能最完整.流程最严密的授权 ...
- Java 和 C++ 的部分区别
1. Java是解释型语言,所谓的解释型语言,就是源码会先经过一次编译,成为中间码,中间码再被解释器解释成机器码.对于Java而言,中间码就是字节码(.class),而解释器在JVM中内置了. 2. ...