GO语言基础教程(102)Go结构体:Go语言结构体:给数据“相亲”,打造你的专属CP!

嘿,各位Gopher(Go语言爱好者)们,今天咱们不聊风花雪月,来聊聊Go语言里如何给数据“组CP”——没错,就是那个让你又爱又恨的结构体(Struct)

想象一下,你要描述一个程序员小哥。你有一堆信息:名字(字符串)、年龄(整型)、是否秃头(布尔型)……如果把这些变量都零散地定义,你的代码很快就会变成一锅“变量八宝粥”,找起来费劲,管理起来糟心。

这时候,结构体就像一位专业的“红娘”闪亮登场!它说:“别急,我把这些相关的数据打包成一个‘复合类型’,给它们一个名分,让它们成为一个和谐的整体!”

第一章:初识结构体——如何“撮合”数据?

说白了,结构体就是一个自定义的数据类型,它包含了一系列的字段(Field)。每个字段都有自己的类型和名字。

定义结构体,就像写一份“相亲简历”:

// 定义一个名为 Programmer 的结构体类型
type Programmer struct {
    Name     string
    Age      int
    IsBald   bool
    Language []string // 会的编程语言,是个切片
}

看,多清晰!Programmer 这个类型内部有名字、年龄、秃头状态和技能列表。type ... struct 就是我们的“简历模板生成器”。

有了模板,接下来就是“发对象”了——创建结构体实例!

有几种方式可以创建,咱们一种一种来看:

方式一:最老实的“填表式”

// 创建一个 Programmer 类型的变量 p1
var p1 Programmer
p1.Name = "码农小明"
p1.Age = 28
p1.IsBald = true
p1.Language = []string{"Go", "Python", "Shell"}
fmt.Printf("码农小明:%+v\n", p1)
// 输出:码农小明:{Name:码农小明 Age:28 IsBald:true Language:[Go Python Shell]}

注意这里的 %+v,它能打印出结构体的字段名和值,非常适合调试!

方式二:一步到位的“简历直投式”

// 在声明时直接初始化所有字段
p2 := Programmer{
    Name:     "大神老张",
    Age:      35,
    IsBald:   false, // 看,大神不秃!
    Language: []string{"Go", "Rust", "Assembly"},
}
fmt.Printf("大神老张:%+v\n", p2)

方式三:不讲武德的“顺序赋值式”

// 按字段定义的顺序直接赋值,可以省略字段名
p3 := Programmer{"菜鸟小丽", 22, false, []string{"Go", "Java"}}
fmt.Printf("菜鸟小丽:%+v\n", p3)

这种方式虽然简洁,但一旦结构体字段顺序发生变化,代码就会出错,所以不推荐使用,除非你在写非常简单的测试代码。

第二章:结构体进阶玩法——“套娃”与“继承”

1. 匿名字段:玩一把“俄罗斯套娃”

有时候,一个结构体可以直接包含另一个结构体,而不给它字段名,这就是匿名字段。这有点像其他语言里的“继承”。

// 定义一个“技能包”结构体
type SkillSet struct {
    Backend  []string
    Frontend []string
    DevOps   []string
}

// 定义一个“高级程序员”,他直接“拥有”了一个技能包(匿名字段)
type SeniorProgrammer struct {
    Programmer // 匿名字段,直接嵌入了 Programmer 的所有字段
    SkillSet   // 再嵌入一个 SkillSet
    Salary     float64
}

