Android笔记(十四):JetPack Compose中附带效应(一)

在Android应用中可以通过定义可组合函数来搭建应用界面。应用界面的更新往往是与可组合函数内部定义的状态值相关联的。当界面的状态值发生变更,会导致应用界面进行更新。在Android笔记(九):Compose组件的状态,对Compose组件的状态进行详细地介绍。理想状态下,可组合函数使用是定义范围内的状态值,通过内部状态的变更,修改可组合项构成的界面。但是,在有些特殊场景下,需要在可组合项中运行一些在可组合函数作用域外的一些应用状态。简单地说,附带效应就是在可组合函数中一些可组合函数作用域外的应用状态的变化。这些效应在可组合函数范围外,对于可组合函数来说并不好控制,容易造成过度使用。因此需要结合Effect API来对这些效应进行可预测地控制和处理。

一、什么是附带效应

在前述中对附带效应进行简单说明,在下面需要通过应用实例来解释:
运行下列代码块:

//全局变量,在MainScreen作用域外
val timer = mutableIntStateOf(0)@Composable
fun MainScreen(){//函数作用域内var runningState = remember{mutableStateOf(false)}val scope = rememberCoroutineScope()Box(modifier = Modifier.fillMaxSize(),contentAlignment = Alignment.Center){Column(modifier = Modifier.fillMaxWidth(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center){Text(text = "计时器",fontSize=30.sp,color=MaterialTheme.colorScheme.primary)Text(text = "${timer.value}秒",fontSize = 24.sp)Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center){IconButton(modifier =  Modifier.width(100.dp),onClick={runningState.value = truescope.launch {while(runningState.value){delay(1000)timer.value +=1Log.d("TAG","${timer} m")}}}){Row{Icon(imageVector = Icons.Filled.Start,tint = Color.Green,contentDescription = null)Text("计时")}}IconButton(modifier = Modifier.width(100.dp),onClick={runningState.value  = false}){Row{Icon(imageVector = Icons.Filled.Stop,tint = Color.Green,contentDescription = null)Text("计时")}}}}}
}

在上述代码中,有几点需要注意:

1.rememberCoroutineScope函数
rememberCoroutineScope函数可以获取感知作用域,在可组合项外启动协程。在上述代码中定义的IconButton中的点击事件onClick的,处理timerState值的变化。因为下列的代码段:

while(runningState.value){
delay(1000)
timer.value +=1
Log.d(“TAG”,"${timer} m")
}

delay函数是一个挂起函数,表示地是在不阻塞线程的情况下将协程延迟给定时间(例如代码中设置的1秒),并在指定时间后恢复。因此需要在协程的范围中调用delay这个挂起函数,因此利用rememberCoroutineScope函数获得一个CorountineScope协程范围,在该范围可以对挂起函数进行调用。

val scope = rememberCoroutineScope()
scope.launch{
while(runningState.value){
delay(1000)
timer.value +=1
Log.d(“TAG”,"${timer} m")
}
}

2.关于外部量的变化
定义了状态值timerState和MainScreen()这个可组合函数。timerState这个可变的状态值是一个全局量,在MainScreen()函数的作用域外。需要将timerState的值进行修改,会发生在两种情况:

(1)MainScreen()可组合函数调用时;
(2)MainScreen()函数进行重组时会调用timerState

而在第二种情况中,可组合函数重组时所触发timerState状态值的变化而变化的情况就是发生了附带效应。但是,上述代码定义的timerState值的变化是不可预知的,因为它的作用域在MainScreen函数外,外界也有可能存在促使timerState值变化的情况。因此需要一些方法对timerState值的变更情况做出预测并做出相应的处理。
在这里插入图片描述
图1 附带效应示意

二、LaunchedEffect

LaunchedEffect函数可以在某个可组合项的作用域内运行挂起函数时,它会启动内部的代码块到协程上下文CoroutineContext中。当函数的key1的值发生变化,会重构LaunchedEffect。这时,LaunchedEffect原来启动的协程会被取消然后又重新启动。当LaunchedEffect退出组合项时,协程会被取消。LaunchedEffect可组合函数定义如下:

@Composable @NonRestartableComposable
@OptIn(InternalComposeApi::class) fun LaunchedEffect(
key1: Any?,
block: suspend CoroutineScope.() -> Unit ) {
val applyContext = currentComposer.applyCoroutineContext
remember(key1) { LaunchedEffectImpl(applyContext, block)
}
}

其中,LaunchedEffect函数中的参数:

key1:表示关键字可以是任何类型,如果是可变的状态值时,可以根据可变状态值的变化,取消原有协程并启动新的协程。 当key1为Unit或true时,LaunchedEffect函数将与当前重组函数保持一致的生命周期。
block:表示要调用的挂起函数。需要在协程范围中运行;

修改上述计时器的代码段,代码如下所示:

//全局变量,在MainScreen作用域外
val timer = mutableIntStateOf(0)@Composable
fun MainScreen(){//函数作用域内var runningState = remember{mutableStateOf(false)}val scope = rememberCoroutineScope()LaunchedEffect(key1 = timer.value){ //一旦发生修改,不断取消原有协程,创建新的协程,delay(1000)timer.value +=1 //会使得LaunchedEffect不断重构}Box(modifier = Modifier.fillMaxSize(),contentAlignment = Alignment.Center){Column(modifier = Modifier.fillMaxWidth(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center){Text(text = "计时器",fontSize=30.sp,color=MaterialTheme.colorScheme.primary)Text(text = "${timer.value}秒",fontSize = 24.sp)Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center){IconButton(modifier =  Modifier.width(100.dp),onClick={runningState.value = truetimer.value +=1 //点击按钮控制第一次值的修改}){Row{Icon(imageVector = Icons.Filled.Start,tint = Color.Green,contentDescription = null)Text("计时")}}IconButton(modifier = Modifier.width(100.dp),onClick={runningState.value  = falsetimer.value +=1}){Row{Icon(imageVector = Icons.Filled.Stop,tint = Color.Green,contentDescription = null)Text("停止")}}}}}
}

在上述代码中可以发现,当点击计时按钮时,修改了LaunchedEffect函数中的键参数timer.value的值,使得,LaunchedEffect函数timer.value的值的变化,使得原有协程取消,启动新的协程,并在新的协程中修改了timer.value的值,LaunchedEffect函数的不断重构,使得状态值不断修改,导致计时界面MainScreen不断重构和刷新。通过这样的方式,达到对timer.value修改的可预测控制。但是这种不断切换协程的方式并不可取,可以修改代码成如下的形式:

//全局变量,在MainScreen作用域外
val timer = mutableIntStateOf(0)@Composable
fun MainScreen(){//函数作用域内var runningState = remember{mutableStateOf(false)}val scope = rememberCoroutineScope()LaunchedEffect(key1 = runningState.value){while(runningState.value){delay(1000)timer.value +=1Log.d("TAG","${timer} m")}}Box(modifier = Modifier.fillMaxSize(),contentAlignment = Alignment.Center){Column(modifier = Modifier.fillMaxWidth(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center){Text(text = "计时器",fontSize=30.sp,color=MaterialTheme.colorScheme.primary)Text(text = "${timer.value}秒",fontSize = 24.sp)Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center){IconButton(modifier =  Modifier.width(100.dp),onClick={runningState.value = true}){Row{Icon(imageVector = Icons.Filled.Start,tint = Color.Green,contentDescription = null)Text("计时")}}IconButton(modifier = Modifier.width(100.dp),onClick={runningState.value  = false}){Row{Icon(imageVector = Icons.Filled.Stop,tint = Color.Green,contentDescription = null)Text("停止")}}}}}
}

运行效果是:
在这里插入图片描述
在上述代码段中,按钮的点击动作只是修改了可组合函数内部的runningState状态值。但是,

LaunchedEffect(key1 = runningState.value){
scope.launch {
while(runningState.value){
delay(1000)
timer.value +=1
Log.d(“TAG”,"${timer} m")
}
}
}

