go结构体详解
结构体简介
Golang 中没有“类”的概念,Golang 中的结构体和其他语言中的类有点相似。和其他面向对象语言中的类相比,Golang 中的结构体具有更高的扩展性和灵活性。
Golang 中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型就无法满足需求了,Golang 提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称 struct。也就是我们可以通过 struct 来定义自己的类型了。
type 关键词自定义类型和类型别名
Golang 中通过 type 关键词定义一个结构体,在讲解结构体之前,我们首先给大家看看通过type 自定义类型以及定义类型别名。
- 自定义类型
在 Go 语言中有一些基本的数据类型,如 string、整型、浮点型、布尔等数据类型, Go 语言中可以使用 type 关键字来定义自定义类型。
type myInt int
上面代码表示:将 myInt 定义为 int 类型,通过 type 关键字的定义,myInt 就是一种新的类型,它具有 int 的特性。
- 类型别名
Golang1.9 版本以后添加的新功能。
类型别名规定:TypeAlias 只是 Type 的别名,本质上 TypeAlias 与 Type 是同一个类型。就像一个孩子小时候有大名、小名、英文名,但这些名字都指的是他本人。
type TypeAlias = Type
我们之前见过的 rune 和 byte 就是类型别名,他们的底层定义如下:
type byte = uint8
type rune = int32
自定义类型和类型别名的区别
类型别名与自定义类型表面上看只有一个等号的差异,我们通过下面的这段代码来理解它们之间的区别。
package mainimport ("fmt"
)// 类型定义
type newInt int// 类型别名
type myInt = intfunc main() {var a newIntvar b myIntfmt.Printf("type of a:%T\n", a) //type of a:main.newIntfmt.Printf("type of b:%T\n", b) //type of b:int
}
结果显示 a 的类型是 main.newInt,表示 main 包下定义的 newInt 类型。b 的类型是 int 类型。
下面让我们通过一些具体的例子来帮助你更好地理解Go语言中自定义类型和类型别名的区别。
- 自定义类型案例
假设我们正在开发一个应用程序,需要记录用户的年龄。我们可以创建一个名为Age
的新类型,基于整数类型:
type Age intfunc (a Age) isAdult() bool {return a >= 18
}func main() {var userAge Age = 20fmt.Println(userAge.isAdult()) // 输出: true
}
在这个例子中,Age
是一个自定义类型。它不仅限于只是一个整数,还拥有自己的方法isAdult()
,用于判断该年龄是否达到成年标准。即使int
类型的值为20,也不能直接赋值给Age
变量(除非显式转换),因为它们被视为不同的类型。
- 类型别名案例
现在考虑我们需要在项目中处理大量的用户ID,这些ID实际上是字符串形式的。为了简化代码,我们可以为string
类型创建一个别名UserID
:
type UserID = stringfunc main() {var id UserID = "user123"var str string = id // 这里可以直接赋值,因为UserID是string的别名fmt.Println(str) // 输出: user123
}
在这个例子中,UserID
是string
类型的别名。这意味着UserID
和string
实际上是同一个类型,你可以自由地将一个UserID
类型的值赋给一个string
类型的变量,反之亦然,而不需要进行任何类型转换。此外,你不能给UserID
添加新的方法,因为它不是新类型,而是现有类型的一个别名。
总结
- 自定义类型:当你想要基于某个已有类型创建一个新的、独立的类型,并且可能想为这个新类型添加特定的方法或行为时使用。例如,我们的
Age
类型。- 类型别名:当你想要简化复杂类型的引用或者是在重构过程中希望保持类型兼容性时使用。比如,我们的
UserID
别名。
结构体定义初始化的几种方法
- 结构体的定义
使用 type 和 struct 关键字来定义结构体,具体代码格式如下:
type 类型名 struct {
字段名 字段类型
字段名 字段类型
…
}
其中:
- 类型名:表示自定义结构体的名称,在同一个包内不能重复。
- 字段名:表示结构体字段名。结构体中的字段名必须唯一。
- 字段类型:表示结构体字段的具体类型。
举个例子,我们定义一个 person(人)结构体,代码如下:
type person struct {name stringcity stringage int8
}
同样类型的字段也可以写在一行,
type person struct {
name, city string
age int8
}
注意:结构体首字母可以大写也可以小写,大写表示这个结构体是公有的,在其他的包里面可以使用。小写表示这个结构体是私有的,只有这个包里面才能使用。
- 结构体实例化(第一种方法)
只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。结构体本身也是一种类型,我们可以像声明内置类型一样使用 var 关键字声明结构体类型。
var 结构体实例 结构体类型
示例代码:
package mainimport ("fmt"
)type person struct {name stringcity stringage int8
}func main() {var p1 personp1.name = "张三"p1.city = "北京"p1.age = 18fmt.Printf("p1=%v\n", p1) //p1={张三 北京 18}fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"张三", city:"北京", age:18}
}
- 结构体实例化(第二种方法)
我们还可以通过使用 new 关键字对结构体进行实例化,得到的是结构体的地址。 格式如下:
package mainimport ("fmt"
)type person struct {name stringcity stringage int8
}func main() {var p2 = new(person)p2.name = "张三"p2.age = 20p2.city = "北京"fmt.Printf("%T\n", p2) //*main.personfmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"张三", city:"北京", age
}
从打印的结果中我们可以看出 p2 是一个结构体指针。
注意:在 Golang 中支持对结构体指针直接使用.来访问结构体的成员。p2.name = “张三” 其实在底层是(*p2).name = “张三”。
- 结构体实例化(第三种方法)
使用&对结构体进行取地址操作相当于对该结构体类型进行了一次 new 实例化操作。
package mainimport ("fmt"
)type person struct {name stringcity stringage int8
}func main() {p3 := &person{}fmt.Printf("%T\n", p3) //*main.personfmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"", city:"", age:0}p3.name = "zhangsan"p3.age = 30p3.city = "深圳"(*p3).age = 40 //这样也是可以的fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"zhangsan", city:"深圳", age:3
}
- 结构体实例化(第四种方法) 键值对初始化
package mainimport ("fmt"
)type person struct {name stringcity stringage int8
}func main() {p4 := person{name: "zhangsan", city: "北京", age: 18}fmt.Printf("p4=%#v\n", p4) //p4=main.person{name:"zhangsan", city:"北京", age:18}
}
- 结构体实例化(第五种方法) 结构体指针进行键值对初始化
package mainimport ("fmt"
)type person struct {name stringcity stringage int8
}func main() {p5 := &person{name: "zhangsan", city: "上海", age: 28}fmt.Printf("p5=%#v\n", p5) //p5=&main.person{name:"zhangsan", city:"上海", age:28}
}
当某些字段没有初始值的时候,这个字段可以不写。此时,没有指定初始值的字段的
package mainimport ("fmt"
)type person struct {name stringcity stringage int8
}func main() {p6 := &person{city: "北京"}fmt.Printf("p6=%#v\n", p6) //p6=&main.person{name:"", city:"北京", age:0}
}
- 结构体实例化(第六种方法) 使用值的列表初始化
package mainimport ("fmt"
)type person struct {name stringcity stringage int8
}func main() {// 初始化结构体的时候可以简写,也就是初始化的时候不写键,直接写值:p7 := &person{"zhangsan", "北京", 28}fmt.Printf("p7=%#v\n", p7) //p7=&main.person{name:"zhangsan", city:"北京", age:28}
}
使用这种格式初始化时,需要注意:
- 必须初始化结构体的所有字段。
- 初始值的填充顺序必须与字段在结构体中的声明顺序一致。
- 该方式不能和键值初始化方式混用。
结构体方法和接收者
在 go 语言中,没有类的概念但是可以给类型(结构体,自定义类型)定义方法。所谓方法就是定义了接收者的函数。接收者的概念就类似于其他语言中的 this 或者 self。
方法的定义格式如下:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
其中
- 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名的第一个小写字母,而不是 self、this 之类的命名。例如,Person 类型的接收者变量应该命名为 p,Connector 类型的接收者变量应该命名为 c 等。
- 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
- 方法名、参数列表、返回参数:具体格式与函数定义相同。
示例代码:
package mainimport ("fmt"
)type Person struct {name stringage int8
}func (p Person) printInfo() {fmt.Printf("姓名:%v 年龄:%v", p.name, p.age)
}
func main() {p1 := Person{name: "小王子", age: 25}p1.printInfo()
}
运行结果:
姓名:小王子 年龄:25
- 值类型的接收者
当方法作用于值类型接收者时,Go 语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。
- 指针类型的接收者
指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向对象中的 this 或者 self。
代码示例:
package mainimport ("fmt"
)type Person struct {name stringage int
}// 值类型接收者
func (p Person) printInfo() {fmt.Printf("姓名:%v 年龄:%v\n", p.name, p.age)
}// 指针类型接收者
func (p *Person) setInfo(name string, age int) {p.name = namep.age = age
}
func main() {p1 := Person{name: "小王子", age: 25}p1.printInfo()p1.setInfo("张三", 20)p1.printInfo()p1.setInfo("李四", 24)p1.printInfo()
}
运行结果:
姓名:小王子 年龄:25
姓名:张三 年龄:20
姓名:李四 年龄:24
在Go语言中,给结构体增加方法时选择值接收者还是指针接收者,主要取决于你是否需要在方法内部修改结构体的字段,以及关于性能和语义方面的考量。以下是选择哪种方式的一些指导原则:
-
如果需要修改结构体的字段:
- 应该使用指针接收者。因为只有通过指针接收者,方法内部对结构体字段的修改才会反映到调用该方法的实例上。例如,在你的代码示例中,
setInfo
方法使用了指针接收者,这样它才能改变Person
实例的name
和age
属性。
- 应该使用指针接收者。因为只有通过指针接收者,方法内部对结构体字段的修改才会反映到调用该方法的实例上。例如,在你的代码示例中,
-
如果方法不修改结构体的字段:
- 通常使用值接收者就足够了。这可以避免不必要的内存分配和复制操作。比如
printInfo
方法,因为它不需要修改结构体的状态,所以可以安全地使用值接收者。
- 通常使用值接收者就足够了。这可以避免不必要的内存分配和复制操作。比如
-
关于性能:
- 对于大型结构体来说,使用指针接收者可以避免复制整个结构体带来的开销,从而提高性能。
- 对于小型结构体(如只包含几个基本类型的字段),使用值接收者可能更简洁且不会显著影响性能。
-
一致性:
- 在一个程序中,对于同一个类型的方法,最好保持一致,要么全部使用指针接收者,要么全部使用值接收者,以避免混淆。特别是当某些方法使用指针接收者而其他方法使用值接收者时,可能会导致意外的行为或增加理解难度。
-
接口实现:
- 如果一个类型实现了某个接口,那么无论是使用值接收者还是指针接收者的具体方法都会被认作实现了该接口,只要调用时的接收者类型匹配即可。但是,为了保持清晰和一致性,通常建议所有方法都使用同一种接收者类型(值或指针)。
我们上面的代码中也能看出,printInfo中并没有修改结构体字段,因此用的是值类型接收者,而setInfo方法中更改了结构体字段,用的就是指针类型接收者。
比如我们把上面setInfo方法传入了值类型接收者:
package mainimport ("fmt"
)type Person struct {name stringage int
}// 值类型接收者
func (p Person) printInfo() {fmt.Printf("姓名:%v 年龄:%v\n", p.name, p.age)
}// 指针类型接收者
func (p Person) setInfo(name string, age int) {p.name = namep.age = age
}
func main() {p1 := Person{name: "小王子", age: 25}p1.printInfo()p1.setInfo("张三", 20)p1.printInfo()p1.setInfo("李四", 24)p1.printInfo()
}
运行结果:
姓名:小王子 年龄:25
姓名:小王子 年龄:25
姓名:小王子 年龄:25
综上所述,选择值接收者还是指针接收者,主要基于你是否需要在方法内修改结构体的字段、性能考虑以及代码的一致性和可读性。根据具体情况做出合适的选择是很重要的。
给任意类型添加方法
在 Go 语言中,接收者的类型可以是任何类型,不仅仅是结构体,任何类型都可以拥有方法。举个例子,我们基于内置的 int 类型使用 type 关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。
package mainimport ("fmt"
)type myInt intfunc (m myInt) SayHello() {fmt.Println("Hello, 我是一个 int。")
}
func main() {var m1 myIntm1.SayHello() //Hello, 我是一个 int。m1 = 100fmt.Printf("%#v %T\n", m1, m1) //100 main.
}
结构体的匿名字段
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。
代码示例:
package mainimport ("fmt"
)// Person 结构体 Person 类型
type Person struct {stringint
}func main() {p1 := Person{"小王子", 18}fmt.Printf("%#v\n", p1) //main.Person{string:"北京", int:18}fmt.Println(p1.string, p1.int) //北京 18
}
运行结果:
main.Person{string:"小王子", int:18}
小王子 18
匿名字段默认采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。
嵌套结构体
一个结构体中可以嵌套包含另一个结构体或结构体指针。
package mainimport ("fmt"
)// Address 地址结构体
type Address struct {Province stringCity string
}// User 用户结构体
type User struct {Name stringGender stringAddress Address
}func main() {user1 := User{Name: "张三", Gender: "男", Address: Address{Province: "广东", City: "深圳"}}fmt.Printf("user1=%#v\n", user1) //user1=main.User{Name:" 张 三 ", Gender:" 男 ", Address:main.Address{Province:"广东", City:"深圳"}}
}
嵌套匿名结构体
package mainimport "fmt"// Address 地址结构体
type Address struct {Province stringCity string
}// User 用户结构体
type User struct {Name stringGender stringAddress // 匿名结构体
}func main() {var user2 Useruser2.Name = "张三"user2.Gender = "男"user2.Address.Province = "广东" //通过匿名结构体.字段名访问user2.City = "深圳" //直接访问匿名结构体的字段名fmt.Printf("user2=%#v\n", user2) //user2=main.User{Name:" 张 三 ", Gender:" 男 ", Address:main.Address{Province:"广东", City:"深圳"
}
注意:当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找。
关于嵌套结构体的字段名冲突
嵌套结构体内部可能存在相同的字段名。这个时候为了避免歧义需要指定具体的内嵌结构体的字段。
package main// Address 地址结构体
type Address struct {Province stringCity stringCreateTime string
}// Email 邮箱结构体
type Email struct {Account stringCreateTime string
}// User 用户结构体
type User struct {Name stringGender stringAddressEmail
}func main() {var user3 Useruser3.Name = "张三"user3.Gender = "男"// user3.CreateTime = "2020" // ambiguous selector user3.CreateTimeuser3.Address.CreateTime = "2020" //指定 Address 结构体中的 CreateTimeuser3.Email.CreateTime = "2021" //指定 Email 结构体中的 C
}
结构体的继承
Go 语言中使用结构体也可以实现其他编程语言中的继承。
package mainimport "fmt"// Animal 动物
type Animal struct {name string
}func (a *Animal) run() {fmt.Printf("%s 会运动!\n", a.name)
}// Dog 狗
type Dog struct {Age int8*Animal //通过嵌套匿名结构体实现继承
}func (d *Dog) wang() {fmt.Printf("%s 会汪汪汪~\n", d.name)
}func main() {d1 := &Dog{Age: 4, Animal: &Animal{name: "乐乐"}} //注意嵌套的是结构体指针d1.wang() //乐乐会汪汪汪~d1.run() //乐乐会动!
}
在Go语言中,实际上并没有传统面向对象编程中的“继承”概念。Go采用了一种称为组合的方式,来实现类似的功能。通过将一个结构体嵌入到另一个结构体中,可以达到代码复用的目的,这种方式有时被称作“嵌入”或“委派”,它提供了一种灵活的方式来模拟继承的行为。
参考文献
https://gobyexample.com/
https://www.w3schools.com/go/
https://go.dev/doc/tutorial/
https://www.geeksforgeeks.org/golang-tutorial-learn-go-programming-language/
相关文章:

