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)
}