Go语言基础之结构体!

Go语言中的基本数据类型可以表示一些事物的基本属性

  • 但是当想表达一个事物的全部或者部分属性时
    • 这时候再用单一的基本数据类型明显就无法满足需求。

Go语言提供了一种自定义数据类型,可以封装多个基本数据类型

  • 这种数据类型叫结构体,struct

Go语言中通过struct来实现面向对象。

结构体的定义

使用typestruct关键字来定义结构体。

1
2
3
4
5
6
7
8
type 类型名 struct{
字段名 字段类型
字段名 字段类型
...
}
// 类型名: 标识自定义结构体的名称,在同一个包内不能重复
// 字段名: 标识结构体字段名。结构体中的字段名必须唯一
// 字段类型: 标识结构体字段的具体类型
1
2
3
4
5
type person struct{   // 定义一个person的结构体
name string
age int8
city string
}

同类型的字段也可以写在一行。

1
2
3
4
type person struct{
name,city string
age int8
}

结构体实例化

只有当结构体实例化时,才会真正地分配内存,也就是必须实例化后才能使用结构体的字段。

结构体本身也是一种类型

  • 可以像声明内置类型一样使用var关键字来声明结构体类型。

通过.来访问结构体字段(成员变量),例如p.namep.age等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var 结构体实例 结构体类型

type person struct {
name string
city string
age int8
}

func main() {
var p1 person
p1.name = "娜扎"
p1.city = "北京"
p1.age = 18
fmt.Printf("p1=%v\n", p1) //p1={娜扎 北京 18}
fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"娜扎", city:"北京", age:18}
}

匿名结构体

在定义一些临时数据结构等常见下可以使用匿名结构体。

1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
)

func main() {
var user struct{Name string; Age int}
user.Name = "小王子"
user.Age = 18
fmt.Printf("%#v\n", user)
}

创建指针类型结构体

通过使用new关键字对结构体进行实例化,得到的是结构体地址。

1
2
3
var p = new(person)
fmt.Printf("%T\n",p) // *main.person
fmt.Printf("p=%#v\n",p) // p=&main.person{name:"",age:0,city:""}

从打印结果来看,此时p是一个结构体指针。

在Go语言中支持对结构体指针直接使用.来访问结构体成员。

1
2
3
4
5
var p = new(person)
p.name = "Negan"
p.age = 68
p.city = "亚历山大"
fmt.Printf("p=%#v\n",p) // p=&main.person{name:"Negan",age:68,city:"亚历山大"}

取结构体的地址实例化

使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。

1
2
3
4
5
6
7
p := &person{}
fmt.Printf("%T\n",p) // *main.person
fmt.Printf("p=%v\n",p) // p=&main.person{name:"",age:0,city:""}
p.name = "Negan"
p.age = 68
p.city = "救世堂"
fmt.Printf("p=%#v\n",p) // p=&main.person{name:"Negan",age:68,city:"救世堂"}

p.name="Negan"其实在底层是(*p3).name="Negan"

  • 这是Go语言实现的语法糖。

结构体初始化

没有初始化的结构体,其成员变量都是对应其类型的零值。

1
2
3
4
5
6
7
8
9
10
type person struct{
name string
age int8
city string
}

func main(){
var p person
fmt.Printf("p=%#v\n",p) // p=main.person{name:“”,age:0,city:""}
}

使用键值对初始化

使用键值对对结构体进行初始化,键对应结构体的字段,值对应该字段的初始值。

1
2
3
4
5
6
p := person{
name:"Negan",
age:68,
city:"亚历山大"
}
fmt.Printf("p=%#v\n",p) // p=main.person{name:"Negan",age:68,city:"亚历山大"}

也可以使用结构体指针进行键值对初始化。

1
2
3
4
5
6
p := &person{
name:"Negan",
age:68,
city:"亚历山大"
}
fmt.Printf("p=%#v\n",p) //p=&main.person{name:"Negan",age:68,city:"亚历山大"}

当某些字段没有初始值的时候,该字段可以不写

  • 此时没有指定初始值的字段的值就是该字段类型的零值。
1
2
3
4
p := &person{
city:"救世堂"
}
fmt.Printf("p=%#v\n",p) // p=&main.person{name:"",age:0,city:"救世堂"}

使用值的列表初始化

初始化结构体的时候可以简写,也就是初始化的时候不写键,直接写值。

使用这种格式初始化时,需要注意:

  • 必须初始化结构体的所有字段
  • 初始值的填充循序必须与字段在结构体中的声明顺序一致
  • 该方式不能和键值初始化方式混用
1
2
3
4
5
6
p := &person{
"Negan",
68,
"救世堂"
}
fmt.Printf("p=%#v\n",p) // p=&main.person{name:"Negan",age:68,city:"救世堂"}

结构体的继承

Go语言中使用结构体可以实现其他编程语言中的面向对象继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Animal 动物
type Animal struct{
name string
}

func (a *Animal) move(){
fmt.Printf("%s会动", a.name)
}

type Dog struct {
Feet int8
*Animal // 通过嵌套匿名结构体实现继承
}

func (d *Dog) wang(){
fmt.Printf("%s会汪汪汪~\n",d.name)
}

func main() {
d := &Dog{
Feet: 4,
Animal:&Animal{
name: "旺财",
},
}
d.wang() // 旺财会汪汪汪~
d.move() // 旺财会动
}

结构体字段的可见性

结构体中字段大写开头表示公开访问

  • 小写表示私有(仅在定义当前结构体的包中可访问)。

嵌套结构体

一个结构体中可以嵌套包含另一个结构体或者结构体指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Address 地址结构体
type Address struct{
Province string
City string
}

// User 用户结构体
type User struct{
Name string
Gender string
Address Address
}

func main() {
user := User{
Name: "Negan",
Gender:"男",
Address: Address{
Province: "陕西",
City: "西安",
},
}
fmt.Printf("user=%#v\n", user)
// user=main.User{Name:"Negan", Gender:"男", Address:main.Address{Province:"陕西", City:"西安"}}
}

嵌套匿名结构体

当访问结构体成员时会现在结构体中查找该字段,找不到再去匿名结构体中查找。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Address 地址结构体
type Address struct {
Province string
City string
}

// User 用户结构体
type User struct{
Name string
Gender string
Address // 匿名结构体
}

func main() {
var user User
user.Name = "Negan"
user.Gender = "男"
user.Address.Province = "陕西" // 通过匿名结构体.字段名访问
user.City = "西安" // 直接访问匿名结构体的字段名
fmt.Printf("user=%#v\n", user)
// user=main.User{Name:"Negan", Gender:"男", Address:main.Address{Province:"陕西", City:"西安"}}
}

嵌套结构体的字段名冲突

嵌套结构体内部可能存在相同的字段名

  • 这个时候为了避免歧义需要制定具体的内嵌结构体的字段。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Address 地址结构体
type Address struct{
Province string
City string
CreateTime string
}

// Email 邮箱结构体
type Email struct{
Account string
CreateTime string
}

// User 用户结构体
type User struct {
Name string
Gender string
Address
Email
}

func main() {
var user User
user.Name = "Negan"
user.Gender = "男"
user.Address.CreateTime = "2020"
user.Email.CreateTime = "2020"
fmt.Printf("%#v\n", user)
}