## 定义
Go语言可以通过自定义的方式形成新的类型,结构体就是这些类型中的一种复合类型,结构体是由零个或多个任意类型的值聚合成的实体,每个值都可以称为结构体的成员。
结构体成员也可以称为“字段”,这些字段有以下特性:
- 字段拥有自己的类型和值;
- 字段名必须唯一;
- 字段的类型也可以是结构体,甚至是字段所在结构体的类型。
使用关键字 type 可以将各种基本类型定义为自定义类型,基本类型包括整型、字符串、布尔等。结构体是一种复合的基本类型,通过 type 定义为自定义类型后,使结构体更便于使用。
```
type 类型名 struct{
字段 类型
}
```
例如:
```
type Person struct{
name string
age int
}
```
同类型的变量也可以写在一行
```
type Person struct{
name, email string
age int
}
```
## 实例化
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存,因此必须在定义结构体并实例化后才能使用结构体的字段。
实例化就是根据结构体定义的格式创建一份与格式一致的内存区域,结构体实例与实例间的内存是完全独立的。
Go语言可以通过多种方式实例化结构体,根据实际需要可以选用不同的写法。
**var 实例化方式**
```
package main
import "fmt"
type Person struct{
name string
age int
}
func main() {
var tom = Person
tom.name = "Tom"
tom.age = 18
// var tom = Person{"Tom", 18} 可以省略字段名,但是顺序必须一一对应
fmt.Println(tom) // {Tom 18}
}
```
**new 实例化方式**
```
package main
import "fmt"
type Person struct{
name string
age int
}
func main() {
tom := new(Person)
tom.name = "Tom"
tom.age = 18
fmt.Println(tom) // &{Tom 18}
}
```
## 结构体初始化
**使用“键值对”初始化结构体**
```
type Person struct{
name string
age int
}
user := Person{name: "Tony", age: 18}
```
**省略键初始化结构体**
```
type Person struct{
name string
age int
}
user := Person{"Tony", 18}
```
**初始化匿名结构体**
匿名结构体的初始化写法由结构体定义和键值对初始化两部分组成,结构体定义时没有结构体类型名,只有字段和类型定义,键值对初始化部分由可选的多个键值对组成:
```
init := struct {
// 匿名结构体字段定义
字段 字段类型
…
}{
// 字段值初始化,注意每行结尾必须有逗号
初始化字段: 字段的值,
…
}
```
```
package main
import "fmt"
func main() {
init := struct{
name string
age int
}{
name: "Tom",
age: 18,
}
fmt.Printf("%v", init) // {Tom 18}
}
```
## 匿名字段
Go语言支持只提供类型,而不写字段名的方式,也就是匿名字段,或称为嵌入字段。
当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个struct。
```
package main
import "fmt"
type Person struct{
name string
age int
}
type Tom struct{
Person
height int
}
func main() {
tom := Tom{Person{"Tom", 18}, 180}
fmt.Printf("%v", tom.name) // Tom
// 通过Person作为字段名访问
fmt.Printf("%v", tom.Person.name) // Tom
}
```
自定义类型和内置类型等都可以作为匿名字段,而且还可以在相应的字段是进行函数操作:
```
package main
import "fmt"
type Person struct{
name string
age int
}
// 类型定义
type Skills []string
type Tom struct{
Person // 匿名字段,struct
Skills // 匿名字段,自定义类型 string slice
height int
int // 内置类型作为匿名字段
}
func main() {
tom := Tom{Person:Person{"Tom", 18}, Skills: []string{"computer", "excel"}, height: 180}
fmt.Printf("his name is:%s\n", tom.name) // he name is:Tom
// 修改 skill 字段
tom.Skills = append(tom.Skills, "Python")
fmt.Printf("his skills are:%v\n", tom.Skills) // he skills are:[computer excel Python]
// 修改匿名内置类型
tom.int = 50
fmt.Printf("%d", tom.int) // 50
}
```
如果 Person 里有个 phone 字段,而 Tom 里也有个 phone 字段,当通过 tom.phone 访问,会优先访问最外层的字段
这样就允许我们去重载通过匿名字段继承的一些字段,当然如果我们想访问重载后对应匿名类型里面的字段,可以通过匿名字段名来访问。
```
package main
import "fmt"
type Person struct{
name , phone string
age int
}
type Tom struct{
Person // 匿名字段,struct
phone string
}
func main() {
tom := Tom{Person:Person{"Tom", "xxx", 18}, phone: "xxxxxx"}
fmt.Printf("his phone is:%s\n", tom.phone) // his phone is:xxxxxx
fmt.Printf("his phone is:%s\n", tom.Person.phone) // his phone is:xxx
}
```