android--RxJava源码详解

其实所有的节日都不是为了礼物和红包而生,而是为了提醒我们,不要忘记爱与被爱,生活需要仪式感,而你需要的是在乎和关爱

目录

前言

一,Hook点

二,RXJava的观察者模式

三,Map操作符原理

前言

关于RXJava的使用请移步文章

android--RXJava详细使用篇-CSDN博客

android--RXJava+Retrofit封装使用-CSDN博客 

一,Hook点

首先我们写一个RxJava从订阅到分发的简单实例:

Observable.create(new ObservableOnSubscribe<Object>() {@Overridepublic void subscribe(ObservableEmitter<Object> e) throws Exception {e.onNext("袁震");}
}).map(new Function<Object, Boolean>() {@Overridepublic Boolean apply(Object o) throws Exception {return true;}
})
.subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean aBoolean) throws Exception {}
});

然后我们点击Observable.create:

 @CheckReturnValue@SchedulerSupport(SchedulerSupport.NONE)public static <T> Observable<T> create(ObservableOnSubscribe<T> source) {//空检查,不用管ObjectHelper.requireNonNull(source, "source is null");//关键代码return RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));}

 第一句代码不用管,是一个空检查。我们看这句代码

RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));

参数是关于创建的被观察者的,我们先不用管。先看下onAssembly:

@NonNull
public static <T> Observable<T> onAssembly(@NonNull Observable<T> source) {Function<? super Observable, ? extends Observable> f = onObservableAssembly;if (f != null) {return apply(f, source);}return source;
}

在这段代码中,如果onObservableAssembly为空,则直接返回我们传入的被观察者。

如果不为空,则继续走apply函数:

@NonNull
static <T, R> R apply(@NonNull Function<T, R> f, @NonNull T t) {try {return f.apply(t);} catch (Throwable ex) {throw ExceptionHelper.wrapOrThrow(ex);}
}

f.apply(t)函数:

public interface Function<T, R> {/*** Apply some calculation to the input value and return some other value.* @param t the input value* @return the output value* @throws Exception on error*/R apply(@NonNull T t) throws Exception;
}

这也就是我们前面的map等方法的入参Function。

然后我们继续回到上面查看我们的onObservableAssembly是不是为空,我们发现只有在一个地方给onObservableAssembly赋值了:

public static void setOnObservableAssembly(@Nullable Function<? super Observable, ? extends Observable> onObservableAssembly) {if (lockdown) {throw new IllegalStateException("Plugins can't be changed anymore");}RxJavaPlugins.onObservableAssembly = onObservableAssembly;
}

所以说,如果我们不手动调用RxJavaPlugins.setOnObservableAssembly,onObservableAssembly就一定是空的。

@NonNull
public static <T> Observable<T> onAssembly(@NonNull Observable<T> source) {//不主动赋值,这段代码一定不会执行  ------------   Function<? super Observable, ? extends Observable> f = onObservableAssembly;if (f != null) {return apply(f, source);}
//----------------------------return source;
}

然后我们看一下map的源码:

@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) {ObjectHelper.requireNonNull(mapper, "mapper is null");return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper));
}

显然和上面create是一样的原理。

所以,如果我们手动的设置了onObservableAssembly的值,在调用map的时候,就会走到apply方法里面,并且把上一步的pbservable传递过来:

RxJavaPlugins.setOnObservableAssembly(new Function<Observable, Observable>() {@Overridepublic Observable apply(Observable observable) throws Exception {Log.d(TAG, "走到了apply方法:" + observable);return observable; }
});

这样就实现了在调用create或者map等操作符之前,实现hook,先调用自己的方法

二,RXJava的观察者模式

RxJava的使用步骤,三个最主要的流程:

1,创建Observable 被观察者

2,创建Observer 观察者

3,使用subcribe订阅

下面我们先来实现一下基本的使用流程:

//创建Observable 被观察者
Observable.create(// 自定义sourcenew ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> emitter) throws Exception {emitter.onNext("yuanzhen");}
})
//subscribe订阅
.subscribe(//创建观察者new Observer<String>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(String s) {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onComplete() {}});

首先,我们先看一下最简单的创建观察者:

new Observer<String>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(String s) {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onComplete() {}
});

