Go语言快速入门Go语言快速入门
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
首页
基础篇
进阶篇
高阶篇
实战篇
Go官方网站
编程指南
  • 基础篇

    • 🚀 基础篇
    • 第1章 - 环境安装
    • 第2章 - Hello World
    • 第3章 - 变量与常量
    • 第4章 - 数据类型
    • 控制流程
    • 函数
    • 数组与切片
    • Map
    • 结构体
    • 指针

结构体

结构体是Go中自定义类型的方式,类似Java的Class,但更简单——没有继承!作为从面向对象语言转过来的人,我一开始非常不适应Go没有类、没有继承的设计。但用久了才发现,组合优于继承,代码更加灵活清晰!

定义结构体

// 定义一个Person结构体
type Person struct {
    Name string
    Age  int
    City string
}

// 定义一个Book结构体
type Book struct {
    Title  string
    Author string
    Price  float64
    Pages  int
}

Java程序员看过来

// Java的类
public class Person {
    private String name;
    private int age;
    private String city;
    // 还要写getter/setter...
}
// Go的结构体 - 简洁很多!
type Person struct {
    Name string
    Age  int
    City string
}

Go没有getter/setter,字段首字母大写就是public,小写就是private(包内可见)。

创建结构体实例

// 方式1:声明后赋值
var p1 Person
p1.Name = "张三"
p1.Age = 25
p1.City = "北京"

// 方式2:字面量创建(推荐!)
p2 := Person{
    Name: "李四",
    Age:  30,
    City: "上海",
}

// 方式3:省略字段名(不推荐,顺序敏感)
p3 := Person{"王五", 28, "深圳"}

// 方式4:部分字段初始化(其他为零值)
p4 := Person{Name: "赵六"}  // Age=0, City=""

// 方式5:new关键字(返回指针)
p5 := new(Person)  // *Person类型
p5.Name = "钱七"

// 方式6:取地址(返回指针,最常用!)
p6 := &Person{
    Name: "孙八",
    Age:  35,
    City: "广州",
}

访问和修改字段

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

// 读取字段
fmt.Println(p.Name)  // 张三
fmt.Println(p.Age)   // 25

// 修改字段
p.Age = 26
p.City = "上海"

// 指针也是用.访问(Go自动解引用!)
pp := &p
fmt.Println(pp.Name)  // 张三(等价于 (*pp).Name)
pp.Age = 27           // 同样可以修改

和C/C++的区别

C/C++中指针访问成员用 ->,Go统一用 .,Go编译器自动处理解引用。

结构体是值类型

p1 := Person{Name: "张三", Age: 25}
p2 := p1  // 复制整个结构体!

p2.Name = "李四"
fmt.Println(p1.Name)  // 张三(原结构体不变)
fmt.Println(p2.Name)  // 李四

传递给函数也是复制:

func birthday(p Person) {
    p.Age++  // 只是修改副本
}

p := Person{Name: "张三", Age: 25}
birthday(p)
fmt.Println(p.Age)  // 25(没变!)

// 要修改原数据,传指针
func birthdayPtr(p *Person) {
    p.Age++  // 修改原数据
}

birthdayPtr(&p)
fmt.Println(p.Age)  // 26(变了!)

结构体标签(Tag)

标签用于给字段添加元信息,常用于JSON、数据库映射等。

type User struct {
    ID        int    `json:"id" db:"user_id"`
    Username  string `json:"username" db:"user_name"`
    Password  string `json:"-"`  // JSON序列化时忽略
    Email     string `json:"email,omitempty"`  // 空值时忽略
    CreatedAt string `json:"created_at"`
}

// JSON序列化
u := User{
    ID:       1,
    Username: "zhangsan",
    Password: "secret123",
    Email:    "",
}

data, _ := json.Marshal(u)
fmt.Println(string(data))
// {"id":1,"username":"zhangsan"}
// Password被忽略了,Email是空值也被忽略了

匿名字段(嵌入)

Go没有继承,但有组合!通过匿名字段实现类似继承的效果。

type Address struct {
    Province string
    City     string
    Street   string
}

type Employee struct {
    Name    string
    Age     int
    Address        // 匿名字段,嵌入Address
}

