go struct结构体
struct结构体
- 用来自定义复杂数据结构
- struct里面可以包含多个字段(属性),字段可以是任意类型
- struct类型可以定义方法,注意和函数的区分
- struct类型是值类型
- struct类型可以嵌套
- Go语言没有class类型,只有struct类型
make 用来创建map、slice、channel
new用来创建值类型
1、struct 声明
type 标识符 struct { field1 type field2 type }
样例:
type Student struct { Name string Age int Score int }
2、struct 中字段访问:和其他语言一样,使用点
var stu Student stu.Name = “tony” stu.Age = 18 stu.Score=20 fmt.Printf(“name=%s age=%d score=%d”, stu.Name, stu.Age, stu.Score)
3、struct定义的几种形式:
- a. var stu Student //值类型
- b. var stu *Student = new(Student) //引用类型
- c. var stu *Student = &Student{} //引用类型
- d. var stu Student = Student {} //值类型
备注:其中b和c返回的都是指向结构体的指针,访问形式如下:
stu.Name、stu.Age和stu.Score或者 (*stu).Name、(*stu).Age等
可以理解为语法糖,写stu.Name时底层会转成(*stu).Name
4. struct的内存布局:struct中的所有字段在内存是连续的,布局如下:
type Rect1 struct { Min, Max Point }
type Rect2 struct { Min, Max *Point }
package main import ( "fmt" ) type Point struct { x int y int } type Rect1 struct { p1 Point p2 Point } type Rect2 struct { p1 *Point p2 *Point } func main() { var r1 Rect1 var r2 Rect2 r2.p1 = new(Point) var r3 = new(Point) var r4 = new(Point) r2.p2 = new(Point) fmt.Println(r3, r4) //r1的内存布局 fmt.Printf("p1.x addr:%p\n", &r1.p1.x) fmt.Printf("p1.y addr:%p\n", &r1.p1.y) fmt.Printf("p2.x addr:%p\n", &r1.p2.x) fmt.Printf("p2.y addr:%p\n", &r1.p2.y) fmt.Println() fmt.Println() //r2的内存布局 fmt.Printf("p1.x addr:%p\n", &(r2.p1.x)) fmt.Printf("p1.y addr:%p\n", &(r2.p1.y)) fmt.Printf("p2.x addr:%p\n", &(r2.p2.x)) fmt.Printf("p2.y addr:%p\n", &(r2.p2.y)) fmt.Printf("p1:%p\n", &r2.p1) fmt.Printf("P2:%p\n", &r2.p2) }
5、链表定义
每个节点包含下一个节点的地址,这样把所有的节点串起来了,通常把 链表中的第一个节点叫做链表头。
type Student struct { Name string Next* Student }
样例一:
package main import ( "fmt" ) type Student struct { Name string Age int Score float32 next *Student } func main() { var head Student head.Name = "hua" head.Age = 18 head.Score = 100 var stu1 Student stu1.Name = "stu1" stu1.Age = 23 stu1.Score = 23 head.next = &stu1 var p *Student = &head for p != nil { fmt.Println(*p) p = p.next } fmt.Println() }
6、双链表定义
如果有两个指针分别指向前一个节点和后一个节点,我们叫做双链表。
type Student struct { Name string Next* Student Prev* Student }
package main import ( "fmt" "math/rand" ) type Student struct { Name string Age int Score float32 next *Student } func trans(p *Student) { for p != nil { fmt.Println(*p) p = p.next } fmt.Println() } func insertTail(p *Student) { var tail = p for i := 0; i < 10; i++ { stu := Student{ Name: fmt.Sprintf("stu%d", i), Age: rand.Intn(100), Score: rand.Float32() * 100, } tail.next = &stu tail = &stu } } func insertHead(p **Student) { for i := 0; i < 10; i++ { stu := Student{ Name: fmt.Sprintf("stu%d", i), Age: rand.Intn(100), Score: rand.Float32() * 100, } stu.next = *p *p = &stu } } func addNode(p *Student, newNode *Student) { for p != nil { if p.Name == "stu9" { newNode.next = p.next p.next = newNode break } p = p.next } } func main() { var head *Student = new(Student) head.Name = "hua" head.Age = 18 head.Score = 100 insertTail(head) trans(head) insertHead(&head) trans(head) var newNode *Student = new(Student) newNode.Name = "stu1000" newNode.Age = 18 newNode.Score = 100 addNode(head, newNode) trans(head) }
7、二叉树定义
如果每个节点有两个指针分别用来指向左子树和右子树,我们把这样的 结构叫做二叉树。
type Student struct { Name string left* Student right* Student }
样例:
package main import "fmt" type Student struct { Name string Age int Score float32 left *Student right *Student } func trans(root *Student) { if root == nil { return } fmt.Println(root) trans(root.left) trans(root.right) } func main() { var root *Student = new(Student) root.Name = "stu01" root.Age = 18 root.Score = 100 var left1 *Student = new(Student) left1.Name = "stu02" left1.Age = 18 left1.Score = 100 root.left = left1 var right1 *Student = new(Student) right1.Name = "stu04" right1.Age = 18 right1.Score = 100 root.right = right1 var left2 *Student = new(Student) left2.Name = "stu03" left2.Age = 18 left2.Score = 100 left1.left = left2 trans(root) }
8、结构体是用户单独定义的类型,不能和其他类型进行强制转换
package main import "fmt" type integer int type Student struct { Number int } type Stu Student //alias func main() { var i integer = 1000 var j int = 100 j = int(i) fmt.Println(j) var a Student a = Student{30} fmt.Println(a) var b Stu a = Student(b) fmt.Println(a) }
9、golang中的struct没有构造函数,一般可以使用工厂模式来解决这个问题
Package model type student struct { Name stirng Age int } func NewStudent(name string, age int) *student { return &student{ Name:name, Age:age, } } Package main S := new(student) S := model.NewStudent(“tony”, 20)
10、struct中的tag
我们可以为struct中的每个字段,写上一个tag。这个tag可以通过反射的 机制获取到,最常用的场景就是json序列化和反序列化。
package main import ( "encoding/json" "fmt" ) type Student struct { Name string `json:"student_name"` Age int `json:"age"` Score int `json:"score"` } func main() { var stu Student = Student{ Name: "stu01", Age: 18, Score: 80, } data, err := json.Marshal(stu) if err != nil { fmt.Println("json encode stu failed, err:", err) return } fmt.Println(string(data)) }
11、匿名字段
结构体中字段可以没有名字,即匿名字段。
package main import( "fmt" "time" ) type Car struct{ name string age int } type Train struct{ Car int start time.Time } func main() { var t Train t.Car.name = "001" t.Car.age = 100 t.name = "train" t.age = 200 fmt.Println(t) }
12、组合和匿名字段
如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问 匿名结构体的方法,从而实现了继承。
如果一个struct嵌套了另一个有名结构体,那么这个模式就叫组合。
13、实现String()
如果一个变量实现了String()这个方法,那么fmt.Println默认会调用这个 变量的String()进行输出。
package main import "fmt" type Car struct { weight int name string } func (p *Car) Run() { fmt.Println("running") } type Bike struct { Car lunzi int } type Train struct { Car } func (p *Train) String() string { str := fmt.Sprintf("name=[%s] weight=[%d]", p.name, p.weight) return str } func main() { var a Bike a.weight = 100 a.name = "bike" a.lunzi = 2 fmt.Println(a) a.Run() var b Train b.weight = 100 b.name = "train" b.Run() fmt.Printf("%s", &b) }
方法
Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以 有方法,而不仅仅是struct
定义:func (recevier type) methodName(参数列表)(返回值列表){}
方法的访问控制,通过大小写控制
package main import "fmt" type Student struct { Name string Age int Score int sex int } func (p *Student) init(name string, age int, score int) { p.Name = name p.Age = age p.Score = score fmt.Println(p) } func (p Student) get() Student { return p } func main() { var stu Student stu.init("stu", 10, 200) stu1 := stu.get() fmt.Println(stu1) }
继承
如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问 匿名结构体的方法,从而实现了继承。
package main import "fmt" type Car struct { weight int name string } func (p *Car) Run() { fmt.Println("running") } type Bike struct { Car lunzi int } type Train struct { c Car } func main() { var a Bike a.weight = 100 a.name = "bike" a.lunzi = 2 fmt.Println(a) a.Run() var b Train b.c.weight = 100 b.c.name = "train" b.c.Run() }
多重继承
如果一个struct嵌套了多个匿名结构体,那么这个结构可以直接访问 多个匿名结构体的方法,从而实现了多重继承。
package main import ( "fmt" ) type Cart1 struct { name string age int } type Cart2 struct { name string age int } type Train struct { Cart1 Cart2 } func main() { var t Train t.Cart1.name = "train" t.Cart1.age = 100 fmt.Println(t) }