Android Kotlin实战之高阶使用泛型扩展协程懒加载详解

news2025/1/11 4:29:00

前言:

通过前面几篇文章,我们已基本掌握kotlin的基本写法与使用,但是在开发过程中,以及一些开源的API还是会出现大家模式的高阶玩法以及问题,如何避免,接下来讲解针对原来的文章进行一些扩展,解决大家在工作中遇到的问题,如何去解决

如果还有人不了解kotlin,可以查看我的基础篇kotlin。

Android kotlin在实战过程问题总结与开发技巧详解_kotlin 同步锁_蜗牛、Z的博客-CSDN博客

Kotlin语法详解与实践教程,区分JAVA以及如何闭坑_kotlin mapof可以嵌套 to_蜗牛、Z的博客-CSDN博客

1. lazy

lazy是kotlin中的懒加载,这种写法在很多场景中都有,懒加载并不是立刻在内存中申请,而是通过lazy(),调用才会

lazy的只能是常量,用val修饰,变量是全局也可以是局部变量

通过这个lazy{}可以看出,无论你在最上面调用什么,默认值都是最后一个。通过IDE,可以提示看到ccc ^lazy,这就是懒加载的值。这个值也可以通过方法获取

 

fun a() {
    val aa: String by lazy {
        MyLog.log("val aa:String by lazy start")
        "aaaa"
        "bbbb"
        b()

    }

    MyLog.log(aa)
    MyLog.log(aa)
}


fun b(): String {
    return "aaaaa"
}

一旦懒加载对象被调用过,后期在调用将只能取到值,不会再处理其他的lazy{} 方法体。

 

注意:lazy和lateinit无法一起使用。lazy修饰val变量,lateinit修饰的是var变量。

2.lateinit

也是懒加载机制,对象是var的格式。但是,这个变量需要是全局变量,不能是局部变量。

lateinit var string: String
lateinit var people: People
fun c() {

    people = People()
    people.name = "zhangshan"

    string = "aaaa"
    MyLog.log(people.name!!)

    val child: People by lazy{
        People()
    }
    

}

3.如何避免参数加"!!"可为空符号

kotlin的变量如果不是懒加载修饰,那么你在申请的时候,就要给出变量值,即默认值

常见写法:

var name:String?=null,表示当前对象是null

var title:String="title",表示已默认值、

但是如果你申请为null,那么你在赋值的时候,这个参数就要在多个地方加上"!!",否则会报错。这种写法很烦人。

如何避免?

使用懒加载机制,通过上面两个懒加载,可以避免这种问题的参数。否则你在任何地方进程参数传递都会被判为null。

4.类的扩展函数

kotlin支持在现有的类中,动态扩展函数,格式就是类对象+"."+函数名,可以支持参数和返回值,扩展的函数和正常函数一样使用

以下是对String扩展一个两个方法,分别是log()和size()


fun String.log(log:String) {

    MyLog.log(log)
}

fun String.size(): Int {

    return toString().length
}

5.扩展属性、扩展变量

kotlin支持动态新增变量,格式就类名+"."+变量名+":"+类型

这种写法可以无限的扩展你需要的变量,扩展变量一样,只能在最外围,不能在方法体中定义,

扩展属性需要额外重写get()方法,且不能申请直接赋值

val String.defaulename: String
    get() {
        return "defaulename"
    }


fun e() {
    val name=""
    MyLog.log(name.defaulename)
}

这里需要重写get(),给出默认值。

6.apply与let与also

apply与alse是链式设置,返回的是当前对象,let返回值是unit,

  • apply、also,闭包的返回值都是this,前者apply接受的闭包类型调用者的扩展函数,后者接受的闭包类型为 入参为调用者类型的函数;
  • also、apply,非常适合对同一个对象连续操作的链式调用;
  • run、let,闭包的返回值为最后一行非赋值代码,前者run接受的闭包类型调用者的扩展函数,后者接受的闭包类型为 入参为调用者类型的函数;
  • run、let,非常适合上一个操作返回值作用于下一个操作的调用;

我们经常会使用如下写法:

if(people!=null){

peope.name="zhangshan"

}

