当前位置: 首页 > news >正文

【Kotlin】类与接口

文章目录

    • 类的定义
      • 创建类的实例
      • 构造函数
        • 主构造函数
        • 次构造函数
        • init语句块
    • 数据类的定义
      • 数据类定义了componentN方法
    • 继承
      • Any
        • Any:非空类型的根类型
        • Any?:所有类型的根类型
    • 覆盖
          • 方法覆盖
          • 属性覆盖
    • 抽象类
    • 接口:使用interface关键字
    • 函数:fun
      • Unit:让函数调用皆为表达式
      • 表达式函数体
      • 类头格式化

类的定义

类可以包含:

  • 构造函数和初始化块
  • 函数
  • 属性
  • 嵌套类和内部类
  • 对象声明

你可以将类想象成一个对象的模板,因为它告诉编译器如何创建该特定类的对象。它还将告诉编译器每个对象应该具有哪些属性,并且从该类生成的每个对象都可以
拥有自己独有的属性值。例如,每个Dog对象都有自己的名称、重量和品种属性,每个Dog的属性值都可以是不同的。

class Dog(val name: String, var weight: Int, val breed: String){fun woo() {}
}

如果有参数的话你只需要在类名后面写上它的参数,如果这个类没有任何内容可以省略大括号:

class Dog(val name: String, var weight: Int, val breed: String)

创建类的实例

val myDog = Dog("Fido", 70, "Mixed" )

上面的类有一个默认的构造函数。

注意:创建类的实例不用new

kotlin_class_dog_sample

构造函数

Kotlin中的一个类可以有一个主构造函数和一个或多个次构造函数。

主构造函数

主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后:

class Person constructor(name: String, surname: String) {
}

如果主构造函数没有任何注解或者可见性修饰符,可以省略constructor关键字:

class Person(name: String, surname: String) {
}

主构造函数不能包含任何的代码。初始化的代码可以放到以init关键字作为前缀的初始化块中:

class Person constructor(name: String, surname: String) {init {print("init")}
}

如果构造函数有注解或可见性修饰符,那么constructor关键字是必需的,并且这些修饰符在它前面

次构造函数

类也可以声明前缀有constructor的次构造函数:

class Person{constructor(name: String) {print("name is $name")}
}

如果类有一个主构造函数,每个次构造函数都需要委托给主构造函数(不然会报错), 可以直接委托或者通过别的次构造函数间接委托。
委托到同一个类的另一个构造函数用this关键字即可:

class Person constructor(name: String) {constructor(name: String, surName: String) : this(name) {print( "name is : $name surName is : $surName")}
}
init语句块

Kotlin引入了一种叫作init语句块的语法,它属于上述构造方法的一部分,两者在表现形式上却是分离的。构造方法在类的外部,它只能对参数进行赋值。
如果我们需要在初始化时进行其他的额外操作,那么我们就可以使用init语句块来执行。比如:

class Bird(weight: Double, age: Int, color: String) {init {println("the weight is ${weight}")}
}

当没有val或者var的时候,构造函数的参数可以在init语句块被直接调用。除此之外,不能在其他地方使用。以下是一个错误的用法:

class Bird(weight: Double, age: Int, color: String) {fun printWeight() {print(weight) // Unresolved reference: weight}
}

事实上,我们的构造方法还可以拥有多个init,他们会在对象被创建时按照类中从上到下的顺序先后执行。例如:

class Bird(weight: Double, aget: Int, color: String) {val weight: Doubleval age: Intval color: Stringinit {this.weight = weightthis.age = age}init {this.color = color}
}

可以发现,多个init语句块有利于进一步对初始化的操作进行职能分离,这在复杂的业务开发中显得特别有用。

数据类的定义

数据类通常需要重写equals()hashCode()toString()这几个方法.
但是在Kotlin中你只需要一行代码。

数据类是一种非常强大的类:

使用Kotlin:

data class Artist(var id: Long,var name: String,var url: String,var mbid: String)

数据类自动覆盖它们的equals方法以改变操作符的行为,由此通过检查对象的每个属性值来判断是否相等。
例如,假设你创建了两个属性值完全相同的Artist对象,使用操作符对它们进行比较将返回true,因为它们存放了相同的数据:除了提供从Any父类继承的equals方法的新实现,数据类还覆盖了hashCode和toString方法。

通过数据类,会自动提供以下函数:

  • 所有属性的get() set()方法
  • equals()
  • hashCode()
  • copy()
  • toString()
  • componentN()

如果我们使用不可修改的对象,就像我们之前讲过的,假如我们需要修改这个对象状态,必须要创建一个新的或者多个属性被修改的实例。
这个任务是非常重复且不简洁的。

举个例子,如果要修改Person类中xoliuage:

data class Person(val name: String,val age: Int)
val p1 = Person("xoliu", 19)
val p2 = p1.copy(age = 22)

如上,我们拷贝了对象然后只修改了age的属性而没有修改这个对象的其它状态。

如果你要在Kotlin声明一个数据类,必须满足以下几点条件:

  • 数据类必须拥有一个构造方法,该方法至少包含一个参数,一个没有数据的数据类是没有任何用处的。
  • 与普通的类不同,数据类构造方法的参数强制使用var或者val进行声明
  • data class之前不能用abstract、open、sealed或者inner进行修饰

与任何其他类一样,你可以向数据类添加属性和方法,只需要将它们包含在类主体中。但是有一个大问题,就是在编译器生成数据类的方法实现时,
比如覆盖equals方法和创建copy方法,它仅包含在主构造函数中定义的属性。因此如果你在数据类主体中定义添加的属性,则它们不会被包含到任何编译器生成的方法中。

数据类定义了componentN方法

定义数据类时,编译器会自动向该类添加一组方法,你可以将其作为访问对象属性值的替代方法。它们被称为componentN方法,其中N表示被访问属性的编号(按声明排序)。多声明,也可以理解为变量映射

继承

Kotlin中所有类都有一个共同的超类Any(java是Object),这对于没有超类型声明的类是默认超类:

class Person // 从 Any 隐式继承

Any不是java.lang.Object。它除了equals()hashCode()toString()外没有任何成员。
在Java中,类默认是可以被继承的,除非你主动加final修饰符。而在Kotlin中恰好相反,默认是不可被继承的,除非你主动加可以继承的修饰符open,如果不加open,那它在转化为Java代码时就是final的:

所以Kotlin中所有的类默认都是不可继承的(final)

所以我们只能继承那些明确声明open或者abstract的类:要声明一个显式的超类型,我们把类型放到类头的冒号之后:

open class Person(num: Int)
// 继承
class SuperPerson(num: Int) : Person(num)

冒号后面的Person(num)会调用Person类的构造函数,以确保所有的初始化代码(例如给属性赋值)能够被执行。
调用父类构造函数是强制性的:如果父类有主构造函数,你必须在子类头中调用它,否则代码将无法通过编译。
请记住,即使你没有在父类中显式地添加构造函数,编译器也会在编译代码的时候自动创建一个空构造函数。
假如我们不想为Person类添加构造函数,因此编译器在编译代码的时候创建了一个空构造函数。该构造函数通过使用Person()被调用。

注意: 上面在说到继承的时候class SuperPerson(num: Int) : Person(num)在父类后面必须加上括号,这是为了能够调用到父类的主构造函数。
Kotlin中规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)
但是如果类没有主构造函数,那么每个次构造函数必须使用super关键字初始化其基类型,或委托给另一个构造函数做到这一点。 这里很特殊,在Kotlin
中是允许类中只有次构造函数,没有主构造函数的。当一个类没有显式的定义主构造函数且定义了次构造函数时,它就是没有主构造函数的。

如果该类有一个主构造函数,其基类必须用基类型的主构造函数参数就地初始化。

如果类没有主构造函数,那么每个次构造函数必须使用super关键字初始化其基类型,或委托给另一个构造函数做到这一点。
注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:

class MyView : View {constructor(ctx: Context) : super(ctx)constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

也就是MyView类的后面没有显式的定义主构造函数,同时又定义了次构造函数。所以现在MyView类是没有主构造函数的。那么既然没有主构造函数,继承View类
的时候也就不需要再在View类后加上括号了。

另外,由于没有主构造函数,次构造函数只能直接调用父类的构造函数,上述代码也是将this关键字换成了super关键字,这部分就很好理解了。

Any

我们都知道,Java并不能在真正意义上被称为一门"纯面向对象"语言,因为它的原始类型(如int)的值与函数等并不能被视作对象。

但是Kotlin不同,在Kotlin的类型系统中,并不区分原始类型(基本数据类型)和包装类型,我们使用的始终是同一个类型。

Any:非空类型的根类型

与Object作为Java类层级结构的顶层类似,Any类型是Kotlin中所有非空类型(如String、Int)的超类,如:

与Java不同的是,Kotlin不区分"原始类型"(primitive type)和其他的类型,他们都是同一类型层级结构的一部分。 如果定义了一个没有指定父类型的类型,
则该类型将是Any的直接子类型。如:

class Animal(val weight: Double)
Any?:所有类型的根类型

如果说Any是所有非空类型的根类型,那么Any?才是所有类型(可空和非空类型)的根类型。这也就是说?Any?是?Any的父类型。

覆盖

方法覆盖

只能重写显示标注可覆盖的方法:

open class Person(num: Int) {open fun changeName(name: String) {}fun changeAge(age: Int) {}
}class SuperPerson(num: Int) : Person(num) {override fun changeName(name: String) {// 通过super关键字调用超类实现super.changeName(name)}
}

SuperPerson.changeName()方法前面必须加上override标注,不然编译器将会报错。如果像上面Person.changeAge()方法没有标注open,则子类中不能定义相同的方法: (不能重写,但能重载)

class SuperPerson(num: Int) : Person(num) {override fun changeName(name: String) {super.changeName(name)}// 编译器报错fun changeAge(age: Int) {}// 重载是可以的fun changeAge(name: String) {}// 重载是可以的fun changeAge(age: Int, name: String) {}
}

标记为override的成员本身是开放的,也就是说,它可以在子类中覆盖。如果你想禁止再次覆盖,可以使用final关键字:

open class SuperPerson(num: Int) : Person(num) {final override fun changeName(name: String) {super.changeName(name)}
}
属性覆盖

属性覆盖与方法覆盖类似,只能覆盖显式标明open的属性,并且要用override开头:

open class Person(num: Int) {open val name: String = ""open fun changeName(name: String) {}fun changeAge(age: Int) {}
}open class SuperPerson(num: Int) : Person(num) {override val name: Stringget() = super.namefinal override fun changeName(name: String) {super.changeName(name)}}

每个声明的属性可以由具有初始化器的属性或者具有get方法的属性覆盖,如果某个属性在父类中被定义为val,你可以在子类中使用var属性覆盖它。
只需要覆盖该属性并将其声明为var即可。请注意,这只适用于这一种方式。如果尝试使用val覆盖var属性,编译器将会报错

抽象类

类和其中的某些成员可以声明为abstract。抽象成员在本类中可以不用实现。需要注意的是,我们并不需要用open标注一个抽象类或者函数——因为这不言而喻,这些属性一定需要去实现的

我们可以用一个抽象成员覆盖一个非抽象的开放成员:

open class Base {open fun f() {}
}abstract class Derived : Base() {override abstract fun f()
}

接口:使用interface关键字

接口可以让你在父类层次结构之外定义共同的行为,接口用于为共同行为定义协议,使你可以不依赖严格的继承结构却又可以利用多态。与抽象类类似,接口不能被实例化且可以定义抽象或具体的方法和属性,但两者有一个关键的不同点:类可以实现多个接口,但是只能继承于一个直接父类。所以接口不仅拥有抽象类的优点,而且使用起来更加灵活。

interface FlyingAnimal {fun fly()
}

虽然Kotlin接口支持属性声明,然而它在Java源码中是通过一个get方法来实现的。在接口的属性并不能像Java接口那样,被直接赋值一个常量。如以下这样是错误的:

interface Flyer {val height = 1000 // error Property initializers are not allowed in interfacesval speed: Int// 可以支持默认实现方法,反编译可以看到是通过静态内部类来提供fly方法的默认实现的,Java8也开始支持了接口方法的默认实现fun fly() {println("I can fly")}
}

Kotlin提供了另外一种方式来实现这种效果:

interface Flyer {val height get() = 1000
}

一个类实现接口时:

class Bird() : Flyer {//  ...
}

接口的后面不用加上括号,因为它没有构造函数可以去调用。

函数:fun

fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)
}

如果你没有指定它的返回值,它就会返回UnitUnitJava中的void类似,但是Unit是一个类型,而void只是一个关键字。Unit可以省略。

fun maxOf(a: Int, b: Int): Int {if (a > b) {return a} else {return b}
}

Unit:让函数调用皆为表达式

如果函数返回Unit类型,该返回类型应该省略:

fun foo() { // 省略了 ": Unit"}

之所以不能说Java中的函数调用皆是表达式,是因为存在特例void。众所周知,在Java中如果声明的函数没有返回值,那么它就需要用void来修饰,如:

void foo() {System.out.println("return nothing")
}

所以foo()就不具有值和类型信息,它就不能算作一个表达式。在Kotlin中,函数在所有的情况下都具有返回类型,所以他们引入了Unit来替代Java中的void关键字。

Unit与Int一样,都是一种类型,然而它不代表任何信息,用面向对象的术语来描述就是一个单例,它的实例只有一个,可写为()。

表达式函数体

如果返回的结果可以使用一个表达式计算出来,你可以不使用括号而是使用等号:

fun add(x: Int,y: Int) : Int = x + y // 省略了{}

Kotlin支持这种单行表达式与等号的语法来定义函数,叫做表达式函数体,作为区分,普通的函数声明则可以叫做代码块函数体。如你所见,在使用表达式函数体
的情况下我们可以不声明返回值类型,这进一步简化了语法。

我们可以给参数指定一个默认值使的它们变的可选,这是非常有帮助的。这里有一个例子,在Activity中创建了一个函数用来Toast一段信息:

fun toast(message: String, length: Int = Toast.LENGTH_SHORT) {Toast.makeText(this, message, length).show()
}

上面代码中第二个参数length指定了一个默认值。这意味着你调用的时候可以传入第二个值或者不传,这样可以避免你需要的重载函数:

toast("Hello")
toast("Hello", Toast.LENGTH_LONG)

类头格式化

有少数几个参数的类可以写成一行:

class Person(id: Int, name: String)

具有较长类头的类应该格式化,以使每个主构造函数参数位于带有缩进的单独一行中。此外,右括号应该另起一行。如果我们使用继承,
那么超类构造函数调用或者实现接口列表应位于与括号相同的行上:

class Person(id: Int, name: String,surname: String
) : Human(id, name) {// ……
}

对于多个接口,应首先放置超类构造函数调用,然后每个接口应位于不同的行中:

class Person(id: Int, name: String,surname: String
) : Human(id, name),KotlinMaker {// ……
}

相关文章:

【Kotlin】类与接口

文章目录 类的定义创建类的实例构造函数主构造函数次构造函数init语句块 数据类的定义数据类定义了componentN方法 继承AnyAny:非空类型的根类型Any?:所有类型的根类型 覆盖方法覆盖属性覆盖 抽象类接口:使用interface关键字函数:funUnit:让…...

Wagtail-基于Python Django的内容管理系统CMS如何实现公网访问

Wagtail-基于Python Django的内容管理系统CMS实现公网访问 文章目录 Wagtail-基于Python Django的内容管理系统CMS实现公网访问前言1. 安装并运行Wagtail1.1 创建并激活虚拟环境 2. 安装cpolar内网穿透工具3. 实现Wagtail公网访问4. 固定的Wagtail公网地址 前言 Wagtail是一个…...

什么是LASSO回归,怎么看懂LASSO回归的结果

随着机器学习的发展,越来越多SCI文章都使用了更多有趣、高效的统计方法来进行分析,LASSO回归就是其中之一。很多小伙伴听说过LASSO,但是对于LASSO是什么,有什么用,怎么才能实现,大家可能一头雾水。今天的文…...

python树长子兄弟链存储结构(孩子兄弟链存储结构)

长子兄弟链存储结构(孩子兄弟链存储结构)解释: 长子兄弟链存储结构是一种树的存储结构,它使用孩子兄弟表示法(也称作左孩子右兄弟表示法)来表示树的结构。这种表示方法主要用于存储一般的树,而不…...

开源和闭源软件对开发的影响

开源软件的优势: 开源性:开源软件允许任何人查看、修改和发布源代码,这促进了代码的共享和集体学习。透明性:开源软件提高了软件的透明度,使用户可以更好地理解软件的工作原理,增加对软件的信任。社区支持…...

centos无法进入系统之原因解决办法集合

前言 可爱的小伙伴们,由于精力有限,暂时整理了两类。如果没有你遇到的问题也没有关系,欢迎底下留言评论或私信,小编看到后第一时间帮助解决 一. Centos 7 LVM xfs文件系统修复 情况1: [sda] Assuming drive cache:…...

【Linux】系统初始化配置

CentOS 7 的虚拟机安装后必须要做的几个操作,记录以下,网络配置修改、yum源安装、基础工具安装: 1、先修改权限,新建普通用户,并授权普通用户apps 的sudo权限; useradd apps password apps visudo apps A…...

使用VC++设计程序对一幅256级灰度图像进行全局固定阈值分割、自适应阈值分割

图像分割–全局固定阈值分割、自适应阈值分割 获取源工程可访问gitee可在此工程的基础上进行学习。 该工程的其他文章: 01- 一元熵值、二维熵值 02- 图像平移变换,图像缩放、图像裁剪、图像对角线镜像以及图像的旋转 03-邻域平均平滑算法、中值滤波算法、…...

【ArcGIS Pro微课1000例】0035:栅格影像拼接(dem高程数据)

本实验讲解在ArcGIS Pro中,栅格数据的两种拼接(镶嵌)方法,适用于遥感影像、DOM、DEM、DSM等常见栅格数据。 文章目录 一、加载实验数据二、栅格拼接工具1. 镶嵌2. 镶嵌至新栅格三、注意事项四、拓展阅读一、加载实验数据 加载配套实验数据中的0035.rar中的两个dem数据,如…...

Zynq-7000系列FPGA使用 Video Processing Subsystem 实现图像缩放,提供工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐FPGA图像处理方案FPGA图像缩放方案自己写的HLS图像缩放方案 3、设计思路详解Video Processing Subsystem 介绍 4、工程代码详解PL 端 FPGA 逻辑设计PS 端 SDK 软件设计 5、工程移植说明vivado版本不一致处理FPGA型号不一致处理其他注意事项…...

【C】内存函数

目录 1. memcpy 使用和模拟实现 2. memmove 使⽤和模拟实现 3. memset 函数的使用 4. memcmp 函数的使用 1. memcpy 使用和模拟实现 void * memcpy ( void * destination, const void * source, size_t num ); • 函数memcpy从source的位置开始向后复制num个字节的数据到d…...

windows系统玩游戏找不到d3dx9_35.dll缺失的解决方法

分享一个我们在打开游戏或许软件过程中遇到的问题——“由于找不到d3dx9_35.dll,无法继续执行代码”的五个修复方案。这个问题可能会影响到我们的工作和娱乐效率,甚至可能导致工作的延期。因此,我希望通过今天的文章,能够帮助大家更好地解决这…...

webshell之内置函数免杀

原始webshell 查杀的点在于Runtime.getRuntime().exec非常明显的特征 利用ProcessBuilder替换Runtime.getRuntime().exec(cmd) Runtime.getRuntime().exec(cmd)其实最终调用的是ProcessBuilder这个函数,因此我们可以直接利用ProcessBuilder来替换Runtime.getRunti…...

react高阶成分(HOC)

使用React函数式组件写了一个身份验证的一个功能,示例通过高阶组件实现的一个效果展示: import React, { useState, useEffect } from react;// 定义一个高阶组件,它接受一个组件作为输入,并返回一个新的包装组件 const withAuth…...

<JavaEE> Thread线程类 和 Thread的常用方法

目录 一、Thread概述 二、构造方法 三、常用方法 1.1 getId()、getName()、getState()、getPririty() 1.2 start() 1.3 isDaemon()、setDaemon() 1.4 isAlive() 1.5 currentThread() 1.6 Interrupt()、interrupted()、isInterrupted() 1.6.1 方法一:添加共…...

Linux加强篇004-Vim编辑器与Shell命令脚本

目录 前言 1. Vim文本编辑器 1.1 编写简单文档 1.2 配置主机名称 1.3 配置网卡信息 1.4 配置软件仓库 2. 编写Shell脚本 2.1 编写简单的脚本 2.2 接收用户的参数 2.3 判断用户的参数 3. 流程控制语句 3.1 if条件测试语句 3.2 for条件循环语句 3.3 while条件循环语…...

【shell脚本】常见的shell脚本面试题目

1、请用shell脚本for,while,until这三种方式写出输出1到100的所有偶数的方法。 sum=0;for((i=0;i<=100;i+=2));do let sum+=i;done;echo $sum sum=0;i=0;while [ $i -le 100 ];do let sum+=i;let i+=2;done;echo $sum sum=0;i=0;until [ $i -gt 100 ];do let sum+=i;let i+…...

Android设计模式--外观模式

弈之为术&#xff0c;在人自悟 一&#xff0c;定义 外观模式要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。提供一个高层次的接口&#xff0c;使得子系统更易于使用。 外观模式在开发中的使用频率是非常高的&#xff0c;尤其是在第三方的SDK里面&#xff0…...

03_MySQL基本SQL语句讲解

#课程目标 能够创建、删除数据表能够对表里的数据记录进行增加、删除、修改、查询操作能够创建、删除用户能够给用户授权并回收权限了解delete和truncate语句的区别 #一、数据库基本操作 ##1、查看数据库相关信息 mysql> show databases; 查看所有数据库 mysql>…...

【C语法学习】28 - 字符测试函数

文章目录 1 isalnum()函数2 isalpha()函数3 islower()函数4 isupper()函数5 isdigit()函数6 isxdigit()函数7 iscntrl()函数8 isgraph()函数9 isspace()函数10 isblank()函数11 isprint()函数12 ispunct()函数13 tolower()函数14 toupper()函数 1 isalnum()函数 isalnum()函数…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...