Go学习笔记-Structs

结构体是Go语言中定义自定义类型的主要方式。结构体将相关的数据组合在一起,支持嵌入、标签等高级特性,是面向对象编程的基础。

基本结构体定义

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package main

import "fmt"

// 1. 基本结构体定义
type Person struct {
    Name string
    Age  int
    City string
}

// 2. 空结构体
type Empty struct{}

// 3. 结构体字段可以是任意类型
type Student struct {
    ID       int
    Name     string
    Scores   []float64
    Metadata map[string]interface{}
    IsActive bool
}

// 4. 结构体字段标签
type User struct {
    ID       int    `json:"id" db:"user_id"`
    Username string `json:"username" db:"username"`
    Email    string `json:"email" db:"email"`
    Password string `json:"-" db:"password"`  // json:"-" 表示不序列化
}

func main() {
    // 1. 结构体的零值
    var p1 Person
    fmt.Printf("零值结构体: %+v\n", p1)
    
    // 2. 结构体字面量初始化
    p2 := Person{
        Name: "张三",
        Age:  25,
        City: "北京",
    }
    fmt.Printf("字面量初始化: %+v\n", p2)
    
    // 3. 按字段顺序初始化(不推荐)
    p3 := Person{"李四", 30, "上海"}
    fmt.Printf("按顺序初始化: %+v\n", p3)
    
    // 4. 部分字段初始化
    p4 := Person{
        Name: "王五",
        Age:  28,
        // City 使用零值
    }
    fmt.Printf("部分初始化: %+v\n", p4)
    
    // 5. 访问和修改字段
    fmt.Printf("姓名: %s, 年龄: %d\n", p2.Name, p2.Age)
    p2.Age = 26
    fmt.Printf("修改年龄后: %+v\n", p2)
    
    // 6. 结构体比较
    p5 := Person{"张三", 26, "北京"}
    p6 := Person{"张三", 26, "北京"}
    fmt.Printf("p5 == p6: %t\n", p5 == p6)  // true
    
    // 7. 空结构体
    var empty Empty
    fmt.Printf("空结构体大小: %d 字节\n", unsafe.Sizeof(empty))
    
    // 8. 复杂结构体
    student := Student{
        ID:     1001,
        Name:   "小明",
        Scores: []float64{85.5, 92.0, 78.5},
        Metadata: map[string]interface{}{
            "grade":  "高三",
            "class":  "1班",
            "mentor": "张老师",
        },
        IsActive: true,
    }
    fmt.Printf("学生信息: %+v\n", student)
}

结构体指针

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package main

import "fmt"

type Rectangle struct {
    Width  float64
    Height float64
}

// 值接收者方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 指针接收者方法
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    // 1. 结构体指针创建
    var p1 *Person
    fmt.Printf("nil指针: %v\n", p1)
    
    // 2. 使用new创建结构体指针
    p2 := new(Person)
    fmt.Printf("new创建的指针: %p, 值: %+v\n", p2, *p2)
    
    // 3. 使用&操作符创建指针
    p3 := &Person{
        Name: "赵六",
        Age:  35,
        City: "广州",
    }
    fmt.Printf("&操作符创建的指针: %p, 值: %+v\n", p3, *p3)
    
    // 4. 通过指针访问字段(Go会自动解引用)
    fmt.Printf("通过指针访问姓名: %s\n", p3.Name)
    p3.Age = 36
    fmt.Printf("通过指针修改年龄: %+v\n", *p3)
    
    // 5. 指针和值的区别
    rect1 := Rectangle{Width: 10, Height: 5}
    rect2 := rect1  // 值拷贝
    
    fmt.Printf("原始矩形: %+v\n", rect1)
    fmt.Printf("拷贝矩形: %+v\n", rect2)
    
    rect2.Width = 20
    fmt.Printf("修改拷贝后,原始矩形: %+v\n", rect1)
    fmt.Printf("修改拷贝后,拷贝矩形: %+v\n", rect2)
    
    // 6. 指针共享数据
    rect3 := &Rectangle{Width: 10, Height: 5}
    rect4 := rect3  // 指针拷贝,指向同一个对象
    
    fmt.Printf("原始矩形指针: %+v\n", *rect3)
    fmt.Printf("拷贝矩形指针: %+v\n", *rect4)
    
    rect4.Width = 20
    fmt.Printf("修改后,原始矩形指针: %+v\n", *rect3)
    fmt.Printf("修改后,拷贝矩形指针: %+v\n", *rect4)
    
    // 7. 方法调用
    rect := Rectangle{Width: 10, Height: 5}
    fmt.Printf("面积: %.2f\n", rect.Area())
    
    rect.Scale(2)  // Go会自动取地址
    fmt.Printf("缩放后: %+v\n", rect)
    
    // 8. 指针方法调用
    rectPtr := &Rectangle{Width: 3, Height: 4}
    fmt.Printf("指针面积: %.2f\n", rectPtr.Area())  // Go会自动解引用
    rectPtr.Scale(3)
    fmt.Printf("指针缩放后: %+v\n", *rectPtr)
}