apply写法:

people?.apply{it->

it.name="zhangsnan"

}

7.类的初始化模块init

kotlin中没有static{}模块,是通过init{}替代了static。如果需要提前初始化的可以放在init中,常见的so库加载可以放到init中

class Example {
    init {

        //init module
    }
}

8 .接口多继承,方法名冲突

 如果接口中有两个相同的方法名,在java中是需要修改一个的。在kotlin中就可以避免这种。只要在继承方法中,指向各个类的即可。


class TestInterface : IntFacA, IntFacB {

    override fun log() {
        super<IntFacA>.log()
        super<IntFacB>.log()
    }

    override fun info() {

    }
}

9.internal 介绍

internal 属于修饰,和provite、public、protect一样

  • private 意味着只在这个类内部(包含其所有成员)可见;
  • protected—— 和 private一样 + 在子类中可见。
  • internal —— 能见到类声明的 本模块内 的任何客户端都可见其 internal 成员;
  • public —— 能见到类声明的任何客户端都可见其 public 成员。

10.数据类(data)

将类对象修饰成data,需要的变量直接在构造器中申明,不需要方法体

data class User(val name: String = "", val age: Int = 0)

11.密封类(sealed)

密封类用来表示受限的类继承结构:当一个值为有限几种的类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。

要声明一个密封类,需要在类名前面添加 sealed 修饰符。虽然密封类也可以有子类,但是所有子类都必须在与密封类自身相同的文件中声明

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

一个密封类是自身抽象的,它不能直接实例化并可以有抽象(abstract)成员。

密封类不允许有非-private 构造函数(其构造函数默认为 private)。

fun eval(expr: Expr): Double = when(expr) {
    is Const -> expr.number
    is Sum -> eval(expr.e1) + eval(expr.e2)
    NotANumber -> Double.NaN
    // 不再需要 `else` 子句,因为我们已经覆盖了所有的情况
}

类似接口继承,通过泛型来判断

12.泛型,通配符类型参数:in 和 out

在开发过程中,泛型的利用,可以很好的解决代码臃肿问题,也能很好的进行解耦。在kotlin中,泛型的使用有普通的,也有通配符限定

1.正常泛型

interface Factory<T> {


    fun getEntity(): T
}

class Room : Factory<String> {

    override fun getEntity(): String {
        return "String info"
    }
}

这种是我们最常见的泛型,通过泛型,可以很好的获取不同的对象类型

2通配符 out和in

java中也有通配符,? extend E,? extends E 表示此方法接受 E 或者 E 的 一些子类型对象的集合,而不仅仅是 E 自身。简而言之,带 extends 限定(上界)的通配符类型使得类型是协变的。

还有一种,? super String,表示只接收String类型。

在 kotlin 语言中,out 表示协变,in 表示逆变;

kotlin 中的 “out T” 等同于 Java 的 “?extends T”

kotlin 中的 “in T” 等同于 Java 的 “?super T”

也就是上线和下线。

13.嵌套类

内部类可以直接使用,就是通过最外层类引用即可,也可以单独使用

class Student {

    lateinit var name: String

    class Classmate {

        lateinit var name: String

    }
}


fun main() {

    var mate=Student.Classmate()
    mate.name="zhangsan"
    MyLog.log(mate.name)
}

14.内部类:inner 

标记为 inner 的嵌套类能够访问其外部类的成员。内部类会带有一个对外部类的对象的引用。

class Student {

    lateinit var name: String

   inner class Classmate {

        lateinit var name: String

    }
}


fun main() {

    var mate=Student().Classmate()
    mate.name="zhangsan"
    MyLog.log(mate.name)
}

15.匿名内部类:

使用对象表达式创建匿名内部类实例:

window.addMouseListener(object : MouseAdapter() {

    override fun mouseClicked(e: MouseEvent) { …… }

    override fun mouseEntered(e: MouseEvent) { …… }
})

接口的监听:

1.lambda表达式

 

2.object匿名内部类

   view.setOnClickListener(object :View.OnClickListener{
        override fun onClick(v: View?) {

        }
    })

16.枚举类

枚举类的最基本的用法是实现类型安全的枚举,kotlin枚举有一下几种