go结构体详解
结构体简介 Golang 中没有“类”的概念,Golang 中的结构体和其他语言中的类有点相似。和其他面向对象语言中的类相比,Golang 中的结构体具有更高的扩展性和灵活性。 Golang 中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一…...

机器学习-关于线性回归的表示方式和矩阵的基本运算规则
最近在学习机器学习的过程中,发现关于线性回归的表示和矩阵的运算容易费解,而且随着学习的深入容易搞混,因此特意做了一些研究,并且记录下来和大家分享。 一、线性模型有哪些表示方式? 器学习中,线性模型…...

kafka 3.5.0 raft协议安装
前言 最近做项目,需要使用kafka进行通信,且只能使用kafka,笔者没有测试集群,就自己搭建了kafka集群,实际上笔者在很早之前就搭建了,因为当时还是zookeeper(简称ZK)注册元数据&#…...

后台管理系统网页开发
CSS样式代码 /* 后台管理系统样式文件 */ #container{ width:100%; height:100%; /* background-color:antiquewhite;*/ display:flex;} /* 左侧导航区域:宽度300px*/ .left{ width:300px; height: 100%; background-color:#203453; display:flex; flex-direction:column; jus…...

使用一个大语言模型对另一个大语言模型进行“调教”
使用一个大语言模型对另一个大语言模型进行“调教”(通常称为微调或适配),是一种常见的技术手段,用于让目标模型更好地适应特定的任务、领域或风格。以下是基于搜索结果整理的详细步骤和方法: 1.准备工作 安装必要的…...

