九、结构体
在实际开发中,我们可以将一组类型不同的、但是用来描述同一件事物的变量放到结构体中。例如,在校学生有姓名、年龄、身高、成绩等属性,学了结构体后,我们就不需要再定义多个变量了,将它们都放到结构体中即可。
在Go语言中,结构体承担着面向对象语言中类的作用。Go语言中,结构体本身仅用来定义属性。还可以通过接收器函数来定义方法,使用内嵌结构体来定义继承。这样使用结构体相关操作Go语言就可以实现OOP
面向对象编程了。
9.1、声明结构体
Go语言通过type
和struct
关键字声明结构体,格式如下:
type 类型名 struct {
// 标识结构体的类型名,在同一个包内不能重复
字段1 字段2类型 // 字段名必须唯一
字段2 字段2类型
...
}
Go语言结构体(Struct
)从本质上讲是一种自定义的数据类型,只不过这种数据类型比较复杂,是由 int、char、float 等基本类型组成的。你可以认为结构体是一种聚合类型
。
type Student struct {
sid int
name string
age int8
course []string // 选秀课程
}
Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性。结构体成员,也可称之为成员变量,字段,属性。属性要满足唯一性。
同类型的变量也可以写在一行,用逗号隔开
type Book struct {
title,author string
price int
}
9.2、结构体的实例化
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存,因此必须在定义结构体并实例化后才能使用结构体的字段。实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的内存是完全独立的。
实例化方式包括如下几种。
9.2.1、声明结构体变量再赋值
结构体本身是一种类型,可以像整型、字符串等类型一样,以 var 的方式声明结构体即可完成实例化。
package main
import "fmt"
type Student struct {
sid int
name string
age int8
course []string // 选修类型
}
func main() {
// 声明一个结构体对象,值类型,默认开辟空间,字段赋予零值
var s Student
fmt.Println("s:", s)
// 要访问结构体成员,需要使用点好 .操作符
fmt.Println(s.name)
// 更改成员变量的值
s.name = "summer"
fmt.Println(s.name)
// s.corese[0] = "chinese" //结果;如何调整
}
1、结构体属于值类型,即var声明后会像整形字符串一样创建内存空间。
2、创建结构体对象如果没有给字段赋值,则默认零值(字符串默认 "",数值默认0,布尔默认false,切片和map默认nil对象)
结构体的内存存储:
package main
import "fmt"
type Student struct {
sid int
name string
age int8
course []string // 选修课程
}
func main() {
// 声明一个结构体对象,值类型,默认开辟空间,字段赋予零值
var s Student
fmt.Println("s:", s)
s.sid = 1001
s.name = "Junne"
s.age = 25
s.course = []string{"chinese", "math", "english"}
fmt.Printf("%p\n", &s)
fmt.Printf("%p\n", &(s.sid))
fmt.Printf("%p\n", &(s.name))
fmt.Printf("%p\n", &(s.age))
fmt.Printf("%p\n", &(s.course)) //切片24个字节
}
学习过值类型和引用类型,知道值类型是变量对应的地址直接存储值,而引用类型是变量对应地址存储的是地址。因为结构体因为是值类型,所以p的地址与存储的第一个值的地址是相同的,而后面每一个成员变量的地址是连续的。
9.2.2、实例化之 结构体
// (1) 方式1
s1 := Student{}
s1.sid = 1001
s1.name = "yuan"
// (2) 方式2:键值对赋值
s2 := Student{sid: 1002, name: "rain", course: []string{"chinese", "math", "english"}}
fmt.Println(s2)
// (3) 方式3:多值赋值
s3 := Student{1003, "alvin", 22, []string{"chinese", "math", "english"}}
fmt.Println(s3)
1、结构体可以使用“键值对”(Key value pair)初始化字段,每个“键”(Key)对应结构体中的一个字段,键的“值”(Value)对应字段需要初始化的值。键值对的填充是可选的,不需要初始化的字段可以不填入初始化列表中,走默认值。
2、多值初始化方式必须初始化结构体的所有字段且每一个初始值的填充顺序必须与字段在结构体中的声明顺序一致。
3、键值对与值列表的初始化形式不能混用。
9.2.3、实例化之&结构体
package main
import "fmt"
type Student4 struct {
sid int
name string
age int8
course []string // 选修课程
}
func CourseInit(stu Student4) {
stu.course = []string{"chinese", "math", "english"}
fmt.Println(stu)
}
func CourseInit2(stu *Student4) {
(*stu).course = []string{"chinese", "math", "english"}
}
func main() {
// 案例
s1 := Student4{sid: 1001, name: "summer", age: 25}
s2 := s1 // 值拷贝
fmt.Println(s2)
s1.age = 100
fmt.Println(s2.name)
// 如果希望s3的值跟岁s2保持一致怎么实现
s3 := &s1 // var s4 *Student = &s2
s1.age = 100
fmt.Println((*s3).age)
fmt.Println(s3.age)
// 案例2
var s4 = Student4{sid: 1001, name: "June", age: 24}
CourseInit(s4)
fmt.Println("s报的课程:", s4.course)
// 怎么能初始化成功呢?
var s5 = &Student4{sid: 1001, name: "summer", age: 26}
CourseInit2(s5)
fmt.Println("s报的课程:", (*s5).course)
fmt.Println("s报的课程:", s5.course)
}
在Go语言中,结构体指针的变量可以继续使用
.
,这是因为Go语言为了方便开发者访问结构体指针的成员变量可以像访问结构体的成员变量一样简单,使用了语法糖(Syntactic sugar)技术,将instance.Name
形式转换为(*instance).Name
。
9.2.4、实例化之 new(结构体)
Go语言中,还可以使用 new 关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。使用 new 的格式如下:其中:
instance := new(T)
其中:
- T 为类型,可以是结构体、整型、字符串等。
instance
:T 类型被实例化后保存到instance
变量中,instance
的类型为 *T,属于指针。
s := new(Student) // &Student{}
fmt.Println(reflect.TypeOf(s)) // *Student
fmt.Println(s) // *Student
s.name = "summer"
fmt.Println((*s).name)
fmt.Println(s.name)
9.4、模拟构造函数
Go语言没有构造函数,但是我们可以使用结构体初始化的过程来模拟实现构造函数。
package main
import "fmt"
type Student struct {
sid int
name string
age int8
course []string // 选秀课程
}
func NewStudent(sid int, name string, age int8, course []string) *Student {
return &Student{
sid: sid,
name: name,
age: age,
course: course,
}
}
func main() {
s := NewStudent(1001, "yuan", 32, nil)
fmt.Println(s)
}
9.5、方法接收器
Go语言中的方法(Method)
是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)
。
方法的定义格式如下:
func (接收者变量 接收者变量类型) 方法名(参数列表) (返回参数) {
函数体
}
其中,
- 接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写,而不是
self
、this
之类的命名。例如,Person
类型的接收者变量应该命名为p
,Connector
类型的接收者变量应该命名为c
等。 - 接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
- 方法名、参数列表、返回参数:具体格式与函数定义相同。
package main
import "fmt"
type Player struct {
Name string
HealthPoint int
Level int
NowPosition []int
Prop []string
}
func NewPlayer(name string, hp int, level int, np []int, prop []string) *Player {
return &Player{
name,
hp,
level,
np,
prop,
}
}
func (p Player) attack() {
fmt.Printf("%s发起攻击!\n", p.Name)
}
func (p *Player) attacked() {
fmt.Printf("%s被攻击!\n", p.Name)
p.HealthPoint -= 10
fmt.Println(p.HealthPoint)
}
func (p *Player) buyProp(prop string) {
p.Prop = append(p.Prop, prop)
fmt.Printf("%s购买道具!\n", p.Name)
}
func main() {
player := NewPlayer("summer", 100, 100, nil, nil)
player.attack()
player.attacked()
fmt.Println(player.HealthPoint)
player.buyProp("魔法石")
fmt.Println(player.Prop)
}
1、官方定义:Methods are not mixed with the data definition (the structs): they are orthogonal to types; representation(data) and behavior (methods) are independent
2、方法与函数的区别是,函数不属于任何类型,方法属于特定的类型。
9.6、匿名字段
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。
package main
import "fmt"
type Person struct {
string
int
}
func main() {
p1 := Person{
"summer",
25,
}
fmt.Printf("%#v\n", p1) main.Person{string:"summer", int:25}
fmt.Println(p1.string, p1.int)
}
结构体也可以作为匿名字段使用
package main
import "fmt"
type Addr struct {
country string
province string
city string
}
type Person struct {
name string
age int
Addr
}
func main() {
p1 := Person{
"summer",
18,
Addr{"中国", "广东省", "深圳"},
}
fmt.Printf("%#v\n", p1) //main.Person{string:"深圳", int:18}
fmt.Println(p1.name, p1.age) // summer 18
fmt.Println(p1.Addr)
fmt.Println(p1.Addr.country) // 中国
fmt.Println(p1.city) // 深圳
}
当结构体中有和匿名字段相同的字段时,采用外层优先访问原则
9.7、结构体的继承
package main
import "fmt"
//Animal 动物
type Animal struct {
name string
}
func (a *Animal) eat() {
fmt.Printf("%s is eating!\n", a.name)
}
func (a *Animal) sleep() {
fmt.Printf("%s is sleeping!\n", a.name)
}
// Dog 类型
type Dog struct {
Kind string
*Animal //通过嵌套匿名结构体实现继承
}
func (d *Dog) bark() {
fmt.Printf("%s is barking ~\n", d.name)
}
// Cat 类型
type Cat struct {
*Animal
}
func (c *Cat) climbTree() {
fmt.Printf("%s is climb tree ~\n", c.name)
}
func main() {
d1 := &Dog{
Kind: "金毛",
Animal: &Animal{ //注意嵌套的是结构体指针
name: "旺财",
},
}
d1.eat()
d1.bark()
c1 := &Cat{
Animal: &Animal{
name: "喵喵",
},
}
c1.sleep()
c1.climbTree()
}
9.8、序列化
序列化: 通过某种方式把数据结构或对象写入到磁盘文件中或通过网络传到其他节点的过程。
反序列化:把磁盘中对象或者把网络节点中传输的数据恢复为python的数据对象的过程。
9.8.1、json初识
序列化最重要的就是json序列化。
JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
go语言数据类型 | json支持的类型 |
---|---|
整型、浮点型 | 整型、浮点型 |
字符串(在双引号中) | 字符串(双引号) |
逻辑值(true 或 false ) |
逻辑值(true 或 false ) |
数组,切片 | 数组(在方括号中) |
map | 对象(在花括号中) |
nil | null |
9.8.2、结构体的json操作
package main
import (
"encoding/json"
"fmt"
)
type Addr struct {
Province string
City string
}
type Stu struct {
Name string `json:"name"` // 结构体的标签
Age int `json:"-"` // 表示不参与序列化
Addr Addr
}
func main() {
var stuMap = map[string]interface{}{"name": "yuan", "age": 32, "addr": "beijing"}
var stuStruct = Stu{Name: "yuan", Age: 18, Addr: Addr{Province: "Hebei", City: "langFang"}}
// 序列化
jsonStuMap, _ := json.Marshal(stuMap)
jsonStuStruct, _ := json.Marshal(stuStruct)
fmt.Println(string(jsonStuMap))
fmt.Println(string(jsonStuStruct))
// 反序列化
// var x = make(map[int]string)
var StuMap map[string]interface{}
err := json.Unmarshal(jsonStuMap, &StuMap)
if err != nil {
return
}
fmt.Println("StuMap", StuMap, StuMap["name"])
var StuStruct Stu
json.Unmarshal(jsonStuStruct, &StuStruct)
if err != nil {
return
}
fmt.Println(StuStruct)
fmt.Println(StuStruct.Name)
fmt.Println(StuStruct.Addr.City)
}
标签:string,fmt,func,Println,结构,name From: https://www.cnblogs.com/xiaohaoge/p/16923584.html