如果点击按钮,会修改键参数runingState.value的值。而在上述代码的LaunchedEffect函数中监控到runningState.value值的变化会将原来的协程取消,然后再重新启动新的协程,在新的协程中修改MainScreen可组合函数外部的timerState状态值,从而达到计时的功能实现。在这里,是通过控制runningState来达到修改外部timerState的目的。如果runningState.value值没有发生任何变化,那么原有的协程不会取消,会继续运行。如果runningState.value的值为false,那么界面将不会发生变化。通过这样的方式,LaunchedEffect监控key1的变化,使得对timerState.value修改可以控制。

三、rememberUpdatedState

在上述代码中,LaunchedEffect函数会根据关键字的值的变化,重启协程。但是,在某些情况下,并不希望LaunchedEffect重启,但是却需要LaunchedEffect函数中变更的状态的值。因此,可以考虑使用rememberUpdatedState函数用于创建可捕获和更新该值的引用。但是注意这样的处理方式代价会比较高昂。

//全局变量,在MainScreen作用域外
val timer = mutableIntStateOf(0)@Composable
fun MainScreen(){//函数作用域内var runningState = remember{mutableStateOf(true)}val scope = rememberCoroutineScope()val timerState = rememberUpdatedState(newValue = timer)LaunchedEffect(Unit){while(runningState.value){delay(1000)timerState.value.value +=1Log.d("TAG","${timer.value} m")}}Box(modifier = Modifier.fillMaxSize(),contentAlignment = Alignment.Center){Column(modifier = Modifier.fillMaxWidth(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center){Text(text = "计时器",fontSize=30.sp,color=MaterialTheme.colorScheme.primary)Text(text = "${timerState.value.value}秒",fontSize = 24.sp)Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center){IconButton(modifier =  Modifier.width(100.dp),onClick={runningState.value = true}){Row{Icon(imageVector = Icons.Filled.Start,tint = Color.Green,contentDescription = null)Text("计时")}}IconButton(modifier = Modifier.width(100.dp),onClick={runningState.value  = falsetimer.value +=1}){Row{Icon(imageVector = Icons.Filled.Stop,tint = Color.Green,contentDescription = null)Text("停止")}}}}}
}

在上述代码中,MainScreen可组合函数内部定义:

val timerState = rememberUpdatedState(newValue = timer)
而在LaunchedEffect代码部分定义为:
LaunchedEffect(Unit){

        while(runningState.value){delay(1000)timerState.value.value +=1Log.d("TAG","${timer.value} m")}}

LaunchedEffect函数中的键参数为Unit,这表示在MainScreen函数被调用时或重新组合时,才会加载LaunchedEffect函数。并不存在键参数值的变化重新加载协程代码的可能。但是,通过rememberUpdatedState(newValue = timer)函数,一致可以通过timerState.value来获取变化的状态timer.
因此,在上述代码中,由于runningState.value初始值为true,因此一启动MainScreen,就会显示显示动态计时的效果。但是,当点击停止按钮runningState.value的值设置为false,导致代码段停止运行。然后再点击"计时“按钮,可以发现,LaunchedEffect函数并没有重启。

四、DisposableEffect清理效应

对于需要在键发生变化或可组合项退出组合后进行清理的附带效应,可以通过DisposableEffect来实现。DisposableEffect是组合的附效应。DisposableEffect函数定义如下:

@Composable
@NonRestartableComposable
fun DisposableEffect(
key1: Any?,
effect: DisposableEffectScope.() -> DisposableEffectResult ) {
remember(key1) {
DisposableEffectImpl(effect)
}
}
当key1是键参数,可以为任何值。当key1的值发生变化或者DisposableEffect离开可组合项,则必须执行撤销或清除操作。