结构体嵌入

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package main

import "fmt"

// 基础结构体
type Address struct {
    Street   string
    City     string
    ZipCode  string
    Country  string
}

type Contact struct {
    Phone string
    Email string
}

// 嵌入结构体(匿名字段)
type Person struct {
    Name string
    Age  int
    Address  // 匿名嵌入
    Contact  // 匿名嵌入
}

// 命名嵌入
type Employee struct {
    ID           int
    Name         string
    HomeAddress  Address  // 命名嵌入
    WorkAddress  Address  // 命名嵌入
    ContactInfo  Contact  // 命名嵌入
}

// 方法定义
func (a Address) String() string {
    return fmt.Sprintf("%s, %s %s, %s", a.Street, a.City, a.ZipCode, a.Country)
}

func (c Contact) String() string {
    return fmt.Sprintf("Phone: %s, Email: %s", c.Phone, c.Email)
}

// 嵌入结构体的方法提升
type Point struct {
    X, Y float64
}

func (p Point) Distance() float64 {
    return math.Sqrt(p.X*p.X + p.Y*p.Y)
}

type Circle struct {
    Point   // 嵌入Point
    Radius  float64
}

func main() {
    // 1. 匿名嵌入的使用
    person := Person{
        Name: "张三",
        Age:  30,
        Address: Address{
            Street:  "中关村大街1号",
            City:    "北京",
            ZipCode: "100080",
            Country: "中国",
        },
        Contact: Contact{
            Phone: "13800138000",
            Email: "zhangsan@example.com",
        },
    }
    
    fmt.Printf("人员信息: %+v\n", person)
    
    // 2. 直接访问嵌入字段
    fmt.Printf("姓名: %s\n", person.Name)
    fmt.Printf("城市: %s\n", person.City)     // 直接访问Address.City
    fmt.Printf("电话: %s\n", person.Phone)    // 直接访问Contact.Phone
    
    // 3. 通过嵌入类型访问
    fmt.Printf("地址: %s\n", person.Address.String())
    fmt.Printf("联系方式: %s\n", person.Contact.String())
    
    // 4. 修改嵌入字段
    person.City = "上海"
    person.Email = "zhangsan@newcompany.com"
    fmt.Printf("修改后的城市: %s\n", person.City)
    fmt.Printf("修改后的邮箱: %s\n", person.Email)
    
    // 5. 命名嵌入
    employee := Employee{
        ID:   1001,
        Name: "李四",
        HomeAddress: Address{
            Street:  "朝阳路100号",
            City:    "北京",
            ZipCode: "100020",
            Country: "中国",
        },
        WorkAddress: Address{
            Street:  "金融街35号",
            City:    "北京",
            ZipCode: "100033",
            Country: "中国",
        },
        ContactInfo: Contact{
            Phone: "13900139000",
            Email: "lisi@company.com",
        },
    }
    
    fmt.Printf("员工信息: %+v\n", employee)
    fmt.Printf("家庭地址: %s\n", employee.HomeAddress.String())
    fmt.Printf("工作地址: %s\n", employee.WorkAddress.String())
    
    // 6. 方法提升
    circle := Circle{
        Point:  Point{X: 3, Y: 4},
        Radius: 5,
    }
    
    fmt.Printf("圆心坐标: (%.1f, %.1f)\n", circle.X, circle.Y)
    fmt.Printf("圆心到原点距离: %.2f\n", circle.Distance())  // 方法提升
    fmt.Printf("半径: %.1f\n", circle.Radius)
    
    // 7. 字段名冲突的处理
    type A struct {
        Name string
    }
    
    type B struct {
        Name string
    }
    
    type C struct {
        A
        B
        Name string  // 覆盖嵌入的Name字段
    }
    
    c := C{
        A:    A{Name: "A的名字"},
        B:    B{Name: "B的名字"},
        Name: "C的名字",
    }
    
    fmt.Printf("C.Name: %s\n", c.Name)     // C的名字
    fmt.Printf("C.A.Name: %s\n", c.A.Name) // A的名字
    fmt.Printf("C.B.Name: %s\n", c.B.Name) // B的名字
}

结构体标签和反射

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package main

import (
    "encoding/json"
    "fmt"
    "reflect"
)