Observer的全部源码如下:

public interface Observer<T> {/*** Provides the Observer with the means of cancelling (disposing) the* connection (channel) with the Observable in both* synchronous (from within {@link #onNext(Object)}) and asynchronous manner.* @param d the Disposable instance whose {@link Disposable#dispose()} can* be called anytime to cancel the connection* @since 2.0*/void onSubscribe(@NonNull Disposable d);/*** Provides the Observer with a new item to observe.* <p>* The {@link Observable} may call this method 0 or more times.* <p>* The {@code Observable} will not call this method again after it calls either {@link #onComplete} or* {@link #onError}.** @param t*          the item emitted by the Observable*/void onNext(@NonNull T t);/*** Notifies the Observer that the {@link Observable} has experienced an error condition.* <p>* If the {@link Observable} calls this method, it will not thereafter call {@link #onNext} or* {@link #onComplete}.** @param e*          the exception encountered by the Observable*/void onError(@NonNull Throwable e);/*** Notifies the Observer that the {@link Observable} has finished sending push-based notifications.* <p>* The {@link Observable} will not call this method if it calls {@link #onError}.*/void onComplete();}

可以看到,Observer使用了泛型,传入了什么,onNext的参数就是什么类型。

有四个函数:

第一个函数 onSubscribe:一订阅马上调用这个函数

第二个函数onNext:拿到上一个操作符留下来的数据

第三个函数onError:拿到上一个操作符留下来的错误数据

第四个函数onComplete:事件结束

创建观察者,其实就是new了一个接口,让用户自己实现方法,这个非常简单。

下面我们看一下Observable的创建过程:

Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> emitter) throws Exception {emitter.onNext("袁震");}
})

我们还是看一下create方法:

@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public static <T> Observable<T> create(ObservableOnSubscribe<T> source) {ObjectHelper.requireNonNull(source, "source is null");return RxJavaPlugins.onAssembly(new ObservableCreate<T>(source));
}

前面我们已经分析过RxJavaPlugins.onAssembly方法,但是参数我们说后面分析。

接下来,我们就来分析一下这个参数:

首先source我们必须搞清楚是什么。他就是上面我们自己创建的ObservableOnSubscribe:

new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> emitter) throws Exception {emitter.onNext("袁震");}

然后把source作为参数,传递给了ObservableCreate:

final ObservableOnSubscribe<T> source;
public ObservableCreate(ObservableOnSubscribe<T> source) {this.source = source;
}

到这里,观察者和被观察者都创建完了,接下来就是最关键的订阅流程了:

分析了观察者和被观察者的创建之后,上面的使用流程,实际上我们可以简化为:

被观察者= new ObservableCreate
观察者= new Observer
被观察者.subscribe(观察者)

所以,subscribe方法肯定是在ObservableCreate这个对象或者其父类里面实现的

我们先看subscribe的源码:

它实在ObservableCreate的父类Observable里面实现的