//全局变量,在MainScreen作用域外
val timer = mutableIntStateOf(0)@Composable
fun MainScreen(){//函数作用域内var runningState = remember{mutableStateOf(true)}val scope = rememberCoroutineScope()LaunchedEffect(Unit){while(runningState.value){delay(1000)timer.value+=1Log.d("TAG","${timer.value} m")}}DisposableEffect(key1 = timer.value){onDispose {timer.value = 0runningState.value = false}}Box(modifier = Modifier.fillMaxSize(),contentAlignment = Alignment.Center){Column(modifier = Modifier.fillMaxWidth(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center){Text(text = "计时器",fontSize=30.sp,color=MaterialTheme.colorScheme.primary)Text(text = "${timer.value}秒",fontSize = 24.sp)Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center){IconButton(modifier =  Modifier.width(100.dp),onClick={runningState.value = true}){Row{Icon(imageVector = Icons.Filled.Start,tint = Color.Green,contentDescription = null)Text("计时")}}IconButton(modifier = Modifier.width(100.dp),onClick={runningState.value  = falsetimer.value +=1}){Row{Icon(imageVector = Icons.Filled.Stop,tint = Color.Green,contentDescription = null)Text("停止")}}}}}
}

上述代码的界面没有任何变化,这是因为下列代码

LaunchedEffect(Unit){
scope.launch {
while(runningState.value){
delay(1000)
timer.value+=1
Log.d(“TAG”,"${timer.value} m")
}
}
}

DisposableEffect(key1 = timer.value){onDispose {timer.value = 0runningState.value = false}
}

启动MainScreen时,timer.value修改为1,但是DisposableEffect发现到timer.value变更,就会执行清除操作,修改timer.value=0和runningState.value=false。在后续中将无法动态显示计时效果。DisposableEffect往往和生命周期进行关联,当键参数为生命周期拥有者对象时,可以利用DisposableEffect对生命周期拥有者的变化执行撤销或清理的工作。
在下面定义两个活动MainActivity和OtherActivity,这两个活动分别为不同的生命周期拥有者,具有不同的生命周期对象。具体定义如下:
(1)MainActivity的定义
首先定义依附MainActivity的界面HomeScreen的定义

@Composable
fun HomeScreen(lifecycleOwner:LifecycleOwner = LocalLifecycleOwner.current,onStart:(MutableState<Int>)->Unit,onStop:(MutableState<Int>)->Unit){val startAction by rememberUpdatedState(newValue = onStart)val stopAction by rememberUpdatedState(newValue = onStop)val context  = LocalContext.currentvar timer = remember{mutableIntStateOf(0)}//以键参数为生命周期拥有者lifecycleOwnerDisposableEffect(key1 = lifecycleOwner){//定义生命周期观察者val observer  = LifecycleEventObserver{_,event->if(event == Lifecycle.Event.ON_RESUME){startAction(timer)}else if(event == Lifecycle.Event.ON_STOP){stopAction(timer)}}//生命周期拥有者lifecycleOwner的生命周期lifecycle加入新的观察者observer,观察者可以观察生命周期的变化lifecycleOwner.lifecycle.addObserver(observer)onDispose {//当离开可组合项执行清理处理Log.d("TAG","清理完毕")timer.value  = 0lifecycleOwner.lifecycle.removeObserver(observer)}}Box(contentAlignment= Alignment.Center,modifier = Modifier.fillMaxSize()){Column{Text(text = "${timer.value}秒",fontSize=30.sp)Button(onClick = {//跳转到其他活动,即修改了生命周期拥有者对象val intent = Intent(context, OtherActivity::class.java)context.startActivity(intent)}){Text("跳转到其他活动")}}}
}

再定义主活动MainActivity

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {Ch06_DemoTheme {// A surface container using the 'background' color from the themeSurface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {HomeScreen(onStart = ::onStartCall, onStop = ::onStopCall )}}}}fun onStartCall(timer: MutableState<Int>){Log.d("TAG","startCall")thread{while(timer.value<100){timer.value++Thread.sleep(1000)}}}fun onStopCall(timer: MutableState<Int>){//start停止Log.d("TAG","stopCall")}
}

(2)OtherActivity的定义

class OtherActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent{Box(contentAlignment = Alignment.Center,modifier = Modifier.wrapContentSize()){Text("OtherActivity",fontSize = 30.sp)}}}
}