golang使用sqlite3,开启wal模式,并发读写
因为sqlite是基于文件的,所以默认情况下,sqlite是不支持并发读写的,即写操作会阻塞其他操作,同时sqlite也很容易就产生死锁。 但是作为一个使用广泛的离线数据库,从sqlite3.7.0版本开始(SQLite Release 3.…...

如何利用maven更优雅的打包
最近在客户现场部署项目,有两套环境,无法连接互联网,两套环境之间也是完全隔离,于是问题就来了,每次都要远程到公司电脑改完代码,打包,通过网盘(如果没有会员,上传下载慢…...

音频进阶学习十二——Z变换一(Z变换、收敛域、性质与定理)
文章目录 前言一、Z变换1.Z变换的作用2.Z变换公式3.Z的状态表示1) r 1 r1 r12) 0 < r < 1 0<r<1 0<r<13) r > 1 r>1 r>1 4.关于Z的解释 二、收敛域1.收敛域的定义2.收敛域的表示方式3.ROC的分析1)当 …...

cursor指令工具
Cursor 工具使用指南与实例 工具概览 Cursor 提供了一系列强大的工具来帮助开发者提高工作效率。本指南将通过具体实例来展示这些工具的使用方法。 1. 目录文件操作 1.1 查看目录内容 (list_dir) 使用 list_dir 命令可以查看指定目录下的文件结构: 示例: list_dir log…...

