八、特殊类型异常机制
特殊类型&异常机制
- 数据类型
- 枚举类型
- 匿名类、单例类和伴生对象
- 匿名类
- 单例类
- 伴生对象
- 委托模式
- 密封类型
- 异常机制
- 异常的使用
- 异常的处理
数据类型
对于那些只需要保存数据的类型,我们常常需要为其重写toString
、equals
等函数,针对于这种情况下,Kotlin为我们提供了专门的数据类,数据类不仅能像普通类一样使用,并且自带我们需要的额外成员函数,比如打印到输出、比较实例、复制实例等。
声明一个数据类非常简单:
//在class前面添加data关键字表示为一个数据类
data class User(val name: String, val age: Int)
数据类声明后,编译器会根据主构造函数中声明的所有属性自动为其生成以下函数:
.equals()
/.hashCode()
.toString()
生成的字符串格式类似于"User(name=John, age=42)"
.componentN()
与按声明顺序自动生成用于解构的函数.copy()
用于对对象进行拷贝
我们可以来试试看:
fun main() {val user1 = User("小明", 18)val user2 = User("小明", 18)println(user1) //打印出来就是格式化的字符串 User(name=小明, age=18)println(user1 == user2) //true,因为自动重写了equals函数val (name, age) = user1 //自动添加componentN函数,因此支持解构操作println("名称: $name, 年龄: $age")
}
当然,为了确保生成代码的一致性和有效性,数据类必须满足以下要求:
- 主构造函数中至少有一个参数。
- 主构造函数中的参数必须标记为
val
或var
。 - 数据类不能是抽象的、开放的、密封的或内部的。
此外,数据类的成员属性生成遵循有关成员继承的以下规则:
-
如果数据类主体中
.equals()
.hashCode()
或.toString()
等函数存在显式(手动)实现,或者在父类中有final
实现,则不会自动生成这些函数,并使用现有的实现。data class User(val name: String, val age: Int) {//如果已经存在toString的情况下,不会自动生成override fun toString(): String = "我是自定义的toString" }fun main() {val user = User("小明", 18)println(user) //结果: 我是自定义的toString }
-
如果超类型具有
open
.componentN()
函数并返回兼容类型,则为数据类生成相应的函数,并覆盖超类型的函数。如果由于一些关键字导致无法重父类对应的函数会导致直接报错。abstract class AbstractUser {//此函数必须是open的,否则无法被数据类继承open operator fun component1() = "卢本伟牛逼" }data class User(val name: String, val age: Int): AbstractUser() //自动覆盖掉父类的component1函数
-
不允许为
.componentN()
和.copy()
函数提供显式实现。[外链图片转存中…(img-GVONt1pf-1728127119308)]
注意,编译器会且只会根据主构造函数中定义的属性生成对应函数,如果有些时候我们不希望某些属性被添加到自动生成的函数中,我们需要手动将其移出主构造函数:
data class Person(val name: String) {var age: Int = 0 //age属性不会被处理
}
此时生成的所有函数将不会再考虑age属性:
fun main() {val person1 = Person("John")val person2 = Person("John")person1.age = 10person2.age = 20println("person1 == person2: ${person1 == person2}")// person1 == person2: trueprintln("person1 with age ${person1.age}: $person1")// person1 年龄为 10: Person(name=John)println("person2 with age ${person2.age}: $person2")// person2 年龄为 20: Person(name=John)
}
数据类自带一个拷贝对象的函数,使用.copy()
函数复制对象,允许您更改其一些属性,而其余的保持不变。此函数对上述User
类的实现如下:
data class User(val name: String, val age: Int)fun main() {val user = User("小明", 18)val copyUser = user.copy() //使用拷贝函数生成一个内容完全一样的新对象println(user == copyUser)println(user === copyUser)
}
在copy函数还可以在拷贝过程中手动指定属性的值:
val user = User("小明", 18)
println(user.copy(age = 17)) //结果为 User(name=小明, age=17)
枚举类型
我们知道,在Kotlin中有一个Boolean类型,它只有两种结果,要么为false要么为true,这代表它的两种状态真和假。有些时候,可能两种状态并不能满足我们的需求,比如一个交通信号灯,它具有三种状态:红灯、黄灯和绿灯。
如果我们想要存储和表示自定义的多种状态,使用枚举类型就非常合适:
//在类前面添加enum表示这是一个枚举类型
enum class LightState {GREEN, YELLOW, RED //直接在枚举类内部写上所有枚举的名称,一般全部用大写字母命名
}
枚举类的值只能是我们在类中定义的那些枚举,不可以存在其他的结果,枚举类型同样也是一个普通的类,只是存在值的限制。
要使用一个枚举类的对象,可以通过类名直接获取定义好的枚举:
fun main() {val state: LightState = LightState.RED //直接得到红灯println(state.name) //自带name属性,也就是我们编写的枚举名称,这里是RED
}
同样的,枚举类也可以具有成员:
//同样可以定义成员变量,但是不能命名为name,因为name拿来返回枚举名称了
enum class LightState(val color: String) {GREEN("绿灯"), YELLOW("黄灯"), RED("红灯"); //枚举在定义时也必须填写参数,如果后面还要编写成员函数之类的其他内容,还需在末尾添加分号结束fun isGreen() = this == LightState.GREEN //定义一个函数也是没问题的
}
我们可以像普通类那样正常使用枚举类的成员:
val state: LightState = LightState.RED
println("信号灯的颜色是: ${state.color}")
println("信号灯是否可以通行: ${state.isGreen()}")
枚举类型可以用于when
表达式进行判断,因为它的状态是有限的:
val state: LightState = LightState.RED
val message: String = when(state) {LightState.RED -> "禁止通行"LightState.YELLOW -> "减速通行"LightState.GREEN -> "正常通行"
}
println(message) //结果为: 禁止通行
在枚举类中也可以编写抽象函数,抽象函数需要由枚举自行实现:
enum class LightState(val color: String) {GREEN("绿灯"){override fun test() = println("我是绿灯,表示可以通过")}, YELLOW("黄灯") {override fun test() = println("我是黄灯,是让你减速,不是让你踩油门加速过去")}, RED("红灯") {override fun test() = println("我是红灯,禁止通行")};abstract fun test() //抽象函数
}fun main() {val state: LightState = LightState.REDstate.test() //调用函数: 我是红灯,禁止通行
}
如果枚举类实现了某个接口,同样可以像这样去实现:
interface Message {fun test()
}enum class LightState(val color: String) : Message {GREEN("绿灯"){override fun test() = println("我是绿灯,表示可以通过")}, YELLOW("黄灯") {override fun test() = println("我是黄灯,是让你减速,不是让你踩油门加速过去")}, RED("红灯") {override fun test() = println("我是红灯,禁止通行")};
}
enum class LightState(val color: String) : Message {GREEN("绿灯"), YELLOW("黄灯"), RED("红灯");override fun test() = println("")
}
枚举类也为我们准备了很多的函数:
val state: LightState = LightState.valueOf("RED") //通过valueOf函数以字符串名称的形式转换为对应名称的枚举
val state: LightState = enumValueOf<LightState>("RED") //同上
println(state)
println(state.ordinal) //枚举在第几个位置
println(state.name) //枚举名称
val entries: EnumEntries<LightState> = LightState.entries //一键获取全部枚举,得到的结果是EnumEntries类型的,他是List的子接口,因此可以当做List来使用
val values: Array<LightState> = enumValues<LightState>() //或者像这样以Array形式获取到所有的枚举
println(entries)
匿名类、单例类和伴生对象
匿名类
有些时候,可能我们并不需要那种通过class
关键字定义的对象,而是以匿名的形式创建一个临时使用的对象,在使用完之后就不再需要了,这种情况完全没有必要定义一个完整的类型,我们可以使用匿名类的形式去编写。
val obj = object { //使用object关键字声明一个匿名类并创建其对象,可以直接使用变量接收得到的对象val name: String = ""override fun toString(): String = "我是一个匿名类" //匿名类默认继承于Any,可以直接重写其toString
}
println(obj)
可以看到,匿名类除了没名字之外,也可以定义成员,只不过这种匿名类不能定义任何构造函数,因为它是直接创建的,这种写法我们也可以叫做对象表达式。
匿名类不仅可以直接定义,也可以作为某个类的子类定义,或是某个接口的实现:
interface Person {fun chat()
}fun main() {val obj: Person = object : Person { //直接实现Person接口override fun chat() = println("牛逼啊")}obj.chat() //当做Person的实现类使用
}
interface Person
open class Human(val name: String)fun main() {val obj: Human = object : Human("小明"), Person { //继承类时,同样需要调用其构造函数override fun toString() = "我叫$name" //因为是子类,直接使用父类的属性也是没问题的}println(obj)
}
可以看到,平时我们无法直接实例化的接口或是抽象类,可以通过匿名类的形式得到一个实例化对象。
我们再来看下面这种情况:
interface KRunnable {fun invoke() //此类型是一个接口且只包含一个函数
}
根据我们上面学习的用法,如果我们想要使用其匿名类,可以像这样编写:
fun main() {val runnable = object : KRunnable { //使用匿名类的形式编写KRunnable的实现类对象override fun invoke() {println("我是函数invoke的实现")} }runnable.invoke()
}
特别的,对于只存在一个抽象函数的接口称为函数式接口或单一抽象方法(SAM)接口,函数式接口可以有N个非抽象成员,但是只能有一个抽象成员。对于函数式接口,可以使用我们前面介绍的Lambda表达式来使代码更简洁:
fun interface KRunnable { //在接口声明前面添加fun关键字fun invoke()
}...fun main() {val runnable = KRunnable { //支持使用Lambda替换println("我是函数invoke的实现")}runnable.invoke()
}
我们再来看下面这种情况:
fun interface Printer {fun print()
}fun test(printer: Printer) { //需要Printer接口实现对象printer.print()
}
我们在调用test时,也可以写的非常优雅:
fun main() {test { //直接Lambda一步到位println("Hello World")}
}
正是因为有了匿名类,所以有些时候我们通过函数得到的结果,可能并不是某个具体定义的类型,也有可能是直接采用匿名形式创建的匿名类对象:
open class Human(val name: String)fun test() = object: Human("小明") { //返回的一个匿名类对象val age: Int = 10override fun toString() = "我叫$name"
}fun main() {println(test().name)println(test().age) //编译错误,因为返回的类型是Human,由于其匿名特性,只能当做Human使用
}
单例类
object
关键字除了用于声明匿名类型,也可以用于声明单例类。单例类是什么意思呢?就像它的名字一样,在整个程序中只能存在一个对象,也就是单个实例,不可以创建其他的对象,始终使用的只能是那一个对象。
object Singleton { //声明的一个单例类private var name = "你干嘛"override fun toString() = "我叫$name"
}fun main() {val singleton = Singleton //通过类名直接得到此单例类的对象//不可以通过构造函数的形式创建对象println(singleton)
}
object Singleton {fun test() = println("原神,启动!")
}fun main() {Singleton.test() //单例定义的函数直接使用类名即可调用
}
用起来与Java中的静态属性挺像的,只不过性质完全不一样。单例类的这种性质在很多情况下都很方便,比如我们要编写某些工具操作,可以直接使用单例类的形式编写。
伴生对象
现在我们希望一个类既支持单例类那样的直接调用,又支持像一个普通class那样使用,这时该怎么办呢?
我们可以使用伴生对象来完成,实际上就是将一个单例类写到某个类的内部:
class Student(val name: String, val age: Int) {//使用companion关键字在内部编写一个伴生对象,它同样是单例的companion object Tools {//伴生对象定义的函数可以直接通过外部类名调用fun create(name: String, age: Int) = Student(name, age)}
}fun main() {//现在Student不仅具有对象的函数,还可以通过类名直接调用其伴生对象通过的函数val student = Student.create("小明", 18)println(student.toString())
}
伴生对象在Student类加载的时候就自动创建好了,因此我们可以实现直接使用。
委托模式
在有些时候,类的继承在属性的扩展上起到了很大的作用,通过继承我们可以直接获得某个类的全部属性,而不需要再次进行编写,不过,现在有了一个更好的继承替代方案,那就是委托模式(在设计模式中也有提及)名字虽然听着很高级,但是其实很简单,比如我们现在有一个接口:
interface Base {fun print()
}
正常情况下,我们需要编写一个它的实现类:
class BaseImpl(val x: Int) : Base {override fun print() = println(x)
}
现在我们换一个思路,我们再来创建一个实现类:
class Derived(val base: Base): Base { //将一个Base的实现类作为属性保存到类中,同样实现Base接口override fun print() = base.print() //真正去实现这个接口的,实际上并不是当前类,而是被拉进来的那个替身
}
这就是一个非常典型的委托模型,且大量实践已证明委托模式是实现继承的良好替代方案。
Kotlin对于这种模式同样给予了原生支持:
interface Base {fun print()
}class BaseImpl(val x: Int) : Base {override fun print() = println(x)
}class Derived(val b: Base): Base by b //使用by关键字将所有接口待实现操作委托给指定成员
这样就可以轻松实现委托模式了。
除了类可以委托给其他对象之外,类的成员属性也可以委托给其他对象:
import kotlin.reflect.KPropertyclass Example {var p: String by Delegate() //属性也可以使用by关键字委托给其他对象
}// 委托的类
class Delegate { //需要重载属性的获取和设置两个运算operator fun getValue(thisRef: Any?, property: KProperty<*>): String {return "$thisRef, 这里委托了 ${property.name} 属性"}operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {println("$thisRef 的 ${property.name} 属性赋值为 $value")}
}fun main() {println(Example().p)
}
不过,自己去定义一个类来进行委托实在是太麻烦了,Kotlin在标准库中也为我们提供了大量的预设函数:
class Example {val p: String by lazy { "牛逼啊" } //lazy为我们生成一个委托对象,这样在获取属性值的时候就会执行lazy里面的操作了,看起来效果就像是延迟执行一样,由于只能获取,所以说只支持val变量
}fun main() {println(Example().p)
}
也可以设置观察者,实时观察变量的变化:
class Example {var p: String by Delegates.observable("我是初始值") {prop, old, new ->println("检测到$prop 的值发生变化,旧值:$old -> 新值:$new")}
}fun main() {Example().p = "你干嘛"
}
属性也可以直接将自己委托给另一个属性:
class Example(var a: String) {var p: String by ::a //使用双冒号来委托给其他属性
}fun main() {val example = Example("你干嘛")println(example.p)
}
相信各位应该能猜到,这样委托给其他属性,当前属性的值修改,会直接导致其他属性的值也会修改,相反同样它们已经被相互绑定了。
属性也可以被委托给一个Map来进行存储:
class User(map: MutableMap<String, Any>) {var name: String by map //直接委托给外部传入的Map集合var age: Int by map //变量的值从Map中进行存取override fun toString(): String = "名称: $name, 年龄: $age"
}fun main() {val map: MutableMap<String, Any> = mutableMapOf("name" to "John Doe","age" to 25)val user = User(map)println(user) //名称: John Doe, 年龄: 25map["age"] = 10 //映射的值修改会直接影响变量的值println(user) //名称: John Doe, 年龄: 10
}
注意,在使用不可变的Map时,只能给val类型变量进行委托,因为不可修改。
密封类型
有些时候,我们可能会编写一些类给其他人使用,但是我们不希望他们随意继承使用我们提供的类,我们只希望在我们提供的框架内部自己进行使用,这时我们就可以将类或接口设定为密封的。
密封类的所有直接子类在编译时都是已知的。不得在定义密封类的模块和包之外出现其他子类。例如,第三方项目无法在其代码中扩展您的密封类。因此,密封类的每个实例都有一个来自预设好的类型,且该类型在编译该类时是已知的。
package com.testsealed class A //声明密封类很简单,直接添加sealed关键字即可
sealed class B: A() //密封类同一个模块或包中可以随意继承,并且子类也可以是密封的
当我们在其他包中使用这个密封类,在其他包或模块中无法使用:
class C: A() //编译错误,不在同一个模块fun main() {val b = B() //编译错误,不可以实例化
}
密封类将类的使用严格控制在了模块内部,包括密封接口及其实现也是如此:一旦编译了具有密封接口的模块,就不会出现新的实现类。
从某种意义上说,密封类类似于枚举类:枚举类型的值数量也受到限制,由我们自己定义,但每个枚举变量仅作为单个实例存在,而密封类的子类可以有多个实例,每个实例都有自己的状态。密封类本身也是抽象的,它不能直接实例化,并且可以具有abstract
成员:
sealed class A
sealed class B: A() {abstract fun test()
}
密封类继承后也可以使其不继续密封,让外部可以正常使用:
sealed class A
class B: A()
class C: A()
class D: A() //不添加sealed关键字使其不再密封
但是由于类A是密封的,因此所有继承自A的类只能是我们自己写的,别人无法编写继承A的类,除非我们将某个继承A的类设定为open特性,允许继承。因此,这也进一步证明密封类在一开始就确定了有哪些子类。
由于密封类能够确定,所以在使用when进行类型判断时,也是有限的:
fun main() {val a: A = C()when(a) {is B -> println("是B")is C -> println("是C")is D -> println("是D")}
}
密封类的应用场景其实远不止这些,由于篇幅有限,这里就不展开讲解了。
异常机制
在理想的情况下,我们的程序会按照我们的思路去运行,按理说是不会出现问题的,但是,代码实际编写后并不一定是完美的,可能会有我们没有考虑到的情况,如果这些情况能够正常得到一个错误的结果还好,但是如果直接导致程序运行出现问题了呢?
我们来看下面这段代码:
fun main() {test(1, 0) //当b为0的时候,还能正常运行吗?
}private fun test(a: Int, b: Int): Int {return a / b //没有任何的判断而是直接做计算
}
1怎么可能去除以0呢,数学上有明确规定,0不能做除数,所以这里得到一个异常:
那么这个异常到底是什么样的一种存在呢?当程序运行出现我们没有考虑到的情况时,就有可能出现异常或是错误!它们在默认情况下会强行终止我们的程序。
异常的使用
们在之前其实已经接触过一些异常了,比如数组越界异常,空指针异常,算术异常等,他们其实都是异常类型,我们的每一个异常也是一个类,他们都继承自Throwable
类!异常类型本质依然类的对象,但是异常类型支持在程序运行出现问题时抛出(也就是上面出现的红色报错)也可以提前声明,告知使用者需要处理可能会出现的异常!
每个异常对象都包含一条消息、一个堆栈跟踪和一个可选原因。
我们自己也可以抛出异常,要抛出异常对象,请使用throw
出表达式:
fun main() {//Exception继承自Throwable类,作为普通的异常类型throw Exception("牛逼")
}
可以看到,控制台出现了下面的报错:
所以,我们平时看到的那些丰富多彩的异常,其实大部分都是由程序主动抛出的。
我们也可以依葫芦画瓢,自定义我们自己的异常类:
class TestException(message: String) : Exception(message)fun main() {throw TestException("自定义异常")
}
是不是感觉很简单,异常的出现就是为了方便我们快速判断程序的错误。我们可以在异常打印出来的栈追踪信息中得到当前程序出现问题的位置:
这里指示的很明确,是我们的Main.kt文件第四行代码出现了异常。
异常的处理
当程序没有按照我们理想的样子运行而出现异常时(JVM平台下,默认会交给JVM来处理,JVM发现任何异常都会立即终止程序运行,并在控制台打印栈追踪信息)现在我们希望能够自己处理出现的问题,让程序继续运行下去,就需要对异常进行捕获,比如:
val array = arrayOf(1, 2, 3)
println(array[3]) //数组长度根本没有4,很明显这里会出现异常
现在我们希望能够手动处理这种情况,即使发生异常也要继续下去,我们可以使用try-catch语句块来完成:
fun main() {try { //使用try-catch语句进行异常捕获val array = arrayOf(1, 2, 3)println(array[3])} catch (e: ArrayIndexOutOfBoundsException) {//因为异常本身也是一个对象,catch中实际上就是用一个局部变量去接收异常}println("程序继续正常运行!")
}
我们可以将代码编写到try
语句块中,只要是在这个范围内发生的异常,都可以被捕获,使用catch
关键字对指定的异常进行捕获,这里我们捕获的是ArrayIndexOutOfBoundsException数组越界异常:
可以看到,当我们捕获异常之后,程序可以继续正常运行,并不会像之前一样直接结束掉。
注意,catch中捕获的类型只能是Throwable的子类,也就是说要么是抛出的异常,要么是错误,不能是其他的任何类型。
我们可以在catch
语句块中对捕获到的异常进行处理:
fun main() {try { //使用try-catch语句进行异常捕获val array = arrayOf(1, 2, 3)println(array[3])} catch (e: ArrayIndexOutOfBoundsException) {e.printStackTrace(); //打印栈追踪信息println("异常错误信息:"+e.message); //获取异常的错误信息}println("程序继续正常运行!")
}
当代码可能出现多种类型的异常时,我们希望能够分不同情况处理不同类型的异常,就可以使用多重异常捕获:
try {//....
} catch (e: Exception) { //父类型在前,会将子类的也捕获} catch (e: NullPointerException) { //因为NullPointerException是Exception的子类型,永远都不会进入这里} catch (e: IndexOutOfBoundsException) { //永远都不会进入这里}
最后,当我们希望,程序运行时,无论是否出现异常,都会在最后执行任务,可以交给finally
语句块来处理:
try {//....
} catch (e: Exception) {} finally {println("lbwnb") //无论是否出现异常,都会在最后执行
}
注意:try
语句块至少要配合catch
或finally
中的一个。
try
也可以当做一个表达式使用,这意味着它可以有一个返回值:
fun main() {val input = readln()val a: Int? = try { input.toInt() } catch (e: NumberFormatException) { null }println(a)
}
针对于空类型,我们也可以在判断为空时直接抛出异常:
val s = person.name ?: throw IllegalArgumentException("Name required")
相关文章:

八、特殊类型异常机制
特殊类型&异常机制 数据类型枚举类型匿名类、单例类和伴生对象匿名类单例类伴生对象 委托模式密封类型异常机制异常的使用异常的处理 数据类型 对于那些只需要保存数据的类型,我们常常需要为其重写toString、equals等函数,针对于这种情况下…...
虾皮Shopee Android面试题及参考答案
HTTP 状态码有哪些? HTTP 状态码是用以表示网页服务器超文本传输协议响应状态的 3 位数字代码。主要分为五大类: 1xx 信息性状态码:表示服务器正在处理请求,这些状态码是临时的响应,主要用于告诉客户端请求已经被接收,正在处理中。例如,100 Continue 表示客户端应当继续…...
Docker Compose 部署大模型GPU集群:高效分配与管理算力资源
Docker Compose 部署大模型GPU集群:高效分配与管理算力资源 文章目录 Docker Compose 部署大模型GPU集群:高效分配与管理算力资源一 Dockerfile 编写二 Dockerfile 示例三 分配GPU资源1)GPU分配:指定count2)GPU分配&am…...

直立行走机器人技术概述
直立行走机器人技术作为现代机器人领域的重要分支,结合了机械工程、计算机科学、人工智能、传感技术和动态控制等领域的最新研究成果。随着技术的不断发展,直立行走机器人在救灾、医疗、家庭辅助等领域开始发挥重要作用。本文旨在对直立行走机器人的相关…...
【Linux】wsl虚拟机时间和实际时间不符合
本文首发于 ❄️慕雪的寒舍 偶然遇到了这个问题,触发原因是电脑在开启wsl的情况下进入了 休眠 模式,且在无网络情况下几天不使用。 然后开启wsl,发现git log显示最新commit的提交时间是明天,给我吓一跳,然后才发现原来…...

初识算法 · 滑动窗口(1)
目录 前言: 长度最小的子数组 题目解析 算法原理 算法编写 无重复长度的最小字符串 题目解析 算法原理 算法编写 前言: 本文开始,介绍的是滑动窗口算法类型的题目,滑动窗口本质上其实也是双指针,但是呢&#…...

nginx和gateway的关系和区别
在技术选型时,选择 Nginx 和 Spring Cloud Gateway(或简称为 Gateway)主要取决于具体应用场景和技术需求。下面是两者的一些关键差异和适用场景。 一、Nginx 概念 Nginx 是一个高性能的 Web 服务器和反向代理服务器,常被用作静…...

【算法笔记】滑动窗口算法原理深度剖析
【算法笔记】滑动窗口算法原理深度剖析 🔥个人主页:大白的编程日记 🔥专栏:算法笔记 文章目录 【算法笔记】滑动窗口算法原理深度剖析前言一.长度最小的子数组1.1题目1.2思路分析1.3算法流程1.4正确性证明1.5代码实现 二.无重复…...

4S店4S店客户管理系统小程序(lw+演示+源码+运行)
社会的发展和科学技术的进步,互联网技术越来越受欢迎。手机也逐渐受到广大人民群众的喜爱,也逐渐进入了每个用户的使用。手机具有便利性,速度快,效率高,成本低等优点。 因此,构建符合自己要求的操作系统是非…...
rabbitMq------连接管理模块
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言管理的字段连接内存管理对象 前言 我们的网络通信框架使用的muduo库,而在mudu库中是已经有了连接的概念,但是我们呢还有一个信道的概念…...
【部署项目】禹神:前端项目部署上线笔记
1.项目打包 ● 我们开发用的脚手架其实就是一个微型服务器,用于:支撑开发环境、运行代理服务器等。 ● 打包完的文件中不存在:.vue、.jsx、.less 等文件,而是:html、css、js等。 ● 打包后的文件,不再借助…...
力扣10.1
983. 最低票价 在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行。在接下来的一年里,你要旅行的日子将以一个名为 days 的数组给出。每一项是一个从 1 到 365 的整数。 火车票有 三种不同的销售方式 : 一张 为期一天 的通行证售…...

TypeScript 算法手册 - 【冒泡排序】
文章目录 TypeScript 算法手册 - 冒泡排序1. 冒泡排序简介1.1 冒泡排序定义1.2 冒泡排序特点 2. 冒泡排序步骤过程拆解2.1 比较相邻元素2.2 交换元素2.3 重复过程 3. 冒泡排序的优化3.1 提前退出3.2 记录最后交换位置案例代码和动态图 4. 冒泡排序的优点5. 冒泡排序的缺点总结 …...

计算机网络——http和web
无状态服务器——不维护客户端 怎么变成有状态连接 所以此时本地建立代理—— 若本地缓存了——但是服务器变了——怎么办?...

使用 Light Chaser 进行大屏数据可视化
引言 在当今数据驱动的世界中,数据可视化变得越来越重要。Light Chaser 是一款基于 React 技术栈的大屏数据可视化设计工具,通过简单的拖拽操作,你可以快速生成漂亮、美观的数据可视化大屏和看板。本文将介绍如何使用 Light Chaser 进行数据…...
Java中的异常概念
在Java编程中,异常(Exception)是一种特殊的情况,它在程序执行期间发生,会干扰程序正常的流程。 ## 一、异常的产生原因 1. **用户输入错误** - 例如,当一个程序期望用户输入一个整数,而用户…...
flutter_鸿蒙next_Dart基础②List
目录 代码示例 代码逐段解析 1. 创建和打印列表 2. 强类型列表 3. 创建可扩展的空列表 4. 创建填充列表 5. 列表扩展 6. 使用可选展开操作符 7. 获取列表长度 8. 列表反转 9. 添加多个元素 10. 移除元素 11. 根据索引移除元素 12. 在特定位置插入元素 13. 清空列…...
【2024保研经验帖】武汉大学测绘遥感国家重点实验室夏令营(计算机向)
前言 先说本人背景:末211,rk前5%,无科研,有几个竞赛(数模、机器人等) 武大的国重是我参加的第二个夏令营,武大国重这次有提前开几个分会场,一个在中南大学,一个在吉林大学,还有在兰…...

PyGWalker:让你的Pandas数据可视化更简单,快速创建数据可视化网站
1、PyGWalker应用: 在数据分析的过程中,数据的探索和可视化是至关重要的环节,如何高效地将分析结果展示给团队、客户,甚至是公众,是很多数据分析师和开发者面临的挑战,接下来介绍的两大工具组合——PyGWalker与Streamlit,可以帮助用户轻松解决这个问题,即使没有复杂的代…...

Ubuntu24.04远程开机
近来在几台机器上鼓捣linux桌面,顺便研究一下远程唤醒主机。 本篇介绍Ubuntu系统的远程唤醒,Windows系统的唤醒可搜索相关资料。 依赖 有远程唤醒功能的路由器(当前一般都带这个功能)有线连接主机(无线连接有兴趣朋友…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...

DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...