运行情况说明:
(1)点击运行的效果如下图所示:
在这里插入图片描述
(2)当点击”跳转其他活动“按钮,跳转到OtherActivity的界面,跟踪的日志如下:
日志显示
在这里插入图片描述

(3)当彻底退出关闭这个应用,则在日志中显示:
在这里插入图片描述
这是因为,在跳转到OtherActivity时,LifecycleOwner虽然已经切换到OtherActivity,但是界面HomeScreen已经在后台,因此并没有执行onDispose的代码块。只有在彻底退出应用,彻底离开可组合函数HomeScreen,可组合函数HomeScreen中的DisposableEffect中监测到代码生命周期拥有者的对象发生了变化,因此执行onDispose代码块。

五、SideEffect

可组合函数进行重组时并不是每次都成功,如果出现界面状态值发生变化,但是界面会进行重组。但是,在重组的过程中,一些状态数据又发生了变化,导致上次重组没有完全完成。这就使得一些与界面重组无关的数据和代码也会被多次调用,这种情况显然是没有必要的。在这样的前提下,可以使用SideEfffect。

SideEffect表示“副作用”,它将 Compose 的状态发布为非 Compose 代码。如需与非 Compose 管理的对象共享 Compose 状态,可使用 SideEffect 中可组合项,因为只有每次成功重组时才会调用该可组合项

SideEffect函数定义如下:

@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun SideEffect(effect: () -> Unit ) {
currentComposer.recordSideEffect(effect)
}

观察下列代码运行结果和日志:

@Preview
@Composable
fun SideScreen(){//可组合的状态var timer by remember{ mutableIntStateOf(0) }var running by remember{mutableStateOf(false)}Log.d("TAG","在SideEffect前的日志")SideEffect{Log.d("TAG","执行SideEffect函数:running:${running}")if(running) {timer++Thread.sleep(1000)}}Log.d("TAG","在SideEffect函数后的日志")Box(contentAlignment= Alignment.Center,modifier = Modifier.fillMaxSize()){Column{Text(text = "${timer}秒",fontSize=30.sp)Row(horizontalArrangement = Arrangement.Center){Button(onClick = {running  = trueLog.d("TAG","running:${running}")}){Text("计时")}Button(onClick = {running = falsetimer = 0Log.d("TAG","running:${running}")}){Text("停止")}}}}
}

上述代码的运行结果如下所示:
在这里插入图片描述
并没有发生动态更新计时时间的显示。这是因为,running的值为false,在条件判断时,使得timer状态值无法发生变化,状态值没有变化,使得界面只会在调用时刷新界面。即使点击”计时“按钮,修改了running的值为true,因为影响界面重组的状态值timer并没有变化,因此界面没有发生重组。这与日志显示是一致的。下图显示的日志就是这样的效果。
在这里插入图片描述
观察日志,可以发现调用可组合函数SideScreen时,重组成功后,才会调用SideEffect函数。因为日志输出SideEffect函数内部的Lambda代码段中的日志是最后调用的。
再来观察下列代码
(1)定义数据类Timer