1.支持匿名类

2.支持参数扩展

enum class MyEmu {
    ONE{
         var size="123"
       },TWO,THREE
}

enum class MyEmu1(tag:Int) {
    ONE(1),TWO(2),THREE(3)
}

如果你想弄那么复杂,就当普通枚举使用

3、枚举类型新增接口

enum class MyEmu :MyInfo{
    ONE{
        override fun log() {
            TODO("Not yet implemented")
        }
    },TWO{
        override fun log() {
            TODO("Not yet implemented")
        }
    }
}
interface MyInfo {

    fun log();
}

如果枚举类型实现了接口,那么枚举中的任何元素的都变成了匿名内部类,都要实现接口的方法

17.open超类

如果一个类无法被继承,就可以将该类通过open修饰,如果类中的变量无法使用,也可以通过open修饰。

open class MyNAME {
    open var name = ""
}

18.静态类:object 

静态类,是通过object修饰,该类里面的所有变量和方法都是静态。且不在需要class修饰

object staticName {
    var name = ""

    fun log() {
        
    }
}

19.伴生对象

伴随对象是在普通类中进行扩展一个对象出来的,该伴生对象的成员可通过只使用类名作为限定符来调用

class BanshengObject {

    companion object {
        var name = ""

        fun log() {

        }
    }
}


fun main() {

    BanshengObject.log()
    BanshengObject.name
}

请注意,即使伴生对象的成员看起来像其他语言的静态成员,在运行时他们仍然是真实对象的实例成员

如果使用 @JvmStatic 注解,你可以将伴生对象的成员生成为真正的静态方法和字段

20.类型别名:typealias

        类型别名为现有类型提供替代名称。 如果类型名称太长,你可以另外引入较短的名称,并使用新的名称替代原类型名。

class Alias {

    inner class MyStudent(name:String)
}

typealias Alias_MyStudent = Alias.MyStudent

typealias SetString=Set<String>
typealias MyHandler = (Int, String, Any)->Void

fun main() {

    var m:MyHandler

   

}

这个别名使用在kotlin有很多,kotlin对java的api进行封装,就是通过别名完成。也可以支持方法的别名

21.内联类

内联类必须含有唯一的一个属性在主构造函数中初始化。在运行时,将使用这个唯一属性来表示内联类的实例

inline class InlinerBean(val title:String) {

    val length: Int
        get() = title.length
}

fun main() {
    val  bean=InlinerBean("this is inline class")
    
}

// 不存在 'InlinerBean' 类的真实实例对象
// 在运行时,'InlinerBean' 仅仅包含 'String' 

 val  bean=InlinerBean("this is inline class")

同时,内联是为了解决额外的堆内存分配问题。

内联类支持普通类中的一些功能。特别是,内联类可以声明属性与函数

内联函数,有且仅有一个构造参数

内联函数也可以当普通函数使用,可以实现接口

22.委托:by

委托模式已经证明是实现继承的一个很好的替代方式, 而 Kotlin 可以零样板代码地原生支持它

interface Base {
    val message: String
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override val message = "BaseImpl: x = $x"
    override fun print() { println(message) }
}

class Derived(b: Base) : Base by b {
    // 在 b 的 `print` 实现中不会访问到这个属性
    override val message = "Message of Derived"
}

fun main() {
    val b = BaseImpl(10)
    val derived = Derived(b)
    derived.print()
    println(derived.message)
}

注意,以这种方式重写的成员不会在委托对象的成员中调用 ,委托对象的成员只能访问其自身对接口成员实现。

23.委托属性

有一些常见的属性类型,虽然我们可以在每次需要的时候手动实现它们, 但是如果能够为大家把他们只实现一次并放入一个库会更好。例如包括:

  • 延迟属性(lazy properties): 其值只在首次访问时计算;
  • 可观察属性(observable properties): 监听器会收到有关此属性变更的通知;
  • 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。

为了涵盖这些(以及其他)情况,Kotlin 支持 委托属性:

class Example {
    var p: String by Delegate()
}