func main() {
    e := Employee{
        Name: "张三",
        Age:  30,
        Address: Address{
            Province: "广东省",
            City:     "深圳市",
            Street:   "科技园路",
        },
    }
    
    // 直接访问嵌入的字段
    fmt.Println(e.City)      // 深圳市(语法糖)
    fmt.Println(e.Address.City)  // 深圳市(完整路径)
    
    // 修改也一样
    e.City = "广州市"
}

多层嵌入

type Contact struct {
    Phone string
    Email string
}

type Person struct {
    Name string
    Contact  // 嵌入Contact
}

type Employee struct {
    Person    // 嵌入Person
    Company string
}

func main() {
    e := Employee{
        Person: Person{
            Name: "张三",
            Contact: Contact{
                Phone: "13800138000",
                Email: "zhangsan@example.com",
            },
        },
        Company: "阿里巴巴",
    }
    
    // 直接访问深层字段
    fmt.Println(e.Name)   // 张三
    fmt.Println(e.Phone)  // 13800138000
    fmt.Println(e.Email)  // zhangsan@example.com
}

字段名冲突

type A struct {
    Name string
}

type B struct {
    Name string
}

type C struct {
    A
    B
}

func main() {
    c := C{}
    // c.Name = "test"  // ❌ 编译错误:歧义
    c.A.Name = "A的Name"  // ✅ 明确指定
    c.B.Name = "B的Name"  // ✅ 明确指定
}

结构体比较

type Point struct {
    X, Y int
}

p1 := Point{1, 2}
p2 := Point{1, 2}
p3 := Point{1, 3}

fmt.Println(p1 == p2)  // true
fmt.Println(p1 == p3)  // false

注意

只有当结构体的所有字段都是可比较的类型时,结构体才能比较。 包含切片、map、函数的结构体不能用 == 比较!

type Data struct {
    Values []int  // 切片不可比较
}

d1 := Data{Values: []int{1, 2, 3}}
d2 := Data{Values: []int{1, 2, 3}}
// d1 == d2  // ❌ 编译错误

空结构体

空结构体 struct{} 不占用内存,常用于:

// 1. 实现Set(用map[T]struct{})
set := make(map[string]struct{})
set["apple"] = struct{}{}
set["banana"] = struct{}{}

if _, exists := set["apple"]; exists {
    fmt.Println("apple存在")
}

// 2. 信号通道(只需要通知,不需要传数据)
done := make(chan struct{})
go func() {
    // 干活...
    close(done)  // 发送完成信号
}()
<-done  // 等待完成

构造函数

Go没有构造函数,约定用 New 开头的函数代替:

type User struct {
    id   int
    name string
    age  int
}

// "构造函数" - 返回指针是惯例
func NewUser(name string, age int) *User {
    return &User{
        id:   generateID(),  // 假设这是生成ID的函数
        name: name,
        age:  age,
    }
}

// 带验证的构造函数
func NewUserWithValidation(name string, age int) (*User, error) {
    if name == "" {
        return nil, errors.New("name不能为空")
    }
    if age < 0 || age > 150 {
        return nil, errors.New("age不合法")
    }
    return &User{
        id:   generateID(),
        name: name,
        age:  age,
    }, nil
}

实战案例:图书管理系统

package main

import (
    "fmt"
    "time"
)

// 图书结构体
type Book struct {
    ISBN      string
    Title     string
    Author    string
    Price     float64
    Publisher string
    PubDate   time.Time
}

// 图书馆结构体
type Library struct {
    Name     string
    Location string
    Books    []Book
}

// 创建新图书
func NewBook(isbn, title, author string, price float64) *Book {
    return &Book{
        ISBN:   isbn,
        Title:  title,
        Author: author,
        Price:  price,
    }
}

// 创建图书馆
func NewLibrary(name, location string) *Library {
    return &Library{
        Name:     name,
        Location: location,
        Books:    make([]Book, 0),
    }
}

// 添加图书
func (l *Library) AddBook(book Book) {
    l.Books = append(l.Books, book)
}

// 按作者查找图书
func (l *Library) FindByAuthor(author string) []Book {
    result := []Book{}
    for _, book := range l.Books {
        if book.Author == author {
            result = append(result, book)
        }
    }
    return result
}

// 计算图书总价值
func (l *Library) TotalValue() float64 {
    total := 0.0
    for _, book := range l.Books {
        total += book.Price
    }
    return total
}