MySQL 主从读写分离实现方案(一)—MariaDB MaxScale实现mysql8读写分离
一:MaxScale 是干什么的?? MaxScale是maridb开发的一个mysql数据中间件,其配置简单,能够实现读写分离,并且可以根据主从状态实现写库的自动切换,对多个从服务器能实现负载均衡。 二:MaxScale …...

阿里云 | DeepSeek人工智能大模型安装部署
ModelScope是阿里云人工智能大模型开源社区 ModelScope网络链接地址 https://www.modelscope.cn DeepSeek模型库网络链接地址 https://www.modelscope.cn/organization/deepseek-ai 如上所示,在阿里云人工智能大模型开源社区ModelScope中,使用阿里云…...

LLAMA-Factory安装教程(解决报错cannot allocate memory in static TLS block的问题)
步骤一: 下载基础镜像 # 配置docker DNS vi /etc/docker/daemon.json # daemon.json文件中 { "insecure-registries": ["https://swr.cn-east-317.qdrgznjszx.com"], "registry-mirrors": ["https://docker.mirrors.ustc.edu.c…...

STM32 CUBE Can调试
STM32 CUBE Can调试 1、CAN配置2、时钟配置3、手动添加4、回调函数5、启动函数和发送函数6、使用方法(采用消息队列来做缓存)7、数据不多在发送函数中获取空邮箱发送,否则循环等待空邮箱 1、CAN配置 2、时钟配置 3、手动添加 需要注意的是STM32CUBE配置的代码需要再…...