func main() {
    superman := SeniorProgrammer{}
    superman.Name = "超级赛亚人" // 可以直接访问 Programmer 的字段!
    superman.Age = 40
    superman.Salary = 99999.99
    superman.Backend = []string{"Go", "C++"} // 直接访问 SkillSet 的字段!
    superman.Frontend = []string{"JavaScript"}

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

看,SeniorProgrammer 直接就把 ProgrammerSkillSet 的字段都“吸收”了,可以直接访问。这极大地促进了代码的复用,让结构体之间的关系变得清晰。

2. 给结构体“绑定技能”——方法

在Go里,没有类的概念。但我们可以为结构体定义方法,也就是属于这个结构体的函数。

// 为 Programmer 结构体定义一个“工作”的方法
func (p Programmer) Work() {
    fmt.Printf("我是%s,我正在用%s敲代码...\n", p.Name, p.Language)
}

// 可以为 SeniorProgrammer 也定义一个同名方法,实现“多态”
func (s SeniorProgrammer) Work() {
    fmt.Printf("我是大佬%s,我正在设计系统架构,顺便指导别人用%s。\n", s.Name, s.Backend)
}

func main() {
    p1 := Programmer{Name: "小明", Language: []string{"Go"}}
    s1 := SeniorProgrammer{}
    s1.Name = "老张"
    s1.Backend = []string{"Go", "Java"}

    p1.Work() // 输出:我是小明,我正在用[Go]敲代码...
    s1.Work() // 输出:我是大佬老张,我正在设计系统架构,顺便指导别人用[Go Java]。
}

注意方法定义里的 (p Programmer)(s SeniorProgrammer),这叫接收者,它说明了这个方法属于哪个结构体。

第三章:结构体与JSON——当好数据“翻译官”

在现代Web开发中,结构体和JSON的互转是家常便饭。Go通过标签(Tag) 让这个过程变得异常简单。

标签,就是结构体字段的“小纸条”注释。

// 为了序列化成JSON,我们定义一个带标签的结构体
type ProgrammerForJSON struct {
    Name     string   `json:"name"`           // 告诉JSON库,这个字段在JSON里叫 "name"
    Age      int      `json:"age"`
    IsBald   bool     `json:"is_bald"`
    Language []string `json:"programming_languages"` // JSON里的字段名可以完全不同
}

func main() {
    // 实例化一个对象
    p := ProgrammerForJSON{
        Name:     "JSON大师",
        Age:      30,
        IsBald:   true,
        Language: []string{"Go", "JSON"},
    }

    // 序列化:Go结构体 -> JSON字符串
    jsonData, err := json.Marshal(p)
    if err != nil {
        log.Fatal("序列化失败了:", err)
    }
    fmt.Println(string(jsonData))
    // 输出:{"name":"JSON大师","age":30,"is_bald":true,"programming_languages":["Go","JSON"]}

    // 反序列化:JSON字符串 -> Go结构体
    jsonString := `{"name":"解码侠","age":25,"is_bald":false,"programming_languages":["Python"]}`
    var p2 ProgrammerForJSON
    err = json.Unmarshal([]byte(jsonString), &p2) // 注意这里要传指针 &p2
    if err != nil {
        log.Fatal("反序列化失败了:", err)
    }
    fmt.Printf("反序列化结果:%+v\n", p2)
    // 输出:反序列化结果:{Name:解码侠 Age:25 IsBald:false Language:[Python]}
}

看到了吗?通过 json:"xxx" 这样的标签,我们轻松地实现了Go结构体和JSON数据之间的“翻译”。Marshal 是“编码”,Unmarshal 是“解码”,解码时需要传入目标结构体的指针,因为函数内部需要修改它的值。

第四章:完整实战示例——一个微型的程序员管理系统

光说不练假把式,最后我们来个综合的,把上面的知识点都串起来!

package main

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

// 1. 定义核心结构体
type SkillSet struct {
    Backend  []string `json:"backend"`
    Frontend []string `json:"frontend,omitempty"` // omitempty 表示如果为空,则不在JSON中输出此字段
}

type Programmer struct {
    ID       int       `json:"id"`
    Name     string    `json:"name"`
    SkillSet SkillSet `json:"skill_set"`
}

// 2. 为Programmer绑定方法
// 介绍自己
func (p Programmer) Introduce() {
    fmt.Printf("大家好,我是%d号程序员%s。", p.ID, p.Name)
    if len(p.SkillSet.Backend) > 0 {
        fmt.Printf(" 我擅长后端语言:%v。", p.SkillSet.Backend)
    }
    if len(p.SkillSet.Frontend) > 0 {
        fmt.Printf(" 我也懂点前端:%v。", p.SkillSet.Frontend)
    }
    fmt.Println()
}

// 学习新技能
func (p *Programmer) LearnNewSkill(skill string, isBackend bool) {
    if isBackend {
        p.SkillSet.Backend = append(p.SkillSet.Backend, skill)
    } else {
        p.SkillSet.Frontend = append(p.SkillSet.Frontend, skill)
    }
    fmt.Printf("%s 学会了新技能:%s!\n", p.Name, skill)
}

func main() {
    fmt.Println("===== 欢迎来到程序员管理系统 =====")

    // 3. 创建两个程序员
    programmer1 := Programmer{
        ID:   1,
        Name: "Go语言爱好者",
        SkillSet: SkillSet{
            Backend: []string{"Go", "Python"},
        },
    }

    programmer2 := Programmer{
        ID:   2,
        Name: "全栈大神",
        SkillSet: SkillSet{
            Backend:  []string{"Java", "Go"},
            Frontend: []string{"JavaScript", "Vue"},
        },
    }

    // 4. 让他们自我介绍
    programmer1.Introduce()
    programmer2.Introduce()

    // 5. 学习新技能 (注意这里接收者是指针,所以能修改原对象)
    programmer1.LearnNewSkill("Rust", true)
    programmer2.LearnNewSkill("React", false)

    // 再次介绍,看看变化
    fmt.Println("\n----- 经过一段时间的学习后 -----")
    programmer1.Introduce()
    programmer2.Introduce()

    // 6. 将程序员团队序列化为JSON
    team := []Programmer{programmer1, programmer2}
    jsonData, err := json.MarshalIndent(team, "", "  ") // MarshalIndent 让JSON更美观
    if err != nil {
        log.Fatal("序列化团队数据失败:", err)
    }
    fmt.Println("\n----- 团队JSON数据 -----")
    fmt.Println(string(jsonData))
}

运行这个程序,你会看到:

  1. 程序员的创建和初始化。
  2. 方法的调用(自我介绍)。
  3. 通过指针接收者方法修改结构体内容(学习新技能)。
  4. 最终将整个团队的数据漂亮地格式化成JSON输出。

结语

好啦,关于Go语言结构体的深度之旅就到这儿。结构体就像是Go世界里乐高积木,让你能把零散的数据组装成有意义的、强大的对象。从基础定义到高级的嵌入、方法、JSON处理,掌握了它,你就拿到了编写整洁、高效、易维护Go程序的金钥匙。

别再让你数据“散装”了,赶紧用结构体给它们“拉郎配”,组建你的数据天团吧!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值