golang 结构体struct
1.golang语言面向对象编程说明
1)golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言,所以我们说golang支持面向对象编程特效是比较准确的。
2)golang没有类(class),Go语言的结构体(struct)和其他编程语言的类(class)有同等的地位,可以理解golang是基于struct来实现OOP特性的。
3)golang面向对象编程非常简洁,去掉了传统OOP语言中的继承,方法重载,狗仔函数和解析函数,隐藏this指针等等
4)golang仍然有面向对象编程的继承,封装和多态的特性,只是实现方式和其他OOP语言不一样,比如继承没有extends关键字,继承是通过匿名字段来实现的
5)golang面向对象(OOP)很优雅,OOP本身就是语言类型系统的一部分,通过接口(interface)关联,耦合性低,也非常灵活,也就是说golang中面向接口编程是非常重要的特性。
6)结构体是值类型
2.张老太养了两只猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年100岁,花色。请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。如果用户输入的小猫名字错误,则显示 张老太没有这只猫
//使用map的缺点,子map的数据类型map[srting]string 只能定义一种数据类型,而年龄和花色是不同的数据类型。
//使用变量和数组都不利于数据管理和维护。因为名字,年龄,颜色都是属于一只猫,但是这里是分开保存的。
//如果希望对一只猫的属性(名字,年龄,颜色)进行操作(绑定方法),也不好处理
map实现
func cxcat(cat map[string]map[string]string, name string) { if name != "小白" && name != "小花" { println("错误,张老太没有这只猫") } else { fmt.Println(cat[name]) } } func main() { //张老太养了两只猫:一只名字叫小白,今年3岁,白色。还有一只叫小花,今年100岁,花色。 //请编写一个程序,当用户输入小猫的名字时,就显示该猫的名字,年龄,颜色。 //如果用户输入的小猫名字错误,则显示 张老太没有这只猫 //使用map的缺点,子map的数据类型map[srting]string 只能定义一种数据类型,而年龄和花色是不同的数据类型。 //使用变量和数组都不利于数据管理和维护。因为名字,年龄,颜色都是属于一只猫,但是这里是分开保存的。 //如果希望对一只猫的属性(名字,年龄,颜色)进行操作(绑定方法),也不好处理 var cat map[string]map[string]string = make(map[string]map[string]string) cat["小白"] = make(map[string]string, 2) cat["小白"]["age"] = "3" cat["小白"]["花色"] = "白色" cat["小花"] = make(map[string]string, 2) cat["小花"]["age"] = "100" cat["小花"]["花色"] = "花色" var name string fmt.Println("请输入小猫的名字:") fmt.Scanln(&name) cxcat(cat, name) }
结构体实现
type Cat struct { Name string Age int Color string } func main() { var cat1 Cat cat1.Name = "小白" cat1.Age = 3 cat1.Color = "白色" fmt.Printf("猫猫的信息如下:\n 名字为:%v\n 年龄为%v\n 颜色为:%v\n", cat1.Name, cat1.Age, cat1.Color) }
2.结构体声明
type 结构体名称 struct {
fieldname1 fieldtype1
fieldname2 fieldtype2
}
1)从概念或叫法上看:结构体字段 = 属性 = field 。如上面field1name 是字段名称 fieldtype是字段类型
2)字段是结构体的一个组成部分,一般是基本数据类型、数组,也可以是引用类型。
3)在创建一个结构体变量后,如果没有给字段赋值,都应对应一个零值,指针、slice和map的零值都是nil,即还没有分配空间,需要先make再使用。
4)不同结构体变量的字段是独立的,互不影响,一个结构体变量字段的更改,不影响另一个。结构体是值类型,默认为值拷贝。
type Person struct { Name string Age int Scores [5]float64 ptr *int slice []int map1 map[string]string } func main() { var p1 Person fmt.Println(p1) //使用slice,一定要先make p1.slice = make([]int, 10) p1.slice[0] = 100 //使用map,一定要先make p1.map1 = make(map[string]string) p1.map1["key1"] = "tom~" fmt.Println(p1)
}
type Monster struct { Name string Age int } func main() { var monster1 Monster monster1.Name = "牛魔王" monster1.Age = 36 monster2 := monster1 monster1.Name = "青牛怪" fmt.Println(monster1) fmt.Println(monster2) }
3.创建结构体实例
1)方式1-直接声明
var person Person
2) 方式2-{}
var person Person = Person{}
p2 := Person{}
3)方式3-& new出来的实例是指针
var person *Person = new (Person)
p2 := new(Person)
赋值
(*p2).num = 10 也可以写成p2.num = 10,golong的设计者为了简化操作在底层会对p2.num = 10进行处理,会给P2加上取值运算
fmt.Println(*p2)
4)方式4-{}
var person *Person = &Person{"Marry",60}
//person是一个指针
(*person).Name = "scott" 也可以写成person.Name = "scott",与上面同样设计者会在底层加取值运算
4.结构体的使用细节
1)结构体的所以字段在内存中是连续存在的
type Point struct { x int y int } type Rect struct { leftUp, rigntDown Point } func main() { r1 := Rect{Point{1, 2}, Point{3, 4}} //r1有四个int,在内存中是连续分布的 fmt.Printf("r1.leftUP.x的地址=%v,r1.leftUP.y的地址=%v,rigntDown.x的地址=%v,rigntDown.y的地址=%v", &r1.leftUp.x, &r1.leftUp.y, &r1.rigntDown.x, &r1.rigntDown.y) } //r1.leftUP.x的地址=0xc00001e020,r1.leftUP.y的地址=0xc00001e028,rigntDown.x的地址=0xc00001e030,rigntDown.y的地址=0xc00001e038
当struct为指针类型时,指针本身的地址是连续的,但是指针指向的地址不一定是连续的
type Rect2 struct { leftUp, rigntDown *Point } func main() { //r2有两个*Point类型,这两个*Point类型本身的地址是连续的 //但他们指向的地址不一定是连续的 r2 := Rect2{&Point{1, 2}, &Point{3, 4}} fmt.Printf("r2.leftUP本身的地址=%v, r2.rigntDown本身的地址=%v\n", &r2.leftUp, &r2.rigntDown) fmt.Printf("r2.leftUP指向的地址=%p, r2.rigntDown指向的地址=%p\n", r2.leftUp, r2.rigntDown) }//r2.leftUP本身的地址=0xc000010250, r2.rigntDown本身的地址=0xc000010258 r2.leftUP指向的地址=0xc000020090, r2.rigntDown指向的地址=0xc0000200a0
2)结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段名(名字,个数和类型)
type A struct { Num int } type B struct { Num int } func main() { var a A var b B a = A(b) fmt.Println(a, b) }
3)结构体进行type重新定义(相当于取别名),golang认为是新的数据类型,但是可以相互之间强转换
type Student struct { Name string Age int } type Stu Student func main() { var stu1 Student var stu2 Stu stu2 = Stu(stu1) //可以强转 fmt.Println(stu1, stu2) }
4)struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列号和反序列化。
对代码说明:将struct变量进行json处理
问题:json处理后的字段也是首字母大写,这样如果我们将json后的字符串返回给其他程序使用,如query,php等,那么可能他们不习惯这个命名方式,怎么办?
解决方案
1)将Monster的字段首字母小写,这个行不通,引用处理后,返回的是空字符串,因为json.Marshal相当于是在其他包访问Monster结构体,字段的首字母小写,就不能在其他包访问
2)使用tag标签来解决
type Monster struct { Name string `json:"name"` Age int8 `json:"age"` Skill string `json:"skill"` } func main() { //1.创建一个Monster变量 monster := Monster{"牛魔王", 80, "牛头拳"} //2.将monster变量序列号为json格式字符串 //encoding.json.Marshal返回v的json编码 jsonStr, err := json.Marshal(monster) if err != nil { fmt.Println("json处理错误", err) } fmt.Println("jsonStr:", string(jsonStr)) }//jsonStr: {"name":"牛魔王","age":80,"skill":"牛头拳"}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?