// 带标签的结构体
type Product struct {
    ID          int     `json:"id" xml:"id" db:"product_id"`
    Name        string  `json:"name" xml:"name" db:"product_name"`
    Price       float64 `json:"price" xml:"price" db:"price"`
    Description string  `json:"description,omitempty" xml:"description" db:"description"`
    InStock     bool    `json:"in_stock" xml:"inStock" db:"in_stock"`
    Tags        []string `json:"tags,omitempty" xml:"tags" db:"-"`  // db:"-" 表示不映射到数据库
}

// 验证标签
type User struct {
    Username string `validate:"required,min=3,max=20"`
    Email    string `validate:"required,email"`
    Age      int    `validate:"min=18,max=100"`
    Password string `validate:"required,min=8"`
}

func main() {
    // 1. JSON序列化和反序列化
    product := Product{
        ID:          1,
        Name:        "Go编程书籍",
        Price:       89.99,
        Description: "一本优秀的Go语言学习书籍",
        InStock:     true,
        Tags:        []string{"编程", "Go", "技术"},
    }
    
    // 序列化为JSON
    jsonData, err := json.Marshal(product)
    if err != nil {
        fmt.Printf("JSON序列化错误: %v\n", err)
        return
    }
    fmt.Printf("JSON数据: %s\n", jsonData)
    
    // 反序列化JSON
    var newProduct Product
    err = json.Unmarshal(jsonData, &newProduct)
    if err != nil {
        fmt.Printf("JSON反序列化错误: %v\n", err)
        return
    }
    fmt.Printf("反序列化产品: %+v\n", newProduct)
    
    // 2. 使用反射读取结构体标签
    fmt.Println("\n结构体字段标签:")
    productType := reflect.TypeOf(product)
    for i := 0; i < productType.NumField(); i++ {
        field := productType.Field(i)
        jsonTag := field.Tag.Get("json")
        dbTag := field.Tag.Get("db")
        fmt.Printf("字段: %s, JSON标签: %s, DB标签: %s\n", 
            field.Name, jsonTag, dbTag)
    }
    
    // 3. 检查标签是否存在
    nameField, _ := productType.FieldByName("Name")
    if xmlTag, ok := nameField.Tag.Lookup("xml"); ok {
        fmt.Printf("Name字段的XML标签: %s\n", xmlTag)
    }
    
    // 4. 自定义标签处理
    user := User{
        Username: "john_doe",
        Email:    "john@example.com",
        Age:      25,
        Password: "secretpassword",
    }
    
    validateStruct(user)
    
    // 5. 动态创建结构体实例
    fmt.Println("\n动态创建结构体:")
    productValue := reflect.ValueOf(&product).Elem()
    
    // 设置字段值
    nameField := productValue.FieldByName("Name")
    if nameField.IsValid() && nameField.CanSet() {
        nameField.SetString("新的产品名称")
    }
    
    priceField := productValue.FieldByName("Price")
    if priceField.IsValid() && priceField.CanSet() {
        priceField.SetFloat(99.99)
    }
    
    fmt.Printf("动态修改后的产品: %+v\n", product)
    
    // 6. 遍历结构体字段和值
    fmt.Println("\n遍历结构体字段和值:")
    userValue := reflect.ValueOf(user)
    userType := reflect.TypeOf(user)
    
    for i := 0; i < userValue.NumField(); i++ {
        field := userType.Field(i)
        value := userValue.Field(i)
        validateTag := field.Tag.Get("validate")
        
        fmt.Printf("字段: %s, 值: %v, 验证规则: %s\n", 
            field.Name, value.Interface(), validateTag)
    }
}

// 简单的结构体验证函数
func validateStruct(s interface{}) {
    fmt.Println("\n验证结构体:")
    v := reflect.ValueOf(s)
    t := reflect.TypeOf(s)
    
    for i := 0; i < v.NumField(); i++ {
        field := t.Field(i)
        value := v.Field(i)
        validateTag := field.Tag.Get("validate")
        
        if validateTag != "" {
            fmt.Printf("验证字段 %s (值: %v) 规则: %s\n", 
                field.Name, value.Interface(), validateTag)
            // 这里可以实现具体的验证逻辑
        }
    }
}

结构体方法

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package main

import (
    "fmt"
    "math"
)

// 定义结构体
type Circle struct {
    X, Y, Radius float64
}

type Rectangle struct {
    Width, Height float64
}

// 值接收者方法
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

// 指针接收者方法
func (c *Circle) Scale(factor float64) {
    c.Radius *= factor
}

func (c *Circle) Move(dx, dy float64) {
    c.X += dx
    c.Y += dy
}

// String方法(实现fmt.Stringer接口)
func (c Circle) String() string {
    return fmt.Sprintf("Circle(center: (%.1f, %.1f), radius: %.1f)", 
        c.X, c.Y, c.Radius)
}

// Rectangle的方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func (r Rectangle) String() string {
    return fmt.Sprintf("Rectangle(width: %.1f, height: %.1f)", 
        r.Width, r.Height)
}

