Go初级之五:结构体与方法

发布于:2025-09-01 ⋅ 阅读:(20) ⋅ 点赞:(0)

Go初级之五:结构体与方法

在Go语言中,结构体(struct)方法(method) 是实现面向对象编程的核心机制。虽然Go不是传统意义上的面向对象语言(没有类、继承等概念),但通过结构体和方法的组合,可以非常优雅地组织代码。


一、结构体(Struct)

1. 什么是结构体?

结构体是一种自定义的数据类型,用于将多个不同类型的数据字段组合在一起,形成一个有意义的整体。

类比:就像“学生”这个概念,包含姓名、年龄、成绩等多个属性。

2. 定义结构体

type Student struct {
    Name  string
    Age   int
    Score float64
}
  • type:关键字,用于定义新类型。
  • Student:结构体名称(大写表示对外可见)。
  • {} 内是字段列表,每个字段有名字和类型。

3. 创建结构体实例

方式一:使用 struct{} 字面量
s1 := Student{
    Name: "张三",
    Age: 18,
    Score: 95.5,
}
方式二:按顺序赋值(不推荐,易错)
s2 := Student{"李四", 17, 88.0}
方式三:new 创建指针
s3 := new(Student)
s3.Name = "王五"
s3.Age = 19
s3.Score = 90.0
// 等价于 s3 := &Student{}

4. 访问结构体字段

fmt.Println(s1.Name)   // 输出:张三
fmt.Println(s1.Age)    // 输出:18
s1.Score = 96.0        // 修改字段

二、方法(Method)

1. 什么是方法?

方法是绑定到某个类型上的函数。在Go中,你可以为结构体(甚至基本类型)定义方法。

2. 定义方法

语法:

func (接收者 变量名 类型) 方法名(参数列表) 返回值 {
    // 方法体
}
示例:为 Student 定义一个方法
func (s Student) PrintInfo() {
    fmt.Printf("姓名: %s, 年龄: %d, 成绩: %.2f\n", s.Name, s.Age, s.Score)
}

func (s Student) IsPass() bool {
    return s.Score >= 60
}
调用方法:
s := Student{"张三", 18, 95.5}
s.PrintInfo()  // 姓名: 张三, 年龄: 18, 成绩: 95.50
fmt.Println(s.IsPass()) // true

3. 值接收者 vs 指针接收者

❌ 值接收者(不会修改原结构体)
func (s Student) SetName(name string) {
    s.Name = name // 只修改副本
}

调用后原结构体不变。

✅ 指针接收者(可以修改原结构体)
func (s *Student) SetName(name string) {
    s.Name = name // 修改原始结构体
}

调用:

s := Student{"张三", 18, 95.5}
s.SetName("李四")
fmt.Println(s.Name) // 输出:李四

建议:如果方法需要修改结构体,或结构体较大(避免复制开销),使用指针接收者。


三、结构体的高级用法

1. 匿名字段(模拟“继承”)

Go没有继承,但可以通过匿名字段实现类似效果。

type Person struct {
    Name string
    Age  int
}

type Student struct {
    Person  // 匿名字段
    Score   float64
    School  string
}

使用:

s := Student{
    Person: Person{Name: "张三", Age: 18},
    Score:  95.5,
    School: "清华",
}

fmt.Println(s.Name)  // 直接访问匿名字段的属性
fmt.Println(s.Age)
fmt.Println(s.Score)

这叫“组合”,不是继承,是Go推崇的设计模式。


2. 结构体标签(Struct Tag)——常用于JSON序列化

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

使用 json.Marshal

u := User{ID: 1, Name: "Alice", Age: 25}
data, _ := json.Marshal(u)
fmt.Println(string(data)) 
// 输出:{"id":1,"name":"Alice","age":25}

常见用途:JSON、数据库ORM(如GORM)、表单验证等。


四、完整示例:学生管理系统

package main

import "fmt"

// 1. 定义结构体
type Student struct {
    Name  string
    Age   int
    Score float64
}

// 2. 定义方法
func (s Student) Print() {
    fmt.Printf("学生: %s, 年龄: %d, 成绩: %.2f\n", s.Name, s.Age, s.Score)
}

func (s *Student) Promote() {
    s.Age++
    fmt.Printf("%s 升了一岁,现在 %d 岁\n", s.Name, s.Age)
}

func (s Student) IsExcellent() bool {
    return s.Score >= 90
}

// 3. 主函数
func main() {
    s := Student{"张三", 18, 95.5}
    
    s.Print()           // 学生: 张三, 年龄: 18, 成绩: 95.50
    fmt.Println(s.IsExcellent()) // true
    
    s.Promote()         // 张三 升了一岁,现在 19 岁
}

五、最佳实践与注意事项

项目 建议
结构体名 驼峰命名,首字母大写表示导出
字段名 同上,小写为包内私有
方法接收者 小结构用值接收者,大结构或需修改时用指针
组合代替继承 多用匿名字段组合,少用嵌套
使用Tag JSON、数据库操作时务必使用

六、常见面试题

  1. 值接收者和指针接收者的区别?

    • 值接收者:操作副本,不改变原值。
    • 指针接收者:操作原值,可修改。
  2. Go有继承吗?

    • 没有,但可以通过匿名字段实现组合。
  3. 什么时候用指针接收者?

    • 方法会修改结构体。
    • 结构体较大(避免复制开销)。
    • 保持一致性(同一个类型的方法尽量统一用指针或值)。

✅ 总结

概念 说明
struct 自定义复合数据类型
method 绑定到类型的函数
接收者 (s Type)(s *Type)
匿名字段 实现组合,“has-a”关系
Struct Tag 元信息,用于JSON、ORM等

下一节预告:Go初级之六:接口(Interface)—— 实现多态与解耦


网站公告

今日签到

点亮在社区的每一天
去签到