MySQL数据存储- 索引组织表
索引组织表 前言数据存储堆表索引组织表 二级索引二级索引的性能评估🔹为什么 idx_name 的性能开销最大?🔹 为什么 idx_last_modify_date 更新频繁会影响性能?分析二级索引性能表格为什么主键应该“紧凑且顺序”?二级索…...

基于STM32设计的仓库环境监测与预警系统
目录 项目开发背景设计实现的功能项目硬件模块组成设计思路系统功能总结使用的模块的技术详情介绍总结 1. 项目开发背景 随着工业化和现代化的进程,尤其是在制造业、食品业、医药业等行业,仓库环境的监控和管理成为了至关重要的一环。尤其是在存储易腐…...

VSCode便捷开发
一、常用插件 Vue 3 Snippets、Vetur、Vue - Official 二、常用开发者工具 三、Vue中使用Element-UI 安装步骤: 1、在VSCode的终端执行如下指令: npm i element-ui -S 2、在main.js中全局引入: import Vue from vue; import ElementUI from …...

理解 Maven 的 pom.xml 文件
pom.xml 是 Maven 项目的核心文件,它是项目构建、依赖管理、插件配置和项目元数据的主要地方。通过 pom.xml 文件,Maven 知道如何构建项目、下载依赖库、执行测试等任务。每个 Maven 项目都必须包含一个 pom.xml 文件。本文将详细讲解 pom.xml 文件的结构…...