// 打印图书馆信息
func (l *Library) Print() {
    fmt.Printf("📚 %s(%s)\n", l.Name, l.Location)
    fmt.Printf("   共有 %d 本图书,总价值 ¥%.2f\n", len(l.Books), l.TotalValue())
    fmt.Println("   ────────────────────────────────")
    for i, book := range l.Books {
        fmt.Printf("   %d. 《%s》 - %s  ¥%.2f\n", i+1, book.Title, book.Author, book.Price)
    }
}

func main() {
    // 创建图书馆
    lib := NewLibrary("城市图书馆", "北京市海淀区")
    
    // 添加图书
    lib.AddBook(Book{
        ISBN:   "978-7-111-44277-5",
        Title:  "Go程序设计语言",
        Author: "艾伦·多诺万",
        Price:  79.00,
    })
    lib.AddBook(Book{
        ISBN:   "978-7-111-52580-9",
        Title:  "Go语言实战",
        Author: "威廉·肯尼迪",
        Price:  59.00,
    })
    lib.AddBook(Book{
        ISBN:   "978-7-111-55287-4",
        Title:  "Go Web编程",
        Author: "郑兆雄",
        Price:  69.00,
    })
    lib.AddBook(Book{
        ISBN:   "978-7-121-32829-0",
        Title:  "Go语言高级编程",
        Author: "柴树杉",
        Price:  89.00,
    })
    
    // 打印图书馆信息
    lib.Print()
    
    // 查找特定作者的书
    fmt.Println("\n🔍 查找作者\"柴树杉\"的图书:")
    books := lib.FindByAuthor("柴树杉")
    for _, b := range books {
        fmt.Printf("   《%s》 ¥%.2f\n", b.Title, b.Price)
    }
}

输出:

📚 城市图书馆(北京市海淀区)
   共有 4 本图书,总价值 ¥296.00
   ────────────────────────────────
   1. 《Go程序设计语言》 - 艾伦·多诺万  ¥79.00
   2. 《Go语言实战》 - 威廉·肯尼迪  ¥59.00
   3. 《Go Web编程》 - 郑兆雄  ¥69.00
   4. 《Go语言高级编程》 - 柴树杉  ¥89.00

🔍 查找作者"柴树杉"的图书:
   《Go语言高级编程》 ¥89.00

实战案例:学生信息管理

package main

import (
    "fmt"
    "sort"
)

// 成绩结构体
type Score struct {
    Subject string
    Grade   float64
}

// 学生结构体
type Student struct {
    ID      string
    Name    string
    Age     int
    Class   string
    Scores  []Score
}

// 计算平均分
func (s *Student) Average() float64 {
    if len(s.Scores) == 0 {
        return 0
    }
    total := 0.0
    for _, score := range s.Scores {
        total += score.Grade
    }
    return total / float64(len(s.Scores))
}

// 获取最高分科目
func (s *Student) BestSubject() (string, float64) {
    if len(s.Scores) == 0 {
        return "", 0
    }
    best := s.Scores[0]
    for _, score := range s.Scores[1:] {
        if score.Grade > best.Grade {
            best = score
        }
    }
    return best.Subject, best.Grade
}

// 判断是否及格(所有科目>=60)
func (s *Student) IsPassed() bool {
    for _, score := range s.Scores {
        if score.Grade < 60 {
            return false
        }
    }
    return true
}

// 班级结构体
type Class struct {
    Name     string
    Students []Student
}

// 获取班级平均分
func (c *Class) Average() float64 {
    if len(c.Students) == 0 {
        return 0
    }
    total := 0.0
    for _, s := range c.Students {
        total += s.Average()
    }
    return total / float64(len(c.Students))
}

// 获取排名
func (c *Class) Ranking() []Student {
    // 复制一份避免修改原切片
    ranked := make([]Student, len(c.Students))
    copy(ranked, c.Students)
    
    sort.Slice(ranked, func(i, j int) bool {
        return ranked[i].Average() > ranked[j].Average()
    })
    return ranked
}