/*** counter:Int记录已经计时的时间* timerInterval:Int时间间隔* /
data class Timer(var counter:Int,val timeInterval:Int=1000)

(2)定义将Compose状态返回非Compose的值

@Composable
fun rememberTimer(counter:Int):Timer{//定义可组合的对象val timer = remember{Timer(0)}SideEffect{timer.counter = counter}return timer
}

在上述的可组合函数rememberTimer中生成并返回了一个非Compose的Timer对象

(3)在界面中进行测试

@Preview
@Composable
fun SideScreen(){//非状态的对象var timer = rememberTimer(counter = 0)val scope = rememberCoroutineScope()var running by remember{mutableStateOf(true)}Box(contentAlignment= Alignment.Center,modifier = Modifier.fillMaxSize()){Column{Text(text = "${timer.counter}秒",fontSize=30.sp)Row(horizontalArrangement = Arrangement.Center){Button(onClick = {running  = truescope.launch {while(running){delay(1000)timer.counter++Log.d("TAG",""+timer.counter)}}Log.d("TAG","running:${running}")}){Text("计时")}Button(onClick = {running = falsetimer.counter = 0Log.d("TAG","running:${running}")}){Text("停止")}}}}
}

运行效果如上图一致。
执行点击”计时“按钮,即使running的值为true,界面不会重组。这是因为影响界面的timer已经是一个非Compose的对象,并不能对界面的重组起到作用。观察日志,可以发现timer.counter值的确每秒进行变更了。
在这里插入图片描述

参考文献

1.Compose 中的附带效应
https://developer.android.google.cn/jetpack/compose/side-effects?hl=zh-cn

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

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

相关文章

【新手解答2】深入探索 C 语言:一些常见概念的解析

C语言的相关问题解答 写在最前面问题1变量名是否有可能与变量重名&#xff1f;变量名和变量的关系变量名与变量是否会"重名"举例说明结论 变量则是一个地址不变&#xff0c;值时刻在变的“具体数字”变量的地址和值变量名与数据类型具体化示例结论 问题2关于你给我的…

priority_queue模拟实现

目录 仿函数 模拟实现 结果 大根堆 小根堆 完整代码 priority_queue.h test.c 仿函数 仿函数的通俗定义&#xff1a;仿函数&#xff08;functor&#xff09;又称为函数对象&#xff08;function object&#xff09;是一个能行使函数功能 的类。仿函数的语法几乎和我们…

ubuntu修改系统语言

修改ubuntu系统语言 操作指令修改系统设置总结 操作 ubuntu系统自带的英文环境&#xff0c;个人觉得用起来不方便。改掉吧。换成中文 指令修改 参考了一些博客的解决方式 ctrlartT 打开终端。 sudo apt-get install language-pack-zh-hans 输入下载汉化包的指令。 但是&…

MATLAB的rvctools工具箱熟悉运动学【机械臂机器人示例】

1、rvctools下载安装 rvctools下载地址&#xff1a;rvctools下载 截图如下&#xff0c;点击红色箭头指示的“Download Shared Folder” 即可下载 下载之后进行解压&#xff0c;解压到D:\MATLAB\toolbox这个工具箱目录&#xff0c;这个安装路径根据自己的情况来选择&#xff0c…

jquery学习的文档

jquery文档 $(document.body).css( "background", "black" ); $(myForm.elements).hide()$("<div>", {class: "test",text: "Click me!",click: function(){$(this).toggleClass("test");} }).appendTo(&q…

OSG编程指南<三>:利用Cmake+VS2019+OSG3.6.5编译osgVerse

目前osgearth的ralease版本和debugx64都已经编译过了并且自测可用&#xff0c;放到资源里osgearth下载&#xff0c;供需要的朋友下载参考。环境配置&#xff1a;win10VS2017OSG3.6.4OSGEarth2.10.2&#xff0c; 1、配置OSG环境 编译好osg&#xff0c;然后在系统变量中配置如下…

ORACLE 多实例监听的配置

多实例监听的配置 三种情况&#xff1a; 1、一个监听在一个IP一个端口上监听多个实例 2、一个监听在一个IP不同端口上监听多个实例 3、多个监听监听多个实例 情况1&#xff1a; listener.ora文件&#xff1a; # listener.ora Network Configuration File: E:\app\…

risc-v异常处理

异常与中断的关系 中断和异常都为异常&#xff0c;分为&#xff1a; 同步异常&#xff1a;IO&#xff0c;illegal, page fault&#xff0c;miss_aligh 等来自处理器内部的发生的异常。异步异常&#xff08;中断&#xff09;&#xff1a;鼠标&#xff0c;键盘&#xff0c;Time…

javascript 运算符

javascript 运算符 目录 javascript 运算符 一、算术运算符 1、自增运算符 2、自减运算符 二、比较运算符 三、赋值运算符 四、逻辑运算符 五、条件运算符 疑难解答&#xff1a; 这一节&#xff0c;我们来介绍JavaScript的运算符。运算符是完成一系列操作的符号&…

常用连接池的使用(jdbc)java 连接数据库

C3P0 导入依赖 <!-- https://mvnrepository.com/artifact/c3p0/c3p0 --><dependency><groupId>c3p0</groupId><artifactId>c3p0</artifactId><version>0.9.1.2</version></dependency><!-- https://mvnrepository.c…

Rocket架构

NameServer&#xff1a;作为注册中心&#xff0c;提供路由注册、路由踢出、路由发现功能&#xff0c;舍弃强一致&#xff0c;保证高可用&#xff0c;集群中各个节点不会实时通讯&#xff0c;其中一个节点下线之后&#xff0c;会提供另外一个节点保证路由功能。 Rocket mq name…

基于Python获取亚马逊的评论信息的处理

文章目录 一、分析亚马逊的评论请求二、获取亚马逊评论的内容三、亚马逊评论信息的处理四、代码整合4.1 代理设置4.2 while循环翻页 总结关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具…

力扣hot100 和为 K 的子数组 前缀和

&#x1f468;‍&#x1f3eb; 题目地址 &#x1f37b; AC code class Solution {public int subarraySum(int[] nums, int k){int ans 0;int n nums.length;int[] s new int[n 1];// 前缀和s[0] 0;s[1] nums[0];for (int i 2; i < n; i)s[i] s[i - 1] nums[i - 1…

函数重载记录

重载 函数重载&#xff08;Function Overloading&#xff09;是指在同一作用域内&#xff0c;可以定义多个名称相同但参数列表&#xff08;参数类型、参数个数或参数顺序&#xff09;不同的函数。编译器会根据调用时提供的参数类型和个数来判断应该调用哪个函数。 条件 函数…

C#-基础及扩展合集(持续更新)

一、基础 Ⅰ 关键字 1、record record&#xff08;记录&#xff09;&#xff0c;编译器会在后台创建一个类。支持类似于结构的值定义&#xff0c;但被实现为一个类&#xff0c;方便创建不可变类型&#xff0c;成员在初始化后不能再被改变 &#xff08;C#9新增&#xff09; …

七牛云1024创建节-赛后有感

距离比赛结束已经过去快半个月时间&#xff0c;七牛云又发起了有奖征文的活动&#xff0c;正好借此机会记录一下自己参加这次比赛的经历&#xff0c;感受和一些比赛的心得。 如何了解到的比赛信息 其实我很早就开始关注七牛云了&#xff0c;最早是在今年二三月的时候&#xf…

JavaScript的输入和输出介绍与说明

JavaScript的输入和输出介绍与说明 概述 JavaScript 作为一种编程语言&#xff0c;其核心规范&#xff08;ECMAScript&#xff09;不直接提供输入和输出功能&#xff0c;或者说其核心规范&#xff08;ECMAScript部分&#xff09;不包含处理输入和输出的内置机制&#xff0c;在…

PostgreSQL + SQL Server = WiltonDB

WiltonDB 是一个基于 PostgreSQL 的开源数据库&#xff0c;通过 Babelfish 插件支持 Microsoft SQL Server 协议以及 T-SQL 语句。 Babelfish 是亚马逊提供的一个开源项目&#xff0c;使得 PostgreSQL 数据库同时具有 Microsoft SQL Server 数据查询和处理的能力。Babelfish 可…

如何提高学生的课堂参与度

提高学生的课堂参与度是让许多教师头疼的问题。不过通过采取一些简单的方法&#xff0c;就可以让学生更积极的参与课堂活动。那么&#xff0c;这些方法是什么呢&#xff1f; 激发学生的兴趣 学生如果对课堂内容感兴趣&#xff0c;他们就会更加积极地参与其中。教师可以通过使用…

详解Java中的泛型(泛型的语法,擦除机制,泛型的上界)

目录 一.什么是泛型 二.Java中为什么要使用泛型 三.泛型的语法 四.泛型类的使用 五.泛型的编译机制&#xff08;擦除机制&#xff09; 六.泛型的上界 一.什么是泛型 泛型&#xff08;Generics&#xff09;是Java SE 5中引入的一个新特性&#xff0c;可以使Java中的类和方…