【Kotlin精简】第3章 类与接口
1 简介
Kotlin类的声明和Java没有什么区别,Kotlin中,类的声明也使用class关键字,如果只是声明一个空类,Kotlin和Java没有任何区别,不过定义类的其他成员会有一些区别。实例化类不用写new,类被继承或者重写必须加上 open 关键字,构造器可以写在类后面。类的基本结构:构造函数,属性,方法。
Kotlin接口和Java的抽象类基本类似,可以有抽象方法,也可以有方法体。但override作为关键字使用,而不是注解。
2 类的构造器
构造器也叫构造方法,是类创建的必要元素。构造函数可以用关键词constructor声明,
在Kotlin中,类允许定义一个主构造器,和若干个第二构造器。
主构造器是类头的一部分,紧跟在类名的后面,参数是可选的。
2.1 主构造器
如下代码定义了一个类,并指定了一个主构造器。
class Person constructor(name: String) {
}// 如果主构造器没有任何注解,任何修饰,constructor可以省略
class Person(name: String) {
}// 如果是主构造器,需要在init代码块中进行初始化主构造器
// 注:主构造器中的参数不仅可以在init代码块中使用,还可以对类的属性进行初始化。
// var和val也可以修饰主构造器参数:
// 如果使用var,表示参数对于构造器来说是变量,在构造器内部可以对其进行操作和改变(在主构造器中修改值后,不会把修改后的值传到对象外面)
// 如果使用val,表示该参数是常量,在构造器中不能修改它的值。
class Person(var name: String) {val myName = nameinit {println(name)}
}
主构造函数不能包含代码块,它只能作为一个纯粹的参数列表声明,如果我们需要初始化逻辑,用关键词init可以声明一个局部作用域,它会在实例化时被调用。事实上所有init block都会被编译为主构造函数的一部分,然后按照声明顺序执行
2.2 第二构造器
Kotlin的类中,除了可以声明一个主构造器之外,还可以声明若干个第二构造器,第二构造器必须在类中声明,前面必须加constructor关键字。
class Person(var name: String) {init {name = "hello"+ nameprintln(name)}constructor(age: Int) : this("js") {println(name + " " + age)}constructor(sex : Byte) :this(20){println(name +" "+ sex)}
}
注意:
- 主构造器中可以使用var和val修饰参数,但第二构造器中不能使用,也就意味着
第二构造器中的参数都是只读的。 - 由于
nit block本质上是主构造函数的一部分,而次构造函数需要委托主构造函数,所以所有的init block要优先于次构造函数执行, 类级别的属性在init block之前,主构造函数后执行。
2.3 单例模式
Kotlin 中的单例模式,需要理解一个特殊的类型,这个类型叫做object,这个object可不是Java中所有类的父类(Object),这个object 就是创建单例用的,我们都知道,Java中单例有懒汉式、饿汉式,双重检查锁等几种单例变种,但是在Kotlin中除了可以使用这几种,还有object的单例实现方式,实际上该方式也是饿汉式的实现,只是Kotlin的语法糖使写法不一样而已。
2.3.1 饿汉式
object Singleton {
}
2.3.2 懒汉式
Kotlin的写法比较多,可以直接从java翻译,还有一种写法就有点复杂了,私有构造函数,我们也用到了他的get()方法。注意:companion object 修饰为伴生对象,伴生对象在类中只能存在一个,类似于java中的静态方法 Java 中使用类访问静态成员,静态方法。
class Singleton2 private constructor(){companion object {private var singleton2 : Singleton2? = nullget() {if (field == null)field = Singleton2()return field;}fun get() : Singleton2? {return singleton2 }}
}class Singleton3 private constructor(){companion object {val Instance by lazy(mode = LazyThreadSafetyMode.NONE) {Singleton3()}}
}
2.3.3 线程安全的懒汉式
Kotlin直接声明@Synchronized为同步就行
class Singleton4 private constructor(){companion object {private var singleton4 : Singleton4? = nullget() {if (field == null)field = Singleton4()return field;}@Synchronizedfun get() : Singleton4? {return singleton4 }}
}
2.3.4 线程安全双重校验
LazyThreadSafetyMode.SYNCHRONIZED即锁的意思
class Singleton5 private constructor(){companion object {val Instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {Singleton5()}}
}
2.3.5 静态内部类
class Singleton6 private constructor(){companion object {fun getInstance() = SingletonHolder.ins}private object SingletonHolder{var ins = Singleton6()}
}
由于Kotlin自身语法简洁的优势,写单例模式也就一两行的代码,这里没有深究一些高级用法,比如Lazy,属于Kotlin延迟属性的委托,有兴趣的可以自己研究下。
3 类的属性
Kotlin中类的属性通过基本关键词val, var来声明,可以像Java一样直接声明在类体中,也可以通过语法糖直接写在主构造函数中。如果属性声明了默认值,根据类型推导规则可以省略类型声明
Kotlin中类的属性必须被初始化,或者声明为abstract。初始化有两种方式,一种是添加默认值,一种是延迟初始化,使用后者需要用lateinit修饰属性,表示我希望该属性在运行时动态加载,并且我信任自己的代码不会在它没有初始化之前就使用它(如果这么干,空指针crash), lateinit 延迟初始化 的属性 , 在使用前可以执行::属性名称.isInitialized检查 , 查看该属性是否进行了初始化操作。
lazy 惰性初始化 的 属性初始化操作 是 提前定义好的 , 在 调用之前 自动进行初始化操作 , 如果不调用 , 则不进行初始化 ;
lateinit 延迟初始化 的 属性初始化操作 , 需要 手动进行初始化 , 如果忘了初始化直接调用就会报错 ;
Kotlin的属性提供了getter/setter语法。一般情况下不需要手动重写get/set方法,下面例子是两种常见的重写case。
const val VERSION = "1.0"class Person(val age: Int,val address: String = "Asia"
) {var country = "China"// 惰性初始化,使用的时候自动初始化val name by lazy { initName() }private fun initName(): String {return "Tom"}// lateinit延迟初始化lateinit var phone: Stringinit{initPhone()}private fun initPhone(){// isInitialized检测lateinit延迟初始化的属性是否已经初始化if(!::phone.isInitialized) {phone = "138****1111"}}// 这里的`field`关键字是字面量的含义,可以粗略理解为它是当前变量在内存中的指针var size = 1get() = fieldset(value) {// 提供特殊的过滤逻辑field = if (value < 10) value else 10}private var _number = "000"var number// 对外仅仅暴露get方法,这里只是演示,真实情况_number一般用val声明get() = _numberset(value) {_number = value}
}
Kotlin的类还存在编译时常量的概念,用const修饰,和Java的final概念基本一致
4 接口
接口本质上也是类,是特殊的类。Kotlin的接口在Java接口基础上扩充了能力,允许直接实现方法体,也允许声明属性(类似Java的抽象类)。但是需要注意的是接口中的属性要么是abstract的,要么提供了get方法,但是接口的属性不存在backing fields,无法用field关键字获取真实值。
接口可以继承多个接口,多个接口也可以被一个类继承。继承规则和上面的类继承基本一致。
继承多个接口遇到同名方法时,解决冲突方法如下:
interface A {fun foo() { print("A") }fun bar()
}interface B {fun foo() { print("B") }fun bar() { print("bar") }
}class C : A {override fun bar() { print("bar") }
}class D : A, B {override fun foo() {super<A>.foo()super<B>.foo()}override fun bar() {super<B>.bar()}
}
但是事实上,几乎不会有人把两个接口的方法命名相同
Kotlin还支持一种特殊的语法糖,当接口中有且只有一个abstract方法时,可以进行如下简写
fun interface IntPredicate {fun accept(i: Int): Boolean
}//传统写法
val isEven = object : IntPredicate {override fun accept(i: Int): Boolean {return i % 2 == 0}
}//语法糖
val isEven = IntPredicate { it % 2 == 0 }
5 类的继承
Kotlin的继承和Java的继承一样都是单继承,区别在于Kotlin用:来代替了extends,Kotlin中所有类都有一个共同的超类 Any,这对于没有继承其他父类的超类型声明的类是默认超类
class Example // 从 Any 隐式继承
Any 有三个方法:equals()、 hashCode() 与 toString()。因此,为所有 Kotlin 类都定义了这些方法。
默认情况下,Kotlin 类是最终(final)的:它们不能被继承。 要使一个类可继承,请用 open 关键字标记它。
继承需要涉及到一些关键词:

5.1 重写方法
open class Shape {//父类有一个带有形参的构造器constructor(name: String?) {}//父类有一个带有形参的构造器constructor() {}open fun draw() { /*……*/ }fun fill() { /*……*/ }
}open class Circle(name: String?) : Shape(name) {constructor() : this(null) {//子类次构造器委托调用子类主构造器,间接委托调用父类构造器}// 父类和其中的draw()方法都是open修饰,因此可以继承并重写,但需要加override 关键词// 重写draw()方法后,如果不想被Circle子类再重写,可以加final修饰final override fun draw() { /*……*/ }
}
重写方法遵循“两同、两小、一大”原则:
- 两同:方法名相同、形参列表相同
- 两小:返回值类型比父类返回值类型小或相等、抛出异常类型比父类小或相等。
- 一大:访问权限比父类大或相等
5.2 重写属性
属性覆盖与方法覆盖类似;在超类中声明然后在派生类中重新声明的属性必须以 override 开头,重写的子类属性的类型必须与父类属性类型兼容(变量类型)。每个声明的属性可以由具有初始化器的属性或者具有 get 方法的属性覆盖。
open class Father {open var a : Float = 1.1fprotected open var b : Float = 1.1fprotected open val c : Float = 1.1f
}class Son : Father() {override var a : Float = 2.2f// 子类属性访问权限必须大于等于父类类型public override var b : Float = 2.2f// 只读属性(val)可被重写成读写属性(var),读写属性(var)不能被重写成只读属性(val)// 因为一个 val 属性本质上声明了一个 get 方法, 而将其覆盖为 var 只是在子类中额外声明一个 set 方法。public override var c : Float = 2.2f
}
5.3 强制重写
当子类同时继承多个超类(只能继承一个类,但可以实现多个接口)时,如果超类成员(属性/方法)名称一样时,子类需强制重写该成员。
子类想调用父类该成员(函数与属性),需通过super<父类名>.成员的方式调用。
interface FatherInterfs {var a: Floatfun method() {println("执行父接口里面该方法 a的值为:$a")//2.2}
}open class Father {protected open val a: Float = 1.1fopen fun method() {println("执行父类该方法 $a")//2.2}
}class Son : Father(), FatherInterfs {override var a: Float = 2.2foverride fun method() {super<FatherInterfs>.method()super<Father>.method()println("执行子类该方法 $a")//2.2println("父接口a的值为 ${super<Father>.a}")//1.1}
}
在一个内部类中访问外部类的超类,可以通过由外部类名限定的 super 关键字来实现:super@Outer:
class FilledRectangle: Rectangle() {override fun draw() { val filler = Filler()filler.drawAndFill()}inner class Filler {fun fill() { println("Filling") }fun drawAndFill() {super@FilledRectangle.draw() // 调用 Rectangle 的 draw() 实现fill()println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // 使用 Rectangle 所实现的 borderColor 的 get()}}
}
6 其他
6.1 数据类 Data class
Kotlin新增的关键词data,修饰类名变成数据类。在Java开发中经常需要解析一个json文件到内存中,这时需要写一个Java Bean类,定义好对应的属性和get/set方法,然后用诸如GSON的解析库解析。这里的Java Bean作为数据的容器。Kotlin的数据类就可以替代这一功能。例如
//帐号信息
{"username": "somebody","id": "18239048190234891032","basic_info": {"age": 10,"level": 2}
}//data class
data class User(val username: String = "unknown",val id: String = "unknown",val basicInfo: BasicInfo = BasicInfo()
)data class BasicInfo(val age: Int = 0,var level: Int = 0
)
形如上述例子,数据类基本语法规则有如下几条:
- 主构造函数至少要有一个参数
- 主构造函数中的所有参数必须声明val/var,也就是把它们作为属性而声明
- data class不能用abstract, open, sealed, inner来修饰
如何理解这三条约束,需要考虑data class背后都干了些什么。所有声明在主构造函数中的属性都会自动生成如下方法
equals() hashCode() 用来判断两个对象是否相等
toString() 形如"User(param1 = value1, param2 = value2)"
componentN() 用于解构的语法糖
copy() “拷贝构造函数"
其中第一点需要强调,因为一般意义上我们可以用hashCode来区分两个对象(虽然这并不保险),但data class的这一特性使得下例中的风险很容易发生
因为data class类体中声明的属性不参与hashCode的计算,所以只要主构造函数的参数列表一致,两个对象的hashCode就相等,虽然它们在内存中是独立的两个对象
data class Person(val name: String) {var age: Int = 0
}
fun main() {val person1 = Person("John")val person2 = Person("John")person1.age = 10person2.age = 20println("person1 == person2: ${person1 == person2}")println("person1 with age ${person1.age}: ${person1}")println("person2 with age ${person2.age}: ${person2}")
}//result
person1 == person2: true
person1 with age 10: Person(name=John)
person2 with age 20: Person(name=John)
关于第三点所说的解构语法,则是一种语法糖,在很多语言中都存在,最常见的例子如下
这里的(key,value)就是解构语法糖
val numbersMap = mutableMapOf<String, String>().apply { this["one"] = "1"this["two"] = "2" }for ((key, value) in numbersMap) { println(key + ' ' + value)
}
而data class会自动声明componetN方法,也就意味着我们可以对它的对象使用这种语法糖
data class User(val age: Int = 0, val name: String = "someone")val (age, name) = User(10, "Alice")
关于第四点的拷贝函数,一个简单的例子是,假设某个人的帐号level信息改变了,其他都不变,那么你可以这么写
val someOne = User("Alice", "123345", BasicInfo(10, 2))val copyOne = someOne.copy(basicInfo = someOne.basicInfo.copy(level = 3)
)
关于数据类最后一点是,kotlin标准库中的Pair和Triple都是data class,所以它们才能使用解构语法
另外,kotlin 1.1后, data class是可以继承自普通类或者接口的,但事实上data class的继承很少使用,暂且不提
6.2 密封类 Sealed Class
Kotlin新增关键字sealed,修饰类名变成密封类。某种意义上,它可以被认为是对枚举类的增强。因为它有以下特点
- 密封类的所有直接子类在编译期就被唯一确定
- 密封类的子类可以拥有多个实例
- 密封类和它的直接子类必须声明在同一个package下
- 密封类本身是abstract的,必须通过子类来实例化
- 密封类的构造器只能是protect或者private
其中第三点关于密封类及子类的位置,一般是把子类作为嵌套类放在密封类内部,也可以把它们拆分成多个文件放在同一个package下。但需要注意,必须是严格的相同package,不能有如下情况
packageA {sealed class ParentpackageB {class child: Parent}}//子类放在密封类所在package的子package中也是不合法的
关于密封类的其他4点,其实共同做了一件事情:“保证密封类只有有限的几种已知子类”。这样和枚举类型就非常相似,枚举类型的实例只能是一些基本类型,作为flag使用。而密封类的子类可以包含属性、方法,同时也能作为flag,是枚举类型的增强。
考虑如下的例子。假设我希望根据屏幕的亮度来自适应调整软件主题,可以设计这样一个Theme的工具类,这里的Dark, Normal两个子类就是对主题类型的枚举,同时内部也包含一定逻辑
fun main() {println(Theme.getThemeByBrightNess(234).toString())//Theme$Dark@7a07c5b4}sealed class Theme {companion object {const val NORMAL_MAX_BRIGHTNESS = 1000ffun getThemeByBrightNess(brightness: Int): Theme = when {Dark.isThisTheme(brightness) -> DarkNormal.isThisTheme(brightness) -> Normalelse -> Normal}}abstract fun isThisTheme(brightness: Int): Booleanobject Dark : Theme() {private val darkBrightRange = (0.1 * NORMAL_MAX_BRIGHTNESS).toInt() .. (0.3 * NORMAL_MAX_BRIGHTNESS).toInt()override fun isThisTheme(brightness: Int): Boolean = brightness in darkBrightRange}object Normal : Theme() {private val normalBrightRange = (0.3 * NORMAL_MAX_BRIGHTNESS).toInt() .. NORMAL_MAX_BRIGHTNESS.toInt()override fun isThisTheme(brightness: Int): Boolean = brightness in normalBrightRange}}
关于密封类的其他细节,参见官方文档
6.3 嵌套类 Nested class
Kotlin并没有关键字nested,嵌套类形如下例,可以视为外部类的一个成员,通过点操作符调用,常见的例子是Adapter里嵌套ViewHolder的声明
interface OuterInterface {class InnerClassinterface InnerInterface
}class OuterClass {class InnerClassinterface InnerInterface
}
需要注意的是,嵌套类并不持有外部类的引用,把它们嵌套纯粹是符合人类逻辑上的收敛
6.4 内部类 inner class
Kotlin用关键字inner修饰一个嵌套类,被称为内部类。二者唯一的变化就是内部类持有了外部类的引用,可以访问外部类的成员
class Outer {private val bar: Int = 1fun foo(): Int{return 666}inner class Inner {fun foo() = bar}
}val demo = Outer().Inner().foo() // 1
很显然的,内部类有两个潜在问题:
- this指针,如果遇到同名方法或属性,需要使用this@receiver的语法指定当前this指向哪一个作用域
- 内部类天然存在循环引用问题,可能会导致内存泄漏
6.5 枚举类 enum class
Kotlin用enum修饰类成为枚举类,最常用的两种case如下。都是作为flag使用,只不过带不带参数
enum class Direction {NORTH, SOUTH, WEST, EAST
}enum class Color(val rgb: Int) {RED(0xFF0000),GREEN(0x00FF00),BLUE(0x0000FF)
}
但事实上,enum class可以实现接口,自定义方法,来实现很多逻辑,例如
enum class ItemType {A, B,c;fun getTypeForMob(): String {return when (this) {A -> "aa"B -> "bb"C -> "cc"}}
}
Kotlin官方库还有一些关于枚举类型的工具函数,用来罗列或查询枚举类型的成员,例如
fun main(args: Array<String>) {//罗列var directions = Direction.values()for (d in directions){println(d)}for (direction in enumValues<Direction>()) {println(direction)}//查找println(Direction.valueOf("WEST")) //创建枚举类对象用这样的方式val west = enumValueOf<Direction>("WEST")}
6.6 内联类 value class
在Kotlin1.5之前,内联类使用inline 修饰类名,和内联函数共用一个修饰符。但1.5之后内联类改用value修饰符。之所以有这个改动,需要理解为什么要有内联类。
简单来说,jvm对Kotlin中的基本类型,如String等做了很多优化,比如将其内存分配从堆上分配改为栈上分配,这些优化能大幅提高代码性能。但是我们开发者有时候会对基本类型做一些封装(装饰者模式),装饰后的类就无法享受jvm的优化了。鱼与熊掌不可兼得
作为成熟的开发者,我们当然选择全部都要。使用如下的内联类语法即可
value class Password(private val s: String)
Kotlin为了实现这一功能,对内联类做了很多限制,主要的几点如下
有且仅有一个包含单个基本类型参数的构造器
内联类可以有成员和方法,但没有字面量(也就是在堆中无法分配内存),只能对构造器中的参数做一些简单处理
内联类可以实现接口,但不能继承其他类,也不能被其他类继承
在某种意义上,内联类和类型别名有些相似,它们之间的核心区别在于内联类声明了一个新的类型,不能与基本类型互相赋值,而类型别名可以
6.7 对象表达式 object expression
Kotlin用object关键字声明一个对象表达式,这个说法可能有些奇怪,但如果改成匿名内部类就觉得非常熟悉了。object就是对匿名内部类的优化,结合Kotlin的lambda语法糖,可以让代码写得极度简洁
最通常的写法如下
window.addMouseListener(object : MouseAdapter() {override fun mouseClicked(e: MouseEvent) { /*...*/ }override fun mouseEntered(e: MouseEvent) { /*...*/ }
})
关于它的一些其他细节
对象表达式可以实现多个接口
对象表达式持有外部类的引用,可以访问外部作用域的成员(比如当前函数作用域)
注意,object关键字除了声明对象表达式,还可以声明单例和伴生对象
相关文章:
【Kotlin精简】第3章 类与接口
1 简介 Kotlin类的声明和Java没有什么区别,Kotlin中,类的声明也使用class关键字,如果只是声明一个空类,Kotlin和Java没有任何区别,不过定义类的其他成员会有一些区别。实例化类不用写new,类被继承或者重写…...
关于面试以及小白入职后的一些建议
面试的本质 面试的过程是一个互相选择的过程;面试官的诉求是,了解应聘者的个人基本信息、工作态度、专业能力及其他综合能力是否与公司招聘岗位匹配;面试者的诉求是,拿下招聘岗位offer,获得工作报酬; 面试…...
Excel 从网站获取表格
文章目录 导入网站数据导入股票实时行情 用 Excel 获取网站数据的缺点:只能获取表格类的数据,不能获取非结构化的数据。 导入网站数据 转到地址之后: 实测该功能经常导致 Excel 卡死。 导入股票实时行情...
rsync 备份工具(附rsync+inotify 实时同步部署实例)
rsync 备份工具(附rsyncinotify 实时同步部署实例) 1、rsync概述1.1关于rsync1.2rsync 的特点1.3工作原理 2、rsync相关命令2.1基本格式和常用选项2.2启动和关闭rsync服务2.3下行同步基本格式2.4上行同步基本格式2.5免交互2.5.1指定密码文件2.5.2rsync-daemon方式2.…...
Java架构师缓存性能优化
目录 1 缓存的负载策略2 缓存的序列化问题3 缓存命中率低4 缓存对数据库高并发访问5 缓存数据刷新的策略5.1. 实时策略5.2. 异步策略5.3. 定时策略6 何时写缓存7 批量数据来更新缓存8 缓存数据过期的策略9 缓存数据如何恢复10 缓存数据如何迁移11 缓存冷启动和缓存预热想学习架…...
探索服务器潜能:创意项目、在线社区与其他应用
目录 一、部署自己的创意项目 优势: 劣势: 结论: 二、打造一款全新的在线社区 优势: 劣势: 结论: 三、其他用途 总结: 随着互联网的发展,越来越多的人开始拥有自己的服务器…...
「网络编程」网络层协议_ IP协议学习_及深入理解
「前言」文章内容是网络层的IP协议讲解。 「归属专栏」网络编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、IP协议简介二、IP协议报头三、IP网段划分(子网划分)四、特殊的IP地址五、IP地址的数量限制六、私有IP地址和公网IP地址七、路由八、分…...
Go 1.21 新内置函数:min、max 和 clear
max 函数 func max[T cmp.Ordered](x T, y …T) T 这是一个泛型函数,用于从一组值中寻找并返回 最大值,该函数至少要传递一个参数。在上述函数签名中,T 表示类型参数,它必须满足 cmp.Ordered 接口中定义的数据类型要求࿰…...
家居行业如何打破获客困局?2023重庆建博会现场,智哪儿AI营销第一课给出了答案
10月12日-14日,2023中国(重庆)建筑及装饰材料博览会(简称:2023中国重庆建博会)正在重庆国际博览中心如火如荼地进行。「智哪儿」携手2023中国重庆建博会主办方共同主办的《2023家居行业AI营销第一课&#x…...
Spring framework Day11:策略模式中注入所有实现类
前言 什么是策略模式? 策略模式(Strategy Pattern)是一种面向对象设计模式,它定义了算法族(一组相似的算法),并且将每个算法都封装起来,使得它们可以互相替换。策略模式让算法的变…...
MBBF展示的奇迹绿洲:5G的过去、此刻与未来
如果你来迪拜,一定不会错过全世界面积最大的人工岛项目,这是被称为世界第八大奇迹的棕榈岛。多年以来,这座岛从一片砂石、一棵棕榈树开始,逐步建成了整个波斯湾地区的地标,吸引着全世界游人的脚步。 纵观整个移动通信发…...
加持智慧医疗,美格智能5G数传+智能模组让就医触手可及
智慧医疗将云计算、物联网、大数据、AI等新兴技术融合赋能医疗健康领域,是提高医疗健康服务的资源利用效率,创造高质量健康医疗的新途径。《健康中国2030规划纲要》把医疗健康提升到了国家战略层面,之后《“十四五”全面医疗保障规划》等一系…...
Stm32_标准库_14_串口蓝牙模块_手机与蓝牙模块通信_实现模块读取并修改信息
由手机向蓝牙模块传输时间信息,Stm32获取信息并将已存在信息修改为传入信息 测试代码: #include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Serial.h"uint16_t num…...
UDP 的报文结构
UDP的报文结构: 其中前面的源端口号和目的端口号,UDP长度和UDP检验和,它们都是2个字节。 那么什么是UDP长度呢,它指的是后面的数据的长度,换算单位也就是64kb,因此一个数据报(数据)最…...
torch.hub.load报错urllib.error.HTTPError: HTTP Error 403: rate limit exceeded
在运行DINOv2的示例代码时,需要载入预训练的模型,比如: backbone_model torch.hub.load(repo_or_dir"facebookresearch/dinov2", modelbackbone_name) torch.hub.load报错“urllib.error.HTTPError: HTTP Error 403: rate limit…...
测试左移右移-理论篇
目录 前言一、浅解左移1.什么是测试左移?1.1对产品1.2对开发1.3对测试1.4对运维 二、浅解右移1.1对产品1.2对开发1.3对测试1.4对运维 三、总结 前言 测试左移右移,很多人说能让测试更拥有主动权,展示出测试岗位也是有很大的价值,…...
【TensorFlow2 之015】 在 TF 2.0 中实现 AlexNet
一、说明 在这篇文章中,我们将展示如何在 TensorFlow 2.0 中实现基本的卷积神经网络 \(AlexNet\)。AlexNet 架构由 Alex Krizhevsky 设计,并与 Ilya Sutskever 和 Geoffrey Hinton 一起发布。并获得Image Net2012竞赛中冠军。 教程概述: 理论…...
Python进阶之迭代器
文章目录 前言一、迭代器介绍及作用1.可迭代对象2. 迭代器 二、常用函数和迭代器1.常用函数2.迭代器 三、总结结束语 💂 个人主页:风间琉璃🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主💬 如果文章对你有帮助、欢迎关注…...
Vue鼠标右键画矩形和Ctrl按键多选组件
效果图 说明 下面会贴出组件代码以及一个Demo,上面的效果图即为Demo的效果,建议直接将两份代码拷贝到自己的开发环境直接运行调试。 组件代码 <template><!-- 鼠标画矩形选择对象 --><div class"objects" ref"objectsR…...
【MySQL JDBC】使用Java连接MySQL数据库
一、什么是JDBC? 理解API的概念 API:Application Programing Interface -- 应用程序编程接口写好一个程序,这个程序需要给别人提供哪些功能?这些功能就是通过一些 函数/类 这样的方式来提供的。例如 Random、Scanner、ArrayList..…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