@SchedulerSupport(SchedulerSupport.NONE)
@Override
public final void subscribe(Observer<? super T> observer) {ObjectHelper.requireNonNull(observer, "observer is null");try {observer = RxJavaPlugins.onSubscribe(this, observer);ObjectHelper.requireNonNull(observer, "Plugin returned null Observer");subscribeActual(observer);} catch (NullPointerException e) { // NOPMDthrow e;} catch (Throwable e) {Exceptions.throwIfFatal(e);// can't call onError because no way to know if a Disposable has been set or not// can't call onSubscribe because the call might have set a Subscription alreadyRxJavaPlugins.onError(e);NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");npe.initCause(e);throw npe;}
}
/**

其他代码都是异常判断,可以忽略,主要看这行代码:

subscribeActual(observer);

然后看他的实现:

protected abstract void subscribeActual(Observer<? super T> observer);

发现是一个抽象方法,所以它的实现肯定在其子类ObservableCreate中:

@Override
protected void subscribeActual(Observer<? super T> observer) {CreateEmitter<T> parent = new CreateEmitter<T>(observer);observer.onSubscribe(parent);try {source.subscribe(parent);} catch (Throwable ex) {Exceptions.throwIfFatal(ex);parent.onError(ex);}
}

这里非常重要,首先他将我们自己创建的观察者传递了进来,然后:

CreateEmitter<T> parent = new CreateEmitter<T>(observer);

他创建了一个CreateEmitter,并且将观察者传递了进去,我们看看CreateEmitter这个类:

    static final class CreateEmitter<T>extends AtomicReference<Disposable>implements ObservableEmitter<T>, Disposable {private static final long serialVersionUID = -3434801548987643227L;final Observer<? super T> observer;CreateEmitter(Observer<? super T> observer) {this.observer = observer;}@Overridepublic void onNext(T t) {if (t == null) {onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));return;}if (!isDisposed()) {observer.onNext(t);}}@Overridepublic void onError(Throwable t) {if (!tryOnError(t)) {RxJavaPlugins.onError(t);}}@Overridepublic boolean tryOnError(Throwable t) {if (t == null) {t = new NullPointerException("onError called with null. Null values are generally not allowed in 2.x operators and sources.");}if (!isDisposed()) {try {observer.onError(t);} finally {dispose();}return true;}return false;}@Overridepublic void onComplete() {if (!isDisposed()) {try {observer.onComplete();} finally {dispose();}}}@Overridepublic void setDisposable(Disposable d) {DisposableHelper.set(this, d);}@Overridepublic void setCancellable(Cancellable c) {setDisposable(new CancellableDisposable(c));}@Overridepublic ObservableEmitter<T> serialize() {return new SerializedEmitter<T>(this);}@Overridepublic void dispose() {DisposableHelper.dispose(this);}@Overridepublic boolean isDisposed() {return DisposableHelper.isDisposed(get());}}

这里面有我们熟悉的onNext,onError,onComplete等方法,调用这些方法的时候,就会调用我们传递进来的观察者的相应的方法。

然后再分析上面的subscribeActual方法

接下来执行observer.onSubscribe(parent);也就是我们自己创建的观察者的onSubscribe方法。

执行完了之后,就会执行source.subscribe(parent);这个source就是我们自己创建的被观察者。

并把创建的包含观察者的CreateEmitter对象传递到这个方法里面去。

我们在使用时是这样实现的:

Observable.create(// 自定义sourcenew ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> emitter) throws Exception {// CreateEmitter.onNextemitter.onNext("袁震");}
})

这时候就会调用CreateEmitter的onNext方法,然后就会调用到观察者的onNext方法。

或者调用onCpmlete方法。

这样就完成了RxJava的观察者模式。

注意,RXJava中的观察者模式 和传统的观察者模式是不同的,关于传统的观察者模式可以看我的文章:Android设计模式--观察者模式-CSDN博客

区别主要是:在标准的观察者设计模式中,是一个“被观察者”,多个“观察者“,并且需要“被观察者”发出改变通知后,所有的”观察者”才能观察到

RxJava观察者设计模式中,是多个“被观察者”,一个“观察者”,并且需要 起点 和 终点 在 “订阅” 一次后,才发出改变通知,终点(观察者)才能观察到

为什么说是多个被观察者呢?我们看下map源码:

 @CheckReturnValue@SchedulerSupport(SchedulerSupport.NONE)public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) {ObjectHelper.requireNonNull(mapper, "mapper is null");return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper));}

 所以说我们每调用一次map,就会创建一个被观察者,但是最终的观察者只有一个

而且,相比于传统的观察模式,Rxjava在观察者和被观察者之间添加了一个抽象层的发射器,降低了耦合度。所以说其实他更接近于发布订阅模式。

三,Map操作符原理

首先我们要理解map的作用,map其实就是把数据从一种类型转换到了另一种类型。

下面我们先实现一个map,它的作用就是把string类型转换为Bitmap

Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> e) throws Exception {}
}).map(new Function<String, Bitmap>() {@Overridepublic Bitmap apply(String s) throws Exception {return null;}
}).subscribe(new Observer<Bitmap>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(Bitmap bitmap) {}@Overridepublic void onError(Throwable e) {}@Overridepublic void onComplete() {}});

首先,Observable.create上面我们已经分析过了,就是创建了ObservableCreate对象,

然后调用了这个对象里面的map方法,我们看下map的源码:

@CheckReturnValue
@SchedulerSupport(SchedulerSupport.NONE)
public final <R> Observable<R> map(Function<? super T, ? extends R> mapper) {ObjectHelper.requireNonNull(mapper, "mapper is null");return RxJavaPlugins.onAssembly(new ObservableMap<T, R>(this, mapper));
}

这个源码上面我们已经看过很多次了,下面我们主要看下这句代码:

new ObservableMap<T, R>(this, mapper)

它创建了一个ObservableMap对象,并且把ObservableCreate对象和我们自己创建的Function传递进去。

实际上我们使用时的代码就可以理解为这样的:

被观察者 =new ObservableCreate
ObservableMap =被观察者.map
ObservableMap.subscribe(观察者)

所以,当我们执行subscribe时,就走到了ObservableMap里面的subscribeActual方法:

@Override
public void subscribeActual(Observer<? super U> t) {source.subscribe(new MapObserver<T, U>(t, function));
}

这里又用MapObserver将观察者包装了起来:

static final class MapObserver<T, U> extends BasicFuseableObserver<T, U> {final Function<? super T, ? extends U> mapper;MapObserver(Observer<? super U> actual, Function<? super T, ? extends U> mapper) {super(actual);this.mapper = mapper;}@Overridepublic void onNext(T t) {if (done) {return;}if (sourceMode != NONE) {actual.onNext(null);return;}U v;try {v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");} catch (Throwable ex) {fail(ex);return;}actual.onNext(v);}@Overridepublic int requestFusion(int mode) {return transitiveBoundaryFusion(mode);}@Nullable@Overridepublic U poll() throws Exception {T t = qs.poll();return t != null ? ObjectHelper.<U>requireNonNull(mapper.apply(t), "The mapper function returned a null value.") : null;}
}

这里就是每添加一个map,就会用MapObserver包装一层,有多少map就包装多少层。

注意:ObservableMap里面的source,实际上就是我们传递进来的ObservableCreate

所以

source.subscribe(new MapObserver<T, U>(t, function));

实际上就会调用到ObservableCreate的subscribeActual:

@Override
protected void subscribeActual(Observer<? super T> observer) {CreateEmitter<T> parent = new CreateEmitter<T>(observer);observer.onSubscribe(parent);try {source.subscribe(parent);} catch (Throwable ex) {Exceptions.throwIfFatal(ex);parent.onError(ex);}
}

但是这里传递进来的是MapObserver,它包装了观察者

之后又用CreateEmitter将MapObserver包装了起来。

下面的 observer.onSubscribe(parent);我们前面分析过了,就是调用我们自定义的观察者的onSubscribe方法

然后下面的source.subscribe(parent)会调用到我们自定义的source:

Observable.create(// 自定义sourcenew ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> e) throws Exception {e.onNext("袁震");}
})

然后这个发射器ObservableEmitter<String> e实际上就是包装了MapObserver的CreateEmitter

调用e.onNext("袁震") 会走到CreateEmitter的onNext(T t):

@Override
public void onNext(T t) {if (t == null) {onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));return;}if (!isDisposed()) {observer.onNext(t);}
}

这里面的observer实际上是包装了观察者的MapObserver。然后就会走到MapObserver的onNext方法中:

@Override
public void onNext(T t) {if (done) {return;}if (sourceMode != NONE) {actual.onNext(null);return;}U v;try {v = ObjectHelper.requireNonNull(mapper.apply(t), "The mapper function returned a null value.");} catch (Throwable ex) {fail(ex);return;}actual.onNext(v);
}

这里面的actual就是我们传进来的观察者, actual.onNext(v);实际上就调用到了我们自定义的onNext方法中去。

mapper就是map中自定义的Function方法,mapper.apply(t)是一个抽象的方法:

public interface Function<T, R> {/*** Apply some calculation to the input value and return some other value.* @param t the input value* @return the output value* @throws Exception on error*/R apply(@NonNull T t) throws Exception;
}