语法是: val/var <属性名>: <类型> by <表达式>。在 by 后面的表达式是该 委托, 因为属性对应的 get()(与 set())会被委托给它的 getValue() 与 setValue() 方法。 属性的委托不必实现任何的接口,但是需要提供一个 getValue() 函数(与 setValue()——对于 var 属性)

class Delegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }
 
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

分析:

by是ReadOnlyProperty的连接,

public fun interface ReadOnlyProperty<in T, out V> {
    /**
     * Returns the value of the property for the given object.
     * @param thisRef the object for which the value is requested.
     * @param property the metadata for the property.
     * @return the property value.
     */
    public operator fun getValue(thisRef: T, property: KProperty<*>): V
}

所有我们在属性委托时,需要实现 public operator fun getValue(thisRef: T, property: KProperty<*>): V

所以定义属性委托必须要实现接口ReadOnlyProperty

实战:

在jetpack的datastore中,也提供了属性委托。

    val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
        "my_datastore",
        produceMigrations = { it ->
            listOf(SharedPreferencesMigration(it, "sp_test"))
        })

24.Lambda 表达式

lambda 表达式与匿名函数是“函数字面值”,即未声明的函数, 但立即做为表达式传递

max(strings, { a, b -> a.length < b.length })

函数 max 是一个高阶函数,它接受一个函数作为第二个参数。 其第二个参数是一个表达式,它本身是一个函数,如下:

fun compare(a: String, b: String): Boolean = a.length < b.length

Lambda 表达式语法

Lambda 表达式的完整语法形式如下:

val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }

lambda 表达式总是括在花括号中, 完整语法形式的参数声明放在花括号内,并有可选的类型标注, 函数体跟在一个 -> 符号之后

val sum = { x: Int, y: Int -> x + y }
    val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
    sum(2,3)

    val add = { x: Int, y: Int -> x + y }
    add(2,3)

从 lambda 表达式中返回一个值

ints.filter {
    val shouldFilter = it > 0 
    shouldFilter
}

ints.filter {
    val shouldFilter = it > 0 
    return@filter shouldFilter
}

25.构造集合

在开发过程中,发现使用集合都需要指定泛型,还有一种可以指定一个默认值,后面所有的集合都是这种

val numbersSet = setOf("one", "two", "three", "four")
val emptySet = mutableSetOf<String>()

26.空集合

还有用于创建没有任何元素的集合的函数:emptyList()、emptySet() 与 emptyMap()。 创建空集合时,应指定集合将包含的元素类型。

27.具体类型构造函数

要创建具体类型的集合,例如 ArrayList 或 LinkedList,可以使用这些类型的构造函数。 类似的构造函数对于 Set 与 Map 的各实现中均有提供

val linkedList = LinkedList<String>(listOf("one", "two", "three"))
val presizedSet = HashSet<Int>(32)

28.集合加减法

我们正常使用逻辑都是对于变量运算,但是在kotlin中的集合也可以使用

 val plusList = numbers + "five"
    val minusList = numbers - listOf("three", "four")
    val minusList2 = numbers - "three"
    println(plusList)
    println(minusList)
    println(minusList2)

29.集合数据分组

Kotlin 标准库提供用于对集合元素进行分组的扩展函数。 基本函数 groupBy() 使用一个 lambda 函数并返回一个 Map。 在此 Map 中,每个键都是 lambda 结果,而对应的值是返回此结果的元素 List。 例如,可以使用此函数将 String 列表按首字母分组。 

 


    val numbers = listOf("one", "two", "three", "four", "five")

    println(numbers.groupBy { it.first().toUpperCase() })

30.协程

Kotlin 是一门仅在标准库中提供最基本底层 API 以便各种其他库能够利用协程的语言。与许多其他具有类似功能的语言不同,async 与 await 在 Kotlin 中并不是关键字,甚至都不是标准库的一部分。此外,Kotlin 的 挂起函数 概念为异步操作提供了比 future 与 promise 更安全、更不易出错的抽象。

kotlinx.coroutines 是由 JetBrains 开发的功能丰富的协程库。它包含本指南中涵盖的很多启用高级协程的原语,包括 launch、 async 等等。


 

