参考《go语言从入门到进阶实战》和韩顺平《go语言核心编程》
type 结构体名称struct {
field1 type
field2 type
}
这里要注意:
如果结构体的字段类型是:指针,slice,和map的零值都是nil,即还没有分配空间/如果需要使用这样的字段,需要先make,才能使用.
例如:
type Person struct{
Name string
Age int
Scores [5]float64
ptr *int/指针
slice []int//切片
map1 map[string]string //map
}
创建实例:
var p1 Person
p1.slice = make([]int,10)
p1.slice[10]=100
p1.map1 = make(map[string]string)
p1.map1["k1"]="acb"
不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个,结构体是值类型,例子:
var monster1 Monster
monster1.Name="牛魔王"
monster1.Age= 500
monster2:=monster1//结构体是值类型,默认为值拷贝monster2.Name="青牛精"
fmt.Print1n("monster:1=",monster1)//monster1={牛魔王500]
fmt.Println(monster2=",monster2)//monster2={青牛精500}
有4方式:
1.方式1-直接声明
案例演示: var person Person
2.方式2-{}
案例演示: var person Person = Person{}
3.方式3-&
案例: var person *Person = new (Person)
4.方式4-{}
案例: var person *Person = &Person{}
同样是指针类型,赋值方式同3
说明:
其中有一种最简单的方式,不用声明var,直接一个变量名,go会自动识别类型:
abc := new (Person)
输出的结果是: p2.Name = tom p1.Name = 小明
内存分配图:
对于如下代码:
输出:、
内存分配:
注意:
3)3) 结构体进行type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转
4) struct 的每个字段上,可以写上一个tag, 该tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化。
举例:
//1.创建一个Monster变量
monster:=Monster{"牛魔王",500,"芭蕉扇~"}
//2.将monster变量序列化为json格式字串
//json.Marshal函数中使用反射,
jsonstr,err= json.Marshal(monster)
if err !=nil
fmt.Println("json处理错误",err)
fmt.Println("jsonstr",string(jsonstr))
在面向对象思想中,实现对象关系需要使用“继承”特性。例如,人类不能飞行,鸟类可以飞行。人类和鸟类都可以继承自可行走类,但只有鸟类继承自飞行类。 面向对象的设计原则中也建议对象最好不要使用多重继承,有些面向对象语言从语言层面就禁止了多重继承,如C#和Java语言。鸟类同时继承自可行走类和飞行类,这显然是存在问题的。在面向对象思想中要正确地实现对象的多重特性,只能使用一些精巧的设计来补救。 Go语言的结构体内嵌特性就是一种组合特性,使用组合特性可以快速构建对象的不同特性。
例子:人和鸟的特性
// 可飞行的
type Flying struct{}
func (f *Flying) Fly() {
fmt.Println("can fly")
}
// 可行走的
type Walkable struct{}
func (f *Walkable) Walk() {
fmt.Println("can calk")
}
// 人类
type Human struct {
Walkable // 人类能行走
}
// 鸟类
type Bird struct {
Walkable // 鸟类能行走
Flying // 鸟类能飞行
}
func main() {
// 实例化鸟类
b := new(Bird)
fmt.Println("Bird: ")
b.Fly()
b.Walk()
// 实例化人类
h := new(Human)
fmt.Println("Human: ")
h.Walk()
}
结构体内嵌初始化时,将结构体内嵌的类型作为字段名像普通结构体一样进行初始化,详细实现过程:
package main
import "fmt"
// 车轮
type Wheel struct {
Size int
}
// 引擎
type Engine struct {
Power int // 功率
Type string // 类型
}
// 车
type Car struct {
Wheel
Engine
}
func main() {
c := Car{
// 初始化轮子
Wheel: Wheel{
Size: 18,
},
// 初始化引擎
Engine: Engine{
Type: "1.4T",
Power: 143,
},
}
fmt.Printf("%+v\n", c)
}
输出:
{Wheel:{Size:18} Engine:{Power:143 Type:1.4T}}
初始化匿名结构体:
有时考虑编写代码的便利性,会将结构体直接定义在嵌入的结构体中。也就是说,结构体的定义不会被外部引用到。在初始化这个被嵌入的结构体时,就需要再次声明结构才能赋予数据。
package main
import "fmt"
// 车轮
type Wheel struct {
Size int
}
// 车
type Car struct {
Wheel
// 引擎
Engine struct {
Power int // 功率
Type string // 类型
}
}
func main() {
c := Car{
// 初始化轮子
Wheel: Wheel{
Size: 18,
},
// 初始化引擎
Engine: struct {
Power int
Type string
}{
Type: "1.4T",
Power: 143,
},
}
fmt.Printf("%+v\n", c)
}
嵌入结构体内部可能拥有相同的成员名,成员重名时会发生什么?下面通过例子来讲解。
01 package main
02
03 import (
04 "fmt"
05 )
06
07 type A struct {
08 a int
09 }
10
11 type B struct {
12 a int
13 }
14
15 type C struct {
16 A
17 B
18 }
19
20 func main() {
21 c := &C{}
22 c.A.a = 1
23 fmt.Println(c)
24 }
代码说明如下: ·第7行和第11行分别定义了两个拥有a int字段的结构体。 ·第15行的结构体嵌入了A和B的结构体。 ·第21行实例化C结构体。 ·第22行按常规的方法,访问嵌入结构体A中的a字段,并赋值1。 ·第23行可以正常输出实例化C结构体。 接着,将第22行修改为如下代码:
func main() {
c := &C{}
c.a = 1
fmt.Println(c)
}
此时再编译运行,编译器报错:编译器告知C的选择器a引起歧义,也就是说,编译器无法决定将1赋给C中的A还是B里的字段a。