func main() {
    // 创建班级
    class := Class{
        Name: "计算机1班",
        Students: []Student{
            {
                ID: "2024001", Name: "张三", Age: 20, Class: "计算机1班",
                Scores: []Score{
                    {"语文", 85},
                    {"数学", 92},
                    {"英语", 78},
                },
            },
            {
                ID: "2024002", Name: "李四", Age: 21, Class: "计算机1班",
                Scores: []Score{
                    {"语文", 90},
                    {"数学", 88},
                    {"英语", 95},
                },
            },
            {
                ID: "2024003", Name: "王五", Age: 20, Class: "计算机1班",
                Scores: []Score{
                    {"语文", 75},
                    {"数学", 55},
                    {"英语", 70},
                },
            },
        },
    }
    
    // 打印班级信息
    fmt.Printf("📚 %s 成绩单\n", class.Name)
    fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
    
    for _, s := range class.Students {
        status := "✅ 及格"
        if !s.IsPassed() {
            status = "❌ 不及格"
        }
        bestSubject, bestGrade := s.BestSubject()
        fmt.Printf("%s(%s)\n", s.Name, s.ID)
        fmt.Printf("   平均分: %.2f  %s\n", s.Average(), status)
        fmt.Printf("   最强科目: %s(%.1f分)\n", bestSubject, bestGrade)
        fmt.Println()
    }
    
    // 打印排名
    fmt.Println("📊 成绩排名")
    fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
    for i, s := range class.Ranking() {
        fmt.Printf("   第%d名: %s  平均分: %.2f\n", i+1, s.Name, s.Average())
    }
    
    fmt.Printf("\n班级平均分: %.2f\n", class.Average())
}

输出:

📚 计算机1班 成绩单
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
张三(2024001)
   平均分: 85.00  ✅ 及格
   最强科目: 数学(92.0分)

李四(2024002)
   平均分: 91.00  ✅ 及格
   最强科目: 英语(95.0分)

王五(2024003)
   平均分: 66.67  ❌ 不及格
   最强科目: 语文(75.0分)

📊 成绩排名
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
   第1名: 李四  平均分: 91.00
   第2名: 张三  平均分: 85.00
   第3名: 王五  平均分: 66.67

班级平均分: 80.89

练习

  1. 定义一个 Rectangle 结构体,包含长和宽,实现计算面积和周长的函数
  2. 定义一个 BankAccount 结构体,实现存款、取款、查询余额功能
  3. 使用嵌入实现一个 Manager 结构体,嵌入 Employee 结构体
参考答案
package main

import (
    "errors"
    "fmt"
)

// 1. Rectangle
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)
}

// 2. BankAccount
type BankAccount struct {
    owner   string
    balance float64
}

func NewBankAccount(owner string) *BankAccount {
    return &BankAccount{owner: owner, balance: 0}
}

func (a *BankAccount) Deposit(amount float64) error {
    if amount <= 0 {
        return errors.New("存款金额必须大于0")
    }
    a.balance += amount
    return nil
}

func (a *BankAccount) Withdraw(amount float64) error {
    if amount <= 0 {
        return errors.New("取款金额必须大于0")
    }
    if amount > a.balance {
        return errors.New("余额不足")
    }
    a.balance -= amount
    return nil
}

func (a *BankAccount) Balance() float64 {
    return a.balance
}

// 3. Manager嵌入Employee
type Employee struct {
    Name   string
    Salary float64
}

type Manager struct {
    Employee            // 嵌入Employee
    Department string
    TeamSize   int
}

func main() {
    // 1. 测试Rectangle
    rect := Rectangle{Width: 10, Height: 5}
    fmt.Printf("矩形: 面积=%.2f, 周长=%.2f\n", rect.Area(), rect.Perimeter())
    
    // 2. 测试BankAccount
    account := NewBankAccount("张三")
    account.Deposit(1000)
    account.Withdraw(300)
    fmt.Printf("账户余额: ¥%.2f\n", account.Balance())
    
    // 3. 测试Manager
    mgr := Manager{
        Employee:   Employee{Name: "李四", Salary: 20000},
        Department: "技术部",
        TeamSize:   10,
    }
    fmt.Printf("经理: %s, 部门: %s, 团队: %d人\n", mgr.Name, mgr.Department, mgr.TeamSize)
}

结构体是Go面向对象的基础,下一节学习指针——理解Go的内存!

最近更新: 2025/12/27 13:26
Contributors: 王长安
Prev
Map
Next
指针