Go学习第八章——面向“对象”编程(入门——结构体与方法)
Go面向“对象”编程(入门——结构体与方法)
- 1 结构体
- 1.1 快速入门
- 1.2 内存解析
- 1.3 创建结构体四种方法
- 1.4 注意事项和使用细节
- 2 方法
- 2.1 方法的声明和调用
- 2.2 快速入门案例
- 2.3 调用机制和传参原理
- 2.4 注意事项和细节
- 2.5 方法和函数区别
- 3 工厂模式
Golang语言面向对象编程说明:
- Golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说Golang支持面向对象编程特性是比较准确的。
- Golang没有类(class),Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解Gelang是基于struct来实现OOP特性的。
- Golang面向对象编程非常简洁,去掉了传统OOP语言的方法重载、构造函数和析构函数、隐藏的this指针等等。
- Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样,比如继承:Golang没有extends 关键字,继承是通过匿名字段来实现。
- Golang面向对象(OOP)很优雅,OOP本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。后面同学们会充分体会到这个特点。也就是说在Golang中面向接口编程是非常重要的特性。
1 结构体
1.1 快速入门
假设定义结构体是老师,属性:名字,年龄,学校
type Teacher struct {//变量名字大写外界可以访问这个属性Name stringAge intSchool string
}
下面我们尝试赋值,并打印出来:
type Teacher struct {//变量名字大写外界可以访问这个属性Name stringAge intSchool string
}func main() {var tea1 Teachertea1.Name = "张三"tea1.Age = 28tea1.School = "深圳大学"fmt.Println("tea1=", tea1)fmt.Println("老师的信息如下:")fmt.Println("name=", tea1.Name)fmt.Println("age=", tea1.Age)fmt.Println("school=", tea1.School)
}
输出结果:
tea1= {张三 28 深圳大学}
老师的信息如下:
name= 张三
age= 28
school= 深圳大学
通过上面的案例和讲解可以看出:
- 结构体是自定义的数据类型,代表一类事务。
- 结构体变量(实例)是具体的,实际的,代表一个具体变量。
1.2 内存解析
在Go语言中,结构体的存储是在堆上。
当我们创建一个结构体实例时,它的内存将被分配在堆上。这意味着结构体的生命周期可以超出创建它的函数的生命周期。
例如,当我们使用new
关键字或make
函数创建一个结构体实例时,它将被分配在堆上。
type Person struct {Name stringAge int
}func main() {// 使用 new 关键字创建结构体实例p := new(Person)p.Name = "Alice"p.Age = 25// 使用 make 函数创建结构体实例(只适用于为某些类型分配内存,如 map、slice 和 channel)m := make(map[string]int)m["key"] = 42// 结构体实例被分配在堆上,可以在其他函数中继续使用anotherFunc(p)yetAnotherFunc(m)
}
这是因为在Go语言中,所有的变量都是通过传值而不是通过引用传递。在堆上分配结构体实例可以确保结构体数据的持久性和可用性。
Go语言中结构体的内存布局
Go语言中的结构体是一组值的集合
,这些值被存储在内存中的一段连续的区域
。结构体的内存布局取决于结构体中的成员变量顺序和类型
,以及对齐方式。
结构体的对齐方式
Go语言中使用的是一种称为Packing的方式
进行对齐。这种方式默认对齐到最多8字节的倍数,即8字节对齐。可以通过在结构体成员变量的后面添加逗号
和数字
的形式手动调节对齐方式。
type Person struct {Name stringAge intHeight float64
}func main() {var p Person// 获取结构体的大小size := unsafe.Sizeof(p)fmt.Printf("结构体大小:%d 字节\n", size)// 获取结构体字段的偏移量nameOffset := unsafe.Offsetof(p.Name)ageOffset := unsafe.Offsetof(p.Age)heightOffset := unsafe.Offsetof(p.Height)fmt.Printf("Name 字段的偏移量:%d 字节\n", nameOffset)fmt.Printf("Age 字段的偏移量:%d 字节\n", ageOffset)fmt.Printf("Height 字段的偏移量:%d 字节\n", heightOffset)// 结构体的对齐方式packed := unsafe.Alignof(p)fmt.Printf("结构体的对齐方式:%d 字节\n", packed)
}
输出结果:
结构体大小:20 字节
Name 字段的偏移量:0 字节
Age 字段的偏移量:8 字节
Height 字段的偏移量:12 字节
结构体的对齐方式:4 字节
在这个示例中,我们定义了一个Person
结构体,它包括名字、年龄和身高三个字段。我们通过unsafe
包中的函数来获取结构体的大小、字段的偏移量以及对齐方式。结构体的大小为20字节,字段的偏移量分别为0字节、8字节和12字节,结构体的对齐方式为4字节。
1.3 创建结构体四种方法
基本介绍
- 从概念或叫法上:结构体字段 = 属性 = field
- 字段是结构体的一个组成部分,一般是基本数据类型、数组、也可以是引用数据类型。
- 指针,slice和map的零值都是nil,即还没有分配空间。
方式一:直接声明
案例:var person Person
方式二:{}
案例:var person Person = Person{“Tom”, 18} => person := Person{“Tom”, 18}
方式三:&
案例:var person *Person = new (Person)
type Person struct {Name stringAge int
}func main() {var p *Person = new(Person)// (*p).Name = "smith" 标准写法// go设计者,为了程序使用方便,底层对下面这个做了优化,实现了这种简单的写法// 会给 p 加上 取值运算 =》 (*p).Name = "smith" p.Name = "smith" p.Age = 18fmt.Println(p)fmt.Println(*p)
}
输出结果:
&{smith 18}
{smith 18}
方式四:{}
案例:var person *Person = &Person{}
type Person struct {Name stringAge int
}func main() {var p *Person = &Person{}// 标准方式:(*person).Name = "scott"p.Name = "scott"p.Age = 18fmt.Println(p)
}
输出结果:&{scott 18}
1.4 注意事项和使用细节
- 结构体的所有字段在内存中是连续的,指针本身的地址是连续的,但是指向的地址不一定是连续的。
//结构体
type Point struct {x inty int
}//结构体
type Rect struct {leftUp, rightDown Point
}//结构体
type Rect2 struct {leftUp, rightDown *Point
}func main() {r1 := Rect{Point{1,2}, Point{3,4}} //r1有四个int, 在内存中是连续分布//打印地址fmt.Printf("r1.leftUp.x 地址=%p r1.leftUp.y 地址=%p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p \n", &r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)//r2有两个 *Point类型,这个两个*Point类型的本身地址也是连续的,//但是他们指向的地址不一定是连续r2 := Rect2{&Point{10,20}, &Point{30,40}} //打印地址fmt.Printf("r2.leftUp 本身地址=%p r2.rightDown 本身地址=%p \n", &r2.leftUp, &r2.rightDown)//他们指向的地址不一定是连续..., 这个要看系统在运行时是如何分配fmt.Printf("r2.leftUp 指向地址=%p r2.rightDown 指向地址=%p \n", r2.leftUp, r2.rightDown)
}
输出结果:
r1.leftUp.x 地址=0x9496080 r1.leftUp.y 地址=0x9496084 r1.rightDown.x 地址=0x9496088 r1.rightDown.y 地址=0x949608c
r2.leftUp 本身地址=0x948a038 r2.rightDown 本身地址=0x948a03c
r2.leftUp 指向地址=0x9496068 r2.rightDown 指向地址=0x94960a0
-
结构体之间可以转换,但是有要求,就是结构体的字段要完全一样(包括:名字、类型,个数)
type A struct {Num int }type B struct {Num int }func main() {var a Avar b Ba = A(b)fmt.Println(a, b) }
-
结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转。
type Student struct {Name stringAge int
}type Stu Studentfunc main() {var stu1 Studentvar stu2 Stu// stu2 = stu1 // 错误,系统认为这是两个不一样的类型stu2 = Stu(stu1)fmt.Println(stu1, stu2)
}
-
struct的每个字段上,可以写上一个标签tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化。
type Student struct {Name string `json:"name"` // 这里就是结构体的标签tagAge int `json:"age"` }func main() {// 1.创建一个student变量student := Student{"张三", 18}// 2.将monster变量序列化为json格式字符串jsonStr, err := json.Marshal(student) // 这里json.Marshal底层用到了反射if err != nil {fmt.Println("jsonStr报错")}fmt.Println("jsonStr:", jsonStr)fmt.Println("string(jsonStr):", string(jsonStr)) }
如果这里不加上标签,生成的json格式,就变成:
string(jsonStr): {"Name":"张三","Age":18}
会发现,这里的Name的首字母是大写,这又是不可避免,因为小写就调用不了
所以这里通过标签,设置一个别名,底层用了反射解决这个问题~~~
输出结果:
string(jsonStr): {"name":"张三","age":18}
2 方法
在某些情况下,我们需要声明(定义)方法。比如Person结构体:除了有一些字段外(年龄,姓名…),Person结构体还有一些行为。比如:可以说好,跑步,学习等,还可以做算术题。这时就要用方法才能完成
Golang中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅只是struct。
2.1 方法的声明和调用
type A struct {Num int
}func (a A) test() {fmt.Println(a.Num)
}
对上面的语法说明:
- func (a A) test() {} 表示A结构体有一方法,方法名为 test
- (a A) 体现test方法是和A类型绑定的
type Person struct {Name string
}func (p Person) test() {p.Name = "Tom"fmt.Println("test():", p.Name)
}func main() {person := Person{Name: "张三"}person.test() // 调用方法fmt.Println("main p.Name=", person.Name)
}
输出结果:
test(): Tom
main p.Name= 张三
对上面的总结:
-
test方法和Person类型绑定
-
test方法只能通过Person类型的遍历来调用,而不能直接调用,也不嫩更实用其他类型变量来调用。
-
func (p Person) test() {} 这个p是它的副本,进行的是值传递,与函数很像。
2.2 快速入门案例
-
给Person结构体添加speak 方法,输出 xxx是一个好人
type Person struct{Name string }//给Person结构体添加speak 方法,输出 xxx是一个好人 func (p Person) speak() {fmt.Println(p.Name, "是一个goodman~") }
-
给Person结构体添加jisuan 方法,可以计算从 1+…+1000的结果,
type Person struct {Name string }func (p Person) jisuan() {res := 0for i := 1; i <= 1000; i++ {res += i}fmt.Println(p.Name, "计算的结果是=", res) }func main() {p := Person{Name: "张三"}p.jisuan() // 输出:张三 计算的结果是= 500500 }
-
给Person结构体jisuan2 方法,该方法可以接收一个参数n,计算从 1+…+n 的结果
type Person struct {Name string }// 给Person结构体jisuan2 方法,该方法可以接收一个参数n,计算从 1+..+n 的结果 func (p Person) jisuan2(n int) {res := 0for i := 1; i <= n; i++ {res += i}fmt.Println(p.Name, "计算的结果是=", res) }func main() {p := Person{Name: "张三"}p.jisuan2(10) // 输出:张三 计算的结果是= 55 }
-
给Person结构体添加getSum方法,可以计算两个数的和,并返回结果
type Person struct {Name string }// 给Person结构体添加getSum方法,可以计算两个数的和,并返回结果 func (p Person) getSum(n1 int, n2 int) int {return n1 + n2 }func main() {p := Person{Name: "张三"}sum := p.getSum(1, 1)fmt.Printf("%v sum=%v", p.Name, sum) //输出:张三 sum=2 }
2.3 调用机制和传参原理
说明:方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当作实参也传递给方法。下面举例说明:
案例:
type Person struct {Name string
}// 给Person结构体添加getSum方法,可以计算两个数的和,并返回结果
func (p Person) getSum(n1 int, n2 int) int {return n1 + n2
}func main() {p := Person{Name: "张三"}n1 := 10n2 := 20res := p.getSum(n1, n2)fmt.Println("res=", res)
}
- 在通过一个变量去调用方法时,其调用机制和函数一样
- 不一样的地方是,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果是引用类型,则进行地质拷贝)
2.4 注意事项和细节
- 结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
- 如果程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
- Golang中的方法作用在指定的数据类型上的(即: 和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct,比如int ,float32等都可以有方法
- 方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包4访问,方法首字母大写,可以在本包和其它包访问。
- 如果一个类型实现了String这个方法,那么fmt.Println默认会调用这个变量的String()进行输出
上面这些注意事项都比较简单,就代码展示一下最后一条:
type Student struct {Name stringAge int
}// 给*Student实现方法String()
func (stu *Student) String() string {str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)return str
}func main() {//定义一个Student变量stu := Student{Name: "tom",Age: 20,}//如果你实现了 *Student 类型的 String方法,就会自动调用fmt.Println(stu)fmt.Println(&stu)
}
输出结果:
{tom 20}
Name=[tom] Age=[20]
2.5 方法和函数区别
- 调用方式不一样
- 函数的调用方式:函数名(实参列表)
- 方法的调用方式:变量.方法名(实参列表)
- 数据传递的限制不一样
- 对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然。
- 对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的遍历调用方法,反过来同样也可以。
这一点很容易迷糊,下面用段代码解释一下:
type Person struct {Name string
}//函数
//对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然func test01(p Person) {fmt.Println(p.Name)
}func test02(p *Person) {fmt.Println(p.Name)
}//对于方法(如struct的方法),
//接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以func (p Person) test03() {p.Name = "jack"fmt.Println("test03() =", p.Name) // jack
}func (p *Person) test04() {p.Name = "mary"fmt.Println("test03() =", p.Name) // mary
}func main() {p := Person{"tom"}test01(p)test02(&p)p.test03()fmt.Println("main() p.name=", p.Name) // tom(&p).test03() // 从形式上是传入地址,但是本质仍然是值拷贝fmt.Println("main() p.name=", p.Name) // tom(&p).test04()fmt.Println("main() p.name=", p.Name) // maryp.test04() // 等价 (&p).test04 , 从形式上是传入值类型,但是本质仍然是地址拷贝
}
输出结果:
tom
tom
test03() = jack
main() p.name= tom
test03() = jack
main() p.name= tom
test03() = mary
main() p.name= mary
test03() = mary
从代码会发现,仅管传递的是一个地址,但是编译器进行了内部优化,实际上还是值传递,只是支持这种写法,但并不是进行一个地址值的修改。
3 工厂模式
说明:Golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题
相当于,这个工厂模式,就是以前构造函数的功能。
看个需求:
一个结构体的声明如下:
package modeltype Student struct {Name string
}
因为这里的 Student 的首字母S是大写的,如果我们想在其他包串接Student的实例(比如:main包),引入model包后,就可以直接创建Student结构体的变量(实例)。
但是问题来了,如果搜字母是小写的,比如是: type student struct {…}就不行了,咋办?-》工厂模式来解决,
Model包
package modeltype student struct {name stringscore float64
}// 因为student结构体首字母是小写,因此是只能在model使用
// 通过工厂模式来解决
func NewStudent(n string, s float64) *student {return &student{name: n,score: s,}
}// 如果score字段首字母小写,则,在其他包不可以直接方法,我们可以提供一个方法
func (s *student) GetScore() float64 {return s.score
}// 如果score字段首字母小写,则,在其他包不可以直接方法,我们可以提供一个方法
func (n *student) GetName() string {return n.name
}
main包
import ("GoStudy_Day1/model""fmt"
)func main() {// 定student结构体是首字母小写,我们可以通过工厂模式来解决stu := model.NewStudent("Tom", 88.8)fmt.Println(stu)fmt.Println(*stu)// fmt.Println("name=", stu.name) // 报错,因为是私密的fmt.Println("name=", stu.GetName())fmt.Println("score=", stu.GetScore())
}
输出结果:
&{Tom 88.8}
{Tom 88.8}
name= Tom
score= 88.8
其实,这就看出来,就是Java的GetSet,绕了一圈,又回来啦~~~!!!!!
相关文章:

Go学习第八章——面向“对象”编程(入门——结构体与方法)
Go面向“对象”编程(入门——结构体与方法) 1 结构体1.1 快速入门1.2 内存解析1.3 创建结构体四种方法1.4 注意事项和使用细节 2 方法2.1 方法的声明和调用2.2 快速入门案例2.3 调用机制和传参原理2.4 注意事项和细节2.5 方法和函数区别 3 工厂模式 Gola…...

「滚雪球学Java」:方法函数(章节汇总)
🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!…...

数据分析必备原理思路(二)
文章目录 三、主流的数据分析方法与框架使用1. 五个数据分析领域关键的理论基础(1)大数定律(2)罗卡定律(3)幸存者偏差(4)辛普森悖论(5)帕累托最优(…...

分布式ID系统设计(1)
分布式ID系统设计(1) 在分布式服务中,需要对data和message进行唯一标识。 比如订单、支付等。然后在数据库分库分表之后也需要一个唯一id来表示。 基于DB的自增就肯定不能满足了。这个时候能够生成一个Global的唯一ID的服务就很有必要我们姑且把它叫做id-server 。…...

机器学习(python)笔记整理
目录 一、数据预处理: 1. 缺失值处理: 2. 重复值处理: 3. 数据类型: 二、特征工程: 1. 规范化: 2. 归一化: 3. 标准化(方差): 三、训练模型: 如何计算精确度,召…...

微客云霸王餐系统 1.0 : 全面孵化+高额返佣
1、业务简介。业务模式是消费者以5-10元吃到原价15-25元的外卖,底层逻辑是帮外卖商家做推广,解决新店基础销量、老店增加单量、品牌打万单店的需求。 因为外卖店的平均生命周期只有6个月,不断有新店愿意送霸王餐。部分老店也愿意做活动&…...

极智开发 | Hello world for Manim
欢迎关注我的公众号 [极智视界],获取我的更多经验分享 大家好,我是极智视界,本文分享一下 Hello world for Manim。 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码和资源下载,链接:https://t.zsxq.com/0aiNxERDq Manim 是什么呢?Manim 是一个用于创…...

【云上探索实验室-码上学堂】免费学习领好礼!
走过路过,不要错过!上云AI三步走,学着课程奖品有! 亚马逊云科技又放福利了,为了让同学们更快入手Amazon CodeWhisperer,官方推出《云上探索实验室-码上学堂》活动,作为一名Amazon CodeWhisperer…...

Flutter最全面试题大全
在理解这些问题之前,建议看一下Flutter架构原理,如下链接: https://blog.csdn.net/wang_yong_hui_1234/article/details/130427887?spm1001.2014.3001.5501 目录 一. 有个Text节点,由于文字内容过多,发生了溢出错误&…...

Linux---(四)权限
文章目录 一、shell命令及运行原理1.什么是操作系统?2.外壳程序3.用户为什么不直接访问操作系统内核?4.操作系统内核为什么不直接把结果显示出来?非要加外壳程序?5.shell理解重点总结(1)shell是什么?&…...

财务RPA机器人真的能提高效率吗?
财务部门作为一个公司的管理职能部门承担着一个公司在商业活动中各个方面的重要职责。理论上来说,一个公司的财务部门的实际工作包含但不限于对企业的盈亏情况进行评估、对风险进行预测、通过数据分析把握好公司的财务状况、税务管理等。 然而,实际上在…...

国产信号发生器 1442/1442A射频信号发生器
信号发生器 1442/A射频信号发生器 1442系列射频信号发生器是一款针对通信、电子等射频应用而设计开发的产品。覆盖了所有的常用射频频段。它采用模块化结构设计,全中文界面、大屏幕菜单控制,其输出信号相位噪声极低,频率分辨率和准确度高&am…...

Kafka与Spark案例实践
1.概述 Kafka系统的灵活多变,让它拥有丰富的拓展性,可以与第三方套件很方便的对接。例如,实时计算引擎Spark。接下来通过一个完整案例,运用Kafka和Spark来合理完成。 2.内容 2.1 初始Spark 在大数据应用场景中,面对…...

山西电力市场日前价格预测【2023-10-27】
日前价格预测 预测说明: 如上图所示,预测明日(2023-10-27)山西电力市场全天平均日前电价为347.06元/MWh。其中,最高日前电价为618.09元/MWh,预计出现在18: 15。最低日前电价为163.49元/MWh,预计…...

centos7安装redis(包含各种报错)
本文主要介绍如果在Centos7下安装Redis。 1.安装依赖 redis是由C语言开发,因此安装之前必须要确保服务器已经安装了gcc,可以通过如下命令查看机器是否安装: gcc -v如果没有安装则通过以下命令安装: yum install -y gcc2.下载r…...

使用GoQuery实现头条新闻采集
概述 在本文中,我们将介绍如何使用Go语言和GoQuery库实现一个简单的爬虫程序,用于抓取头条新闻的网页内容。我们还将使用爬虫代理服务,提高爬虫程序的性能和安全性。我们将使用多线程技术,提高采集效率。最后,我们将展…...

“一带一路”十周年:用英语讲好中华传统故事
图为周明霏小选手 2023年是“一带一路”倡议提出十周年。十年来,中国的“友谊圈”已经扩展到亚洲、非洲、欧洲、大洋洲和拉丁美洲,这一倡议已经成为提升我国文化软实力、传播中华传统文化的重要策略和途径之一。在这个广阔的交流平台上,使用…...

机器视觉兄弟们还有几个月就拿到年终奖了,但我想跑路了
大聪明的我一般会把年终奖拿了,再走。听说有人还没有年终奖,太伤心了,赶紧跑吧。注意,机器视觉小白不要轻举妄动。 今年太难了,真的是让人很难过,很不爽,很不舒服。 公司难,机器视…...

base_lcoal_planner的LocalPlannerUtil类中getLocalPlan函数详解
本文主要介绍base_lcoal_planner功能包中LocalPlannerUtil类的getLocalPlan函数,以及其调用的transformGlobalPlan函数、prunePlan函数的相关内容 一、getLocalPlan函数 getLocalPlan函数的源码如下: bool LocalPlannerUtil::getLocalPlan(const geomet…...

elasticSearch put全局更新和单个字段更新语法
1、如下:更新改类型未doc(文档)的全局字段数据 注意:如果你使用的是上面的语句,但是只写了id和title并赋值,图片上其他字段没有填写,执行命令后,则会把原文档中的其他字段都给删除了,你会发现查…...

记录一次时序数据库的实战测试
0x1.前言 本文章仅用于信息安全防御技术分享,因用于其他用途而产生不良后果,作者不承担任何法律责任,请严格遵循中华人民共和国相关法律法规,禁止做一切违法犯罪行为。文中涉及漏洞均以提交至教育漏洞平台。 0x2.背景 在某…...

HTML中文本框\单选框\按钮\多选框
<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <h1>登录注册</h1> <form action"第一个网页.html" method"post&quo…...

解释器模式——化繁为简的翻译机
● 解释器模式介绍 解释器模式(Interpreter Pattern)是一种用的比较少的行为型模式,其提供了一种解释语言的语法或表达的方式,该模式定义了一个表达式接口,通过该接口解释一个特定的上下文。在这么多的设计模式中&…...

【凡人修仙传】定档,四女神出场,韩立遭极阴岛陷阱,蛮胡子亮相
【侵权联系删除】【文/郑尔巴金】 距离凡人修仙传动画星海飞驰序章完结,已经过去了两个月的时间,相信大家等待的心情相当难熬,而且也愈发期待韩立结丹后在乱星海发生的故事。按照官方当初立下的FLAG,新年番动画即将在金秋十一月上…...

【解决】设置pip安装依赖包路径默认路径在conda路径下,而不是C盘路径下
【解决】设置pip安装依赖包路径默认路径在conda路径下,而不是C盘路径下 问题描述 在win11下安装miniconda,在conda环境里使用pip安装,依赖包总是安装到C盘路径,如 C:\Users\Jimmy\AppData\Local\Programs\Python\Python311\Lib\…...

JoySSL-新兴国产品牌数字证书
随着我国对数据安全重视程度的不断提升,国产SSL证书越来越受到广大政府机关和企业的青睐,成为提升网站数据安全能力的重要技术手段。那么什么是国产SSL证书?国产SSL证书和普通SSL证书又有什么区别呢? 什么是国产SSL证书ÿ…...
kafka3.X基本概念和使用
kafka基本概念和使用 文章目录 kafka基本概念和使用 kafka的概念基本概念Kafka的使用 首先kafka的安装kafka的简单实用和理解搭建集群(3个节点)windows版本环境搭建 本文"kafka的概念"部分是在[初谈Kafka][ https://juejin.im/post/5a8e7f…...

用低代码平台代替Excel搭建进销存管理系统
目录 一、用低代码平台搭建系统 1.需求调研 2.基于痛点梳理业务流程 3.低代码实现 (1)基础资料模块 (2)采购管理模块 (3)销售管理模块 (4)库存管理模块 (5&…...

Redis和Memcached网络模型详解
1. Redis单线程单Reactor网络模型 1.1 redis单线程里不能执行十分耗时的流程,不然会客户端响应不及时 解决方法一: beforesleep里删除过期键操作若存在大量过期键时,会耗费大量时间,redis采用的策略之一就是采用timelimit方案超过…...

二叉搜索树的实现(递归方式)
目录 实现思路 插入操作 删除操作 完整代码 测试案例 总结 二叉搜索树(Binary Search Tree,BST)是一种常用的数据结构,它具有以下特点: 左子树上所有节点的值均小于它的根节点的值右子树上所有节点的值均大于它的…...