您现在的位置是:首页 >技术教程 >go破冰之旅·12·大行其道的结构体网站首页技术教程
go破冰之旅·12·大行其道的结构体
只有干货,把各种玩法一次性带给你!
上文中,我们以大量篇幅分析了切片扩容:切片的扩容
本文我们来看看go中的结构体如何play。
目录
初识结构体
结构体就是一组属性及其类型的集合,你可以在一个结构体中定义想定义的任何类型的属性。
通过一个结构体,可以定义或描述一个事物及其属性集合。
关于命名
重点在于首字母的大小写。其余命名和go中其它如包的命名等规范一致。
首字母大写时,可公开调用;
首字母小写时,不可公开调用。公开调用是指可在不同包之间进行调用,不可公开就是只能同包内调用。
定义、实例化、属性操作
举个例子,对于现实中的“人”,具有姓名、年龄、性别、爱好等属性,这里我们用一个结构体来描述一下:
// 使用type关键字,People为结构体的名称
type People struct {
// 括号里是一个个属性的定义
Name string
Age int8
Gender int8 // 0:男,1:女
Hobby []string // 一个人可能有多个爱好
}
这样就“抽象”出来了,这个结构体就是一个“模板”,用这个“模板”我们可以制造出该模板的实例化对象。
接着我们创建它的对象并将多个“People”组装:
// 实例化3个对象,设置每个对象的自有值
p1 := People{Name: "name1", Age: 99, Gender: 0, Hobby: []string{"play games", "read"}}
p2 := People{"name2", 99, 1, []string{"read"}}
p3 := People{"name3", 97, 0, []string{"play ball", "do sports"}}
// 使用对象的某个属性
fmt.Println(p1.Name)
// 将p2的年龄改为98
p2.Age = 98
// 定义一个People列表,把这三个People对象加进来
var peoples = []People{
p1, p2, p3,
}
for _, people := range peoples {
fmt.Printf("My name is %s, i like %s
", people.Name, strings.Join(people.Hobby, "、"))
}
/*
My name is name1, i like play games、read
My name is name2, i like read
My name is name3, i like play ball、do sports
*/
结构体嵌套
结构体也可以多个嵌套哦!
下面有一个学生结构体,这需要使用People及其属性,怎样才能避免把People里的属性不用在学生里再次重复定义一遍呢?
s1 := Student{
People: People{"stu1", 8, 1, []string{"read"}},
StuID: 1, Grade: 2, Class: 1,
}
s2 := Student{
People: People{"stu2", 8, 0, []string{"do sports"}},
StuID: 2, Grade: 2, Class: 2,
}
var students []Student
students = append(students, s1, s2)
for _, stu := range students {
fmt.Printf("My name is %s, i like %s, id is %d, grade %d class %d
",
stu.Name, strings.Join(stu.Hobby, "、"), stu.StuID, stu.Grade, stu.Class)
}
后面再有教师、医生、律师等其他职业的结构体要抽象,只需把People这个基本结构嵌套进去即可,因为这个基本结构就包含了职业这一类对象的基本属性。
你可以称之为“结构体继承”,但建议不要以面向对象的相关思想来在go中套用,每个语言都有其特色,上述的嵌套是为了让共用的、基本的属性更便捷的在新结构体中进行表述。
关于属性首字母的大小写
基于结构体首字母大写的前提下,属性首字母小写时,即使另一个包可以调用到该结构体,但到了调用其中的首字母小写的属性时就无法调用了。
关于json的转换
另外,属性首字母大写时,转换json无问题,首字母小写时转换出来的json没有该字段。
我们将上述People结构体的Gender属性进行修改,首字母改为小写,然后进行json转换:
// 定义一个待转换的对象
p1 := People{Name: "name1", Age: 99, gender: 1, Hobby: []string{"play games", "read"}}
bs, _ := json.Marshal(p1)
fmt.Println(string(bs))
// {"Name":"name1","Age":99,"Hobby":["play games","read"]}
可以看到得到的json字符串中gender字段消失了。
如要在json中包含全部字段,首字母定义时是大写但想让转换出的json字段为小写时,还可通过结构体的tag(标签)来自定义,比如我们让gender字段转换后为小写:
type People struct {
// 括号里是一个个属性的定义
Name string
Age int8
Gender int8 `json:"gender"` // 0:男,1:女
Hobby []string // 一个人可能有多个爱好
}
再次以同样的方式:
p1 := People{Name: "name1", Age: 99, Gender: 1, Hobby: []string{"play games", "read"}}
bs, _ := json.Marshal(p1)
fmt.Println(string(bs))
// {"Name":"name1","Age":99,"gender":1,"Hobby":["play games","read"]}
可以看到gender显示为小写。
结构体指针与参数传递
对于结构体指针,同样我们使用上面的People来阐述。
// 定义结构体指针,表示p1是*People类型的对象
var p1 *People
// 实例化(&取址符放在结构体前面),此时p1指针变量存储的是该对象的内存地址
p1 = &People{Name: "name1", Age: 99, Gender: 1, Hobby: []string{"play games", "read"}}
// 假设有一个peoples切片,是People类型,而不是*People类型
var peoples []People
// 现在要把p1追加进来
peoples = append(peoples, *p1) // 通过*来解引用,把这个内存地址对应的p1的值就可以取出来
// 访问/调用属性,注意并不是*p1.Name
fmt.Println(p1.Name) // name1
结构体对象作为参数传递
我们以一个函数修改结构体对象的name属性为需求,分别有这两个函数:
func updateName0(p People){
p.Name = "new"
}
func updateName1(p *People){
p.Name = "new"
}
这两个区别是,前者传的参数是结构体对象,函数中修改的是副本的属性,外界对象并未变化,而后者传的是结构体指针,在通过访问属性来修改时可以正常修改,测试数据:
p1 := People{Name: "name1", Age: 99, Gender: 1, Hobby: []string{"play games", "read"}}
updateName0(p1)
fmt.Println(p1)
updateName1(&p1) // 将p1的内存地址传进去
fmt.Println(p1)
结果不出所料:
{name1 99 1 [play games read]}
{new 99 1 [play games read]}
这和前文数组与切片:数组作为参数传递 情形如出一辙。
感谢支持,再会!