docker数据持久化的意义
Docker 数据持久化是指在 Docker 容器中保存的数据不会因为容器的停止、删除或重启而丢失。Docker 容器本身是临时性的,默认情况下,容器内的文件系统是临时的,容器停止或删除后,其中的数据也会随之丢失。为了确保重要数据…...

opentelemetry-collector 配置elasticsearch
一、修改otelcol-config.yaml receivers:otlp:protocols:grpc:endpoint: 0.0.0.0:4317http:endpoint: 0.0.0.0:4318 exporters:debug:verbosity: detailedotlp/jaeger: # Jaeger supports OTLP directlyendpoint: 192.168.31.161:4317tls:insecure: trueotlphttp/prometheus: …...

ASP.NET Core JWT Version
目录 JWT缺点 方案 实现 Program.cs IdentityHelper.cs Controller NotCheckJWTVersionAttribute.cs JWTVersionCheckkFilter.cs 优化 JWT缺点 到期前,令牌无法被提前撤回。什么情况下需要撤回?用户被删除了、禁用了;令牌被盗用了&…...

【ArcGIS】R语言空间分析、模拟预测与可视化技术
R语言在空间数据挖掘中具有广泛的应用,以下是一些关键内容和常用包的介绍: R语言空间数据挖掘的关键技术 空间数据类型 矢量数据:包括点(Point)、线(Line)、面(Polygon)等…...

日常知识点之面试后反思遗留问题汇总
梳理一下最近接触到的几个知识点: 1:突然问到端口复用 (SO_REUSEADDR) 端口复用一般用在服务端重启时,套接字处于time_wait状态时,无法绑定该端口,导致无法启动问题。 设置端口复用ÿ…...

链表(LinkedList) 1
上期内容我们讲述了顺序表,知道了顺序表的底层是一段连续的空间进行存储(数组),在插入元素或者删除元素需要将顺序表中的元素整体移动,时间复杂度是O(n),效率比较低。因此,在Java的集合结构中又引入了链表来解决这一问…...

Qt:Qt Creator项目创建
目录 认识Qt Creator Qt Creator概览 使用Qt Creator新建项目 选择项目模板 选择项目路径 选择构建系统 填写类信息设置界面 选择语言和翻译文件 选择Qt套件 选择版本控制系统 最终效果 认识Qt Creator Qt Creator概览 从开始菜单或者快捷方式打开Qt Creator集成开…...

windows11上,使用pipx安装Poetry,Poetry的安装路径是什么?
当使用 pipx 安装 Poetry 时,pipx 会将 Poetry 安装到一个独立的虚拟环境中,并将其可执行文件链接到一个集中的目录中。以下是 pipx 安装 Poetry 时的路径信息: 1. Poetry 的安装路径 pipx 会为每个工具(如 Poetry)创…...

详解状态模式
引言 水有固态、液态、气态三种状态,在不同条件下这三种状态可以相互转化。同样在软件设计中,有些对象也有不同的状态,不同状态的行为不同,状态模式就是用来处理这种情况的。 1.概念 状态模式(State Pattern):允许一个…...

能否通过蓝牙建立TCP/IP连接来传输数据
前言: 最近在做一个项目时,产生了一个疑问:能否通过蓝牙建立TCP/IP连接来传输数据 查阅了一些文章,可以得出结论:不行 下面是我截取的两篇个人认可的文章的回答: 文章一: 蓝牙是一种短距离无…...

uniapp mqttjs 小程序开发
在UniApp中集成MQTT.js开发微信小程序时,需注意平台差异、协议兼容性及消息处理等问题。以下是关键步骤与注意事项的综合指南: 一、环境配置与依赖安装 安装MQTT.js 推荐使用兼容性较好的版本:mqtt4.1.0(H5和小程序兼容性最佳&…...

爬虫工程师分享:获取京东商品详情SKU数据的技术难点与攻破方法
在电商数据领域,京东商品详情页的SKU数据是许多爬虫工程师的目标。这些数据包含了商品的价格、库存、规格等关键信息,对于市场分析、价格监控等应用场景至关重要。然而,获取这些数据并非易事,京东作为国内电商巨头,其反…...

数据库操作与数据管理——Rust 与 SQLite 的集成
第六章:数据库操作与数据管理 第一节:Rust 与 SQLite 的集成 在本节中,我们将深入探讨如何在 Rust 中使用 SQLite 数据库,涵盖从基本的 CRUD 操作到事务处理、数据模型的构建、性能优化以及安全性考虑等方面。SQLite 是一个轻量…...