// 定义接口
type Shape interface {
    Area() float64
    Perimeter() float64
    String() string
}

type Scalable interface {
    Scale(factor float64)
}

// 方法集合示例
type Counter struct {
    count int
}

func (c Counter) Value() int {
    return c.count
}

func (c *Counter) Increment() {
    c.count++
}

func (c *Counter) Add(n int) {
    c.count += n
}

func (c *Counter) Reset() {
    c.count = 0
}

func main() {
    // 1. 基本方法调用
    circle := Circle{X: 0, Y: 0, Radius: 5}
    fmt.Printf("圆: %s\n", circle.String())
    fmt.Printf("面积: %.2f\n", circle.Area())
    fmt.Printf("周长: %.2f\n", circle.Perimeter())
    
    // 2. 指针接收者方法
    fmt.Println("\n修改圆:")
    circle.Scale(2)  // Go会自动取地址
    fmt.Printf("缩放后: %s\n", circle.String())
    
    circle.Move(10, 10)
    fmt.Printf("移动后: %s\n", circle.String())
    
    // 3. 通过指针调用方法
    circlePtr := &Circle{X: 5, Y: 5, Radius: 3}
    fmt.Printf("\n指针圆: %s\n", circlePtr.String())  // Go会自动解引用
    fmt.Printf("面积: %.2f\n", circlePtr.Area())
    
    circlePtr.Scale(1.5)
    fmt.Printf("缩放后: %s\n", circlePtr.String())
    
    // 4. 接口使用
    fmt.Println("\n接口使用:")
    var shapes []Shape
    shapes = append(shapes, Circle{X: 0, Y: 0, Radius: 3})
    shapes = append(shapes, Rectangle{Width: 4, Height: 6})
    
    for i, shape := range shapes {
        fmt.Printf("形状%d: %s\n", i+1, shape.String())
        fmt.Printf("  面积: %.2f\n", shape.Area())
        fmt.Printf("  周长: %.2f\n", shape.Perimeter())
    }
    
    // 5. 类型断言和接口
    fmt.Println("\n类型断言:")
    for _, shape := range shapes {
        if scalable, ok := shape.(Scalable); ok {
            fmt.Printf("%s 可以缩放\n", shape.String())
            // 注意:这里不能直接调用Scale,因为接口中的是值
            _ = scalable
        } else {
            fmt.Printf("%s 不能缩放\n", shape.String())
        }
    }
    
    // 6. 方法集合
    fmt.Println("\n方法集合:")
    counter := Counter{}
    fmt.Printf("初始值: %d\n", counter.Value())
    
    counter.Increment()
    fmt.Printf("增加1后: %d\n", counter.Value())
    
    counter.Add(5)
    fmt.Printf("增加5后: %d\n", counter.Value())
    
    counter.Reset()
    fmt.Printf("重置后: %d\n", counter.Value())
    
    // 7. 指针和值的方法集合差异
    fmt.Println("\n指针和值的方法集合:")
    
    // 值类型可以调用值接收者和指针接收者的方法
    var c1 Counter
    c1.Increment()  // Go会自动取地址
    fmt.Printf("值类型调用指针方法: %d\n", c1.Value())
    
    // 指针类型可以调用值接收者和指针接收者的方法
    var c2 *Counter = &Counter{}
    c2.Increment()
    fmt.Printf("指针类型调用指针方法: %d\n", c2.Value())  // Go会自动解引用
    
    // 8. 方法表达式
    fmt.Println("\n方法表达式:")
    circle2 := Circle{X: 0, Y: 0, Radius: 2}
    
    // 方法表达式
    areaFunc := Circle.Area
    fmt.Printf("方法表达式计算面积: %.2f\n", areaFunc(circle2))
    
    // 方法值
    areaMethod := circle2.Area
    fmt.Printf("方法值计算面积: %.2f\n", areaMethod())
}

总结

  1. 结构体定义

    • 使用type name struct{}语法
    • 字段可以是任意类型
    • 支持字段标签
    • 零值是所有字段的零值
  2. 结构体初始化

    • 字面量初始化
    • 部分字段初始化
    • 使用new()创建指针
    • 使用&操作符创建指针
  3. 结构体嵌入

    • 匿名嵌入实现继承效果
    • 方法提升
    • 字段提升
    • 命名嵌入避免冲突
  4. 结构体方法

    • 值接收者:不修改原结构体
    • 指针接收者:可以修改原结构体
    • 方法集合:类型可调用的方法集合
    • 接口实现:通过方法实现接口
  5. 高级特性

    • 结构体标签用于序列化等
    • 反射操作结构体
    • 方法表达式和方法值
    • 接口和类型断言
updatedupdated2025-09-202025-09-20