什么是结构体
回顾下slice和map他们都是单类型的
func main() {
// 切片或者数组的类型只能存一种
s1 := []int{0, 1, 2, 3, 4, 5}
// map也是同样它的这个类型的值也只能是单类型的
m1 := map[string]string{"name": "eson", "addr": "hebei"}
fmt.Println(s1, m1)
}
那如果想存储多种类型怎么办?使用结构体:struct
// Person struce save person property
type Person struct {
name string
age int
}
func main() {
p1 := Person{name: "eric", age: 18}
fmt.Printf("%v
", p1)
}
总结下:
结构体是一组数据,它是一个复合类型(每个数据有自己的类型)
自定义类型&类型别名
除了标准的:字符串、数字、小数、布尔值我们可以通过type关键字自定义类型
// PersonAge 自定义一个类型它有uint8的所有特性
type PersonAge uint8
类型别名go1.9版本之后的新功能
package main
import "fmt"
// NewAge 自定义类型
type NewAge uint
// PersonAge go1.9之后引用了类型别名
// 类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。
type PersonAge = uint8
func main() {
var a1 NewAge
var a2 PersonAge
fmt.Printf("自定义类型: %T
", a1) // 自定义类型: main.NewAge
fmt.Printf("类型的别名: %T
", a2) // 类型的别名: uint8
}
类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。好比一个人本身有中文名字又启用了一个英文名字,这个应为名字指向也是同一个人
区别:
自定义类型是一个独立的数据类型,但是类型别名他的类型还是它指向的数据类型
使用结构体
定义结构体
type 类型名 struct {
字段名 字段类型
字段名 字段类型
…
}
解释:
- 类型名:标识自定义结构体的名称,在同一个包内不能重复
- 字段名:标识结构体字段名。同一个结构体内的字段名必须唯一
- 字段类型:标识结构体字段的具体类型
type Person struct {
name string
age uint
}
声明使用
标准声明方式
func main() {
// 标准声明方式
// 当我们定义了一个变量后默认就使用了这个结构体的零值
var p1 Person
fmt.Printf("%v
", p1)
p1.name = "eric"
fmt.Printf("%v
", p1)
}
实例化指针
func main() {
// p1 := new(Person)
// 或者采用
p1 := &Person{}
fmt.Printf("%v
", p1)
}
new或者取址符会返回一个指针类型的结构体,什么使用指针类型的结构体呢?
- 结构体很大的时候不希望每次使用的时候都COPY一份内存数据
实例化先声明一个变量在赋值(没有初始化实例化一般都会使用零值),初始化是在声明的时候就赋值
初始化声明(常用)
package main
import "fmt"
type person struct {
name string
age int
}
func main() {
// 键值对初始化
p1 := person{name: "eson", age: 18}
// 列表初始化
p2 := person{"eric", 18}
// 指针初始化(&或new)
p3 := &person{name: "fire", age: 18}
fmt.Printf("%v,%v,%v", p1, p2, p3)
}
匿名结构体
在一些需要临时使用一个结构体的场景的时候,可以使用匿名结构体
package main
import "fmt"
func main() {
// 匿名结构体
var user struct {
name string
age int
}
user.name = "eson"
user.age = 18
// user的类型是: struct { name string; age int }
fmt.Printf("user的类型是: %T
", user)
// user的值:{eson 18}
fmt.Printf("user的值:%v
", user)
}
结构体的指针优化
看下面的例子
package main
import "fmt"
type person struct {
name string
age int
}
func main() {
p1 := &person{name: "John", age: 18} // 初始化一个person的结构体指针变量
// 当我们使用的时候怎么用?
}
如下方式调用,是不是怪怪的?
package main
import "fmt"
type person struct {
name string
age int
}
func main() {
p1 := &person{} // 初始化一个person的结构体指针变量
// 当我们使用的时候怎么用?
(*p1).name = "John"
(*p1).age = 18
fmt.Printf("%v
", p1) // &{John 18}
}
go给我们做了一个语法糖,我们可以在调用指针类型的结构体的时候可以进行简写成如下
package main
import "fmt"
type person struct {
name string
age int
}
func main() {
p1 := &person{} // 初始化一个person的结构体指针变量
p1.name = "John"
p1.age = 18
fmt.Printf("%v
", p1) // &{John 18} 它还是一个指针类型的
}
结构体嵌套
package main
import "fmt"
// Address 包含省份和城市
type Address struct {
Province string
City string
}
// Info 包含年龄、email等信息
type Info struct {
Age int
Email string
}
// 长辈
type Account struct {
Email string
}
// Student 学生信息
type Student struct {
Name string
Address Address
Info
Account
}
func main() {
s1 := &Student{Name: "eson",
Info: Info{Age: 18, Email: "eson@gamil.com"},
Address: Address{Province: "hb", City: "hs"},
Account: Account{Email: "eson@gamil.com"},
}
fmt.Println(s1.Name)
// 匿名字段可以直接访问
fmt.Println(s1.Age)
// 非匿名字段需要输入输入输入全部
fmt.Println(s1.Address.City)
// 如果匿名字段冲突了需要输入全部的
fmt.Println(s1.Info.Email)
fmt.Println(s1.Account.Email)
}