结构体

结构体(Struct)是 Go 语言中用于组织数据的核心概念。它将多个不同类型的字段组合成一个整体,是 Go 实现面向对象编程的基础。

什么是结构体?

结构体是一组字段的集合,每个字段都有名称和类型。

结构体示意:

┌─────────────────────────────────┐
│         Person 结构体            │
├─────────────────────────────────┤
│  Name: string  →  "张三"         │
│  Age:  int     →  25            │
│  City: string  →  "北京"         │
└─────────────────────────────────┘

定义结构体

基本语法

type 结构体名 struct {
    字段名1 类型1
    字段名2 类型2
    ...
}

示例

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    var p Person
    fmt.Printf("p: %+v\n", p)
}

输出:

p: {Name: Age:0 City:}

创建结构体实例

方式一:var 声明

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    var p Person
    p.Name = "张三"
    p.Age = 25
    p.City = "北京"

    fmt.Printf("p: %+v\n", p)
}

方式二:字面量

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    p := Person{
        Name: "张三",
        Age:  25,
        City: "北京",
    }

    fmt.Printf("p: %+v\n", p)
}

方式三:按顺序赋值

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    p := Person{"张三", 25, "北京"}
    fmt.Printf("p: %+v\n", p)
}

注意:这种方式必须按字段顺序赋值,不推荐使用。

方式四:new 函数

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    p := new(Person)
    p.Name = "张三"
    p.Age = 25

    fmt.Printf("p: %+v\n", p)
    fmt.Printf("p 类型: %T\n", p)
}

输出:

p: &{Name:张三 Age:25 City:}
p 类型: *main.Person

方式五:取地址

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    p := &Person{
        Name: "张三",
        Age:  25,
    }

    fmt.Printf("p: %+v\n", p)
}

访问结构体字段

使用点号 . 访问字段:

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    p := Person{Name: "张三", Age: 25}

    fmt.Printf("姓名: %s\n", p.Name)
    fmt.Printf("年龄: %d\n", p.Age)

    p.Age = 26
    fmt.Printf("修改后年龄: %d\n", p.Age)
}

结构体方法

Go 语言中,可以为结构体定义方法。

值接收者

package main

import "fmt"

type Rectangle struct {
    Width  float64
    Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

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

func main() {
    r := Rectangle{Width: 10, Height: 5}

    fmt.Printf("矩形: 宽=%.1f, 高=%.1f\n", r.Width, r.Height)
    fmt.Printf("面积: %.2f\n", r.Area())
    fmt.Printf("周长: %.2f\n", r.Perimeter())
}

指针接收者

package main

import "fmt"

type Counter struct {
    Value int
}

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

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

func main() {
    c := Counter{Value: 0}

    c.Increment()
    c.Increment()
    fmt.Printf("计数: %d\n", c.Value)

    c.Reset()
    fmt.Printf("重置后: %d\n", c.Value)
}

值接收者 vs 指针接收者

对比项值接收者指针接收者
修改原对象不能
复制开销复制整个结构体只复制指针
使用场景小结构体、只读大结构体、需要修改

结构体嵌套

Go 通过结构体嵌套实现组合:

package main

import "fmt"

type Address struct {
    City    string
    Street  string
    ZipCode string
}

type Person struct {
    Name    string
    Age     int
    Address Address
}

func main() {
    p := Person{
        Name: "张三",
        Age:  25,
        Address: Address{
            City:    "北京",
            Street:  "长安街",
            ZipCode: "100000",
        },
    }

    fmt.Printf("姓名: %s\n", p.Name)
    fmt.Printf("城市: %s\n", p.Address.City)
    fmt.Printf("街道: %s\n", p.Address.Street)
}

匿名字段

结构体可以有匿名字段:

package main

import "fmt"

type Person struct {
    string
    int
}

func main() {
    p := Person{"张三", 25}
    fmt.Printf("姓名: %s, 年龄: %d\n", p.string, p.int)
}

结构体标签

结构体标签用于为字段添加元信息,常用于 JSON 序列化:

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email,omitempty"`
    Password string `json:"-"`
}

func main() {
    user := User{
        ID:       1,
        Name:     "张三",
        Email:    "zhangsan@example.com",
        Password: "123456",
    }

    data, _ := json.MarshalIndent(user, "", "  ")
    fmt.Println("JSON 输出:")
    fmt.Println(string(data))
}

输出:

JSON 输出:
{
  "id": 1,
  "name": "张三",
  "email": "zhangsan@example.com"
}

常用标签:

标签说明
json:"name"JSON 序列化时的字段名
json:"name,omitempty"字段为零值时忽略
json:"-"忽略该字段
xml:"name"XML 序列化时的字段名
db:"name"数据库字段名
form:"name"表单字段名

匿名结构体

package main

import "fmt"

func main() {
    p := struct {
        Name string
        Age  int
    }{
        Name: "张三",
        Age:  25,
    }

    fmt.Printf("p: %+v\n", p)
}

构造函数

Go 没有构造函数,但可以约定使用 NewXxx 函数:

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func NewPerson(name string, age int) *Person {
    return &Person{
        Name: name,
        Age:  age,
    }
}

func main() {
    p := NewPerson("张三", 25)
    fmt.Printf("p: %+v\n", p)
}

实际案例

案例 1:学生管理系统

package main

import "fmt"

type Student struct {
    ID     int
    Name   string
    Scores map[string]float64
}

func NewStudent(id int, name string) *Student {
    return &Student{
        ID:     id,
        Name:   name,
        Scores: make(map[string]float64),
    }
}

func (s *Student) AddScore(subject string, score float64) {
    s.Scores[subject] = score
}

func (s *Student) GetAverage() float64 {
    if len(s.Scores) == 0 {
        return 0
    }
    total := 0.0
    for _, score := range s.Scores {
        total += score
    }
    return total / float64(len(s.Scores))
}

func (s *Student) PrintInfo() {
    fmt.Printf("学号: %d, 姓名: %s\n", s.ID, s.Name)
    fmt.Println("成绩:")
    for subject, score := range s.Scores {
        fmt.Printf("  %s: %.1f\n", subject, score)
    }
    fmt.Printf("平均分: %.2f\n", s.GetAverage())
}

func main() {
    student := NewStudent(1, "张三")
    student.AddScore("数学", 90)
    student.AddScore("英语", 85)
    student.AddScore("物理", 88)

    student.PrintInfo()
}

案例 2:图形计算

package main

import (
    "fmt"
    "math"
)

type Circle struct {
    Radius float64
}

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

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

type Rectangle struct {
    Width  float64
    Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

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

func main() {
    circle := Circle{Radius: 5}
    fmt.Printf("圆形 (半径=%.1f)\n", circle.Radius)
    fmt.Printf("  面积: %.2f\n", circle.Area())
    fmt.Printf("  周长: %.2f\n", circle.Perimeter())

    rect := Rectangle{Width: 10, Height: 5}
    fmt.Printf("\n矩形 (宽=%.1f, 高=%.1f)\n", rect.Width, rect.Height)
    fmt.Printf("  面积: %.2f\n", rect.Area())
    fmt.Printf("  周长: %.2f\n", rect.Perimeter())
}

总结

本章学习了 Go 语言的结构体:

知识点说明
定义type Name struct { ... }
创建字面量、new、取地址
方法值接收者、指针接收者
嵌套组合实现代码复用
标签为字段添加元信息