其具体的实现由我们自己定义:

.map(new Function<String, Bitmap>() {@Overridepublic Bitmap apply(String s) throws Exception {//自己实现return null;}
})

如果有多个map,就是这样一层一层的去包装,然后再一层一层的去拆包。

这样就实现了RxJava链式编程的核心思想。

这个可能比较绕,但是理解了安卓之神的RxJava设计思想,其他的框架看起来也就很easy了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/682455.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Amber-Leedcode-Java - 代码随想录打卡第38 - 39天-动态规划汇总

本质上感觉是一个相加的问题&#xff0c;状态的转换&#xff0c;由前一种状态推至下一种状态509. 斐波那契数 较为简单 746. 使用最小花费爬楼梯 62. 不同路径 一开始写的时候被吓到了&#xff0c;但是发现听完一半之后再写还是比较容易的 对于我而言主要是找到逻辑&#xf…

Linux文件属性与目录等知识详解

在 Linux 系统中&#xff0c;文件和目录是基本的文件系统组成部分。文件系统是用于组织和存储文件的一种结构&#xff0c;而文件和目录则是文件系统的核心元素。以下是对 Linux 文件和目录的详细解释&#xff1a; 1. 文件&#xff08;File&#xff09; 在 Linux 中&#xff0…

Vue3+Ant-Design-Vue:报错Cannot read properties of null (reading ‘isCE‘)

问题描述 在使用Ant-Design-Vue内置的Table表格组件&#xff0c;实现expand展开行功能时&#xff0c;报错&#xff1a;Uncaught TypeError: Cannot read properties of null (reading ‘isCE‘) 。 报错信息图示&#xff1a; 在GitHub上找到如下描述&#xff0c; 解决方案 网上…

【漏洞复现】和为顺IP-COM WiFi未授权下载漏洞

Nx01 产品简介 深圳市和为顺网络技术有限公司是一家聚焦于商用级网络通信设备的研发与应用&#xff0c;为全球中小型企业提供高速、安全、易维护的网络设备产品和解决方案的公司。 Nx02 漏洞描述 深圳市和为顺网络技术有限公司IP-COM WiFi方案解决专家存在任意文件下载漏洞&am…

代码随想录算法训练营29期Day50|LeetCode 70,322,279

文档讲解&#xff1a;爬楼梯&#xff08;进阶&#xff09; 零钱兑换 完全平方数 70.爬楼梯&#xff08;进阶&#xff09; 题目链接&#xff1a;https://kamacoder.com/problempage.php?pid1067 思路&#xff1a; 这其实是一个完全背包问题。1阶&#xff0c;2阶&#xff0c…

编曲学习:录音设备的选取,麦克风和声卡,监听耳机

如果要录翻唱歌曲或原创歌曲人声的话&#xff0c;哪款麦克风或声卡比较好&#xff0c;性价比比较高&#xff1f; 可以尝试一下福克斯特声卡&#xff0c;和舒尔sm58动圈麦。之前问了一些大佬&#xff0c;有人用的是莱维特 540s。 我一直有点疑问&#xff0c;便宜声卡与麦克风&…

springsecurity6使用

spring security 中的类 &#xff1a; AuthenticationManager : 实现类&#xff1a;ProviderManager 管理很多的 provider &#xff0c;&#xff0c;&#xff0c; 经常使用的&#xff0c;DaoAuthenticationProvider , 这个要设置一个 UserDetailService , 查找数据库&#xff…

java正则表达式获取json字符串中指定key的value

<仅支持取JSON字符串中, 简单属性值的配置, 即值内容中不包含[]或{}格式的数据> import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.List; import java.util.regex.…

【ASP.NET 6 Web Api 全栈开发实战】--前言

《ASP.NET 6 Web Api 实战》专栏通过一步一步的开发并完善一个记账软件项目&#xff0c;来引导大家学习相关的知识&#xff0c;其中的知识包括但不限于如下内容&#xff1a; Web Api 开发.NET 6 项目微服务架构的搭建身份认证移动端应用开发more。。。 专栏结构 专栏分为单体…

分享88个CSS3特效,总有一款适合您

分享88个CSS3特效&#xff0c;总有一款适合您 88个CSS3特效下载链接&#xff1a;https://pan.baidu.com/s/1pDAyFESnO8HSnCZj4-DOzQ?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集整理更不…

STM32CubeMX中外部中断的配置和使用指南

使用STM32CubeMX进行外部中断&#xff08;External Interrupt&#xff09;的配置和使用对于STM32微控制器开发非常重要。外部中断可以让微控制器在外部事件发生时及时作出反应&#xff0c;例如按键触发、传感器信号等。通过STM32CubeMX的图形化界面&#xff0c;开发人员可以轻松…

第13章 网络 Page724 asio定时器

程序代码&#xff1a; 11行&#xff0c;声明一个ios对象 13行&#xff0c;使用ios对象作为参数声明一个定时器&#xff0c;此时&#xff0c;定时器和ios完成了关联&#xff0c;后面定时器如果有任务的话&#xff0c;就可以将任务交给ios 16行&#xff0c;为定时器设置一个定…

力扣-125. 验证回文串

文章目录 力扣题目代码 力扣题目 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s&#xff0c;如果它是 回文串 &#xff0c;…

【日常聊聊】新年新征程:迎接学习的挑战

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 结语 我的其他博客 前言 随着新的一年的到来&#xff0c;程序员们站在了全新的起点。这是一个充满机遇和挑战的时刻&#xff0…

【JavaEE】_HTTP请求与响应

目录 1. HTTP协议 1.1 HTTP简介 1.2 Fiddler 2. HTTP请求 2.1 首行 2.2 请求头&#xff08;header&#xff09; 2.3 空行 2.4 正文&#xff08;body&#xff09; 3. HTTP响应 3.1 首行 3.2 响应头&#xff08;header&#xff09; 3.3 空行 3.4 正文&#xff08;bo…

13.let、const、var的区别

&#xff08;1&#xff09;块级作用域&#xff1a; 块作用域由 { }包括&#xff0c;let和const具有块级作用域&#xff0c;var不存在块级作用域。块级作用域解决了ES5中的两个问题&#xff1a; 内层变量可能覆盖外层变量用来计数的循环变量泄露为全局变量 &#xff08;2&…

51单片机编程基础(C语言):LED点阵屏

点阵屏介绍 类似于数码管&#xff0c;要用到肉眼视觉效应。扫描&#xff0c;才能把每一个LED都能选中&#xff0c;从而显示我们想要的图形&#xff0c;否则&#xff0c; 只能一次点亮一个LED&#xff0c; LED使用 51单片机点阵屏电路图&#xff1a; 实际连接顺序如下图&#…

Java中的乐观锁和悲观锁

使用场景及用法 悲观锁 总是假设最坏的情况&#xff0c;每次去拿数据的时候都认为别人会修改&#xff0c;所以每次在拿数据的时候都会上锁&#xff0c;这样别人想拿这个数据就会阻塞直到它拿到锁&#xff08;共享资源每次只给一个线程使用&#xff0c;其它线程阻塞&#xff0c;…

爱快使用VPN

文章目录 一、VPN服务器1. 各种VPN比较2. PPTP服务端配置3. 创建登录账号4. 创建端口映射5. 设置动态域名 二、Windows客户端1. 连接配置2. 不能连接 Internet 配置 一、VPN服务器 1. 各种VPN比较 PPTPIPSECOpenVPN简介微软推出的VPN协议&#xff0c;占用资源少更高级的VPN协…

php基础学习之分支结构和循环结构(不细讲,来对比一下和两大常用高级编程语言(C++/Java)的细微区别以便记忆)

分支结构 常见分支结构 编程语言常见分支结构有&#xff1a; if语句if-else语句if-elseif-else语句switch语句 其中&#xff0c;除了if-elseif-else语句外&#xff0c;另外3中分支语句在php中和C/Java是一模一样的&#xff01; 而if-elseif-else的唯一不同点就在&#xff0c;【…