在kotlin中,suspend修饰的方法叫着协程。目前针对协程有一下几种

1.同步:runBlocking:

2.异步:kotlinx-coroutines-core库

 代码库:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"

class MySuspendMain {
    suspend fun info() {

    }
}


fun main() {
    GlobalScope.launch { // 在后台启动一个新的协程并继续
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("Hello,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活

    runBlocking {
        val mains=MySuspendMain()
        mains.info()

    }
    

}

由于kotlinx-coroutines-core库很大,我会出一篇详细的文章讲解。

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

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

相关文章

【10K功能测试】-【20K自动化测试】之间的区别

前言 按测试执行的类型来分&#xff1a;功能测试、自动化测试 1&#xff0e;功能测试 功能测试俗称点点点测试。初级测试人员的主要测试任务就是执行测试工程师所写的测试用 例&#xff0c;记录用例的执行状态及bug情况。与开发人员进行交互直到bug被修复。 功能测试理论上是…

顺丰同城测试开发一面 49min答案,全文7000字,面试总结都在这里了

今天给大家分享一份顺丰同城的测试开发一面面试真题。老规矩&#xff0c;当你看到这份面试题的时候&#xff0c;先不要着急去看答案&#xff0c;你可以想想假如你在面试现场&#xff0c;你会怎么回答&#xff1f;这个思考的过程其实也是很重要的。 全文7000字干货&#xff0c;…

ADC0832的AD模数转换原理及编程

✅作者简介&#xff1a;嵌入式领域优质创作者&#xff0c;博客专家 ✨个人主页&#xff1a;咸鱼弟 &#x1f525;系列专栏&#xff1a;单片机设计专栏 &#x1f4c3;推荐一款求职面试、刷题神器&#x1f449;注册免费刷题 目录 一、描述 二、模数转换原理&#xff1a; 三、…

Gorm -- 添加记录

文章目录添加单条记录直接添加模型对象赋予默认值方法一&#xff1a; gorm 标签赋予默认值方法二&#xff1a; 设置钩子方法&#xff08;Hooks&#xff09;指定字段插入插入时忽略某些字段插入时禁止使用钩子方法添加多条记录通过对象列表插入通过字典列表插入在字典中使用SQL内…

【Linux】工具(3)——gcc/g++

咱们继续进阶&#xff0c;接下来进入到Linux工具中gcc和g的学习在本章博客正式开始介绍之前&#xff0c;我们先要弄清楚程序是怎么翻译的&#xff1a;C语言程序环境一、什么是gcc/g&#x1f4cc;gcc是一个c编译器&#xff0c; g是c编译器。我们根据代码的后缀名来判断用哪个编译…

NCRE计算机等级考试Python真题(十)

第十套试题1、数据库系统的核心是___________。A.数据库管理系统B.数据模型C.软件工具D.数据库正确答案&#xff1a; A2、下列叙述中正确的是___________。A.线性表链式存储结构的存储空间可以是连续的&#xff0c;也可以是不连续的B.线性表链式存储结构与顺序存储结构的存储空…

Stable Diffusion原理详解

Stable Diffusion原理详解 最近AI图像生成异常火爆&#xff0c;听说鹅厂都开始用AI图像生成做前期设定了&#xff0c;小厂更是直接用AI替代了原画师的岗位。这一张张丰富细腻、风格各异、以假乱真的AI生成图像&#xff0c;背后离不开Stable Diffusion算法。 Stable Diffusion…

大容量存储(涉及到的硬盘存取的原理)

磁盘的结构 我们简单分析一下&#xff0c;这里是计组的知识&#xff0c;大家可以自己多看些资料 柱面是基本单位 我们可以看到内圈的空间比外圈的要小&#xff0c;但是我们的每个磁道里的扇区是一样的&#xff0c;所以外圈的数据密度分布比内圈要更分散一些&#xff0c;这样就…

LeetCode 235. 二叉搜索树的最近公共祖先

LeetCode 235. 二叉搜索树的最近公共祖先 难度&#xff1a;middle\color{orange}{middle}middle 题目描述 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先…

解决pycharm中报ModuleNotFoundError: No module named ‘tensorflow‘错误

在pycharm中编写python程序时&#xff0c;遇到了报ModuleNotFoundError:no module named XXX的错误。以下是我的解决方法。 目录 一、错误现象 二、原因分析 三、解决方法 四、更多错误解决方法 一、错误现象 执行python程序时&#xff0c;报错&#xff0c;错误信息为Modu…

python字符串练习

python字符串练习 1.去掉字符串中所有的空格 s This is a demo print(s.replace( , )) 2.获取字符串中数字的个数 data input("请输入一些字符串&#xff1a;") a 0 for i in data:if i.isdigit():a a 1 print("数字个数:", a)3.将字母全部转换为…

Cypher中的聚合

深解Cypher中的聚合 值或计数的聚合要么从查询返回&#xff0c;要么用作多步查询下一部分的输入。查看数据模型 CALL db.schema.visualization() 查看图中节点的属性类型 CALL db.schema.notetypeproperties() 查看图中关系的属性类型 CALL db.schema.reltypeproperties() C…

普通二本,去过阿里外包,到现在年薪40W+的高级测试工程师,我的两年转行经历...

我是一个普通二本大学机械专业毕业&#xff0c;14年毕业&#xff0c;16年转行&#xff0c;目前做IT行业的软件测试已经有3年多&#xff0c;职位是高级软件测试工程师&#xff0c;坐标上海…我想现在我也有一点资格谈论关于转行这个话题&#xff1b;希望你在决定转行之前&#x…

爆品分析第4期 | 从周销12件到3700+件,这款收腰裤热度和口碑都爆了!

衣食住行&#xff0c;衣是排在第一位的&#xff0c;作为复购率最高的类目之一&#xff0c;服饰一直是TikTok上电商选品的风向标&#xff0c;是衡量电商发展情况的重要参考指标。随着疫情的结束和经济的日渐好转&#xff0c;消费者对服装类的需求上升。除了时装、T恤等日常消费的…

无损音乐格式:FLAC和ALAC

前言&#xff1a;我最近在弄苹果的airplay项目&#xff0c;发现airplay2对比airplay多了音质方面的增强。AAC和MP3接触过&#xff0c;但对FLAC和ALAC完全不了解&#xff0c;整理学习资料汇总成如下信息&#xff1a; AirPlay2 在2017年推出&#xff0c;在前一代AirPlay的基础上…

一篇文章搞性IPV6的原理和配置

本章对IPV6做了简单的介绍&#xff0c;并通过实验让读者了解IPV6地址和6TO4隧道的配置方法。 本章包含以下内容&#xff1a;  IPV6的概述 配置IPV6地址 配置6to4隧道  10.1 IPv6的概述 IPv6&#xff08;Internet Protocol Version 6&#xff09;是网络层协议…

MyBatis源码分析(一)MyBatis整体架构分析

文章目录一、为什么要用MyBatis1、原始JDBC的痛点2、Hibernate 和 JPA3、MyBatis的特点4、MyBatis整体架构5、MyBatis主要组件及其相互关系6、MyBatis源码的特点二、源码环境搭建未完待续一、为什么要用MyBatis 1、原始JDBC的痛点 在传统JDBC场景下&#xff0c;SQL 夹杂在Jav…

Databend 开源周报第 80 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.com 。Whats New探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。Features & Improvements :…

Unity Asset Bundle学习 - 加载本地资源

Unity的 Asset Bundle 文档 https://docs.unity3d.com/cn/2019.4/Manual/AssetBundles-Workflow.html 第一次接触 直接按官方文档操作 下面接着按文档走流程 构建AssetBundle 此脚本将在 Assets 菜单底部创建一个名为 Build AssetBundles 的菜单项&#xff0c;该菜单项将执行与…

STM32学习笔记-DMA

文章目录一、功能框图通道选择仲裁器FIFO1. **FIFO**: First in First out2. FIFO作用&#xff1a;端口二、DMA模式配置1. 传输模式2. 源地址和目标地址3. 流控制器4. 循环模式5. 传输类型6. 直接模式7. 双缓冲模式8.DMA中断事件三、程序设计1. DMA初始化结构体DMA(Direct Memo…