Go 语言结构体
结构体是 Go 语言中用于创建自定义数据类型的重要特性。它允许将不同类型的数据组合成一个逻辑单元,是面向对象编程的基础。
📋 结构体基础
结构体定义和初始化
go
package main
import "fmt"
// 定义结构体
type Person struct {
Name string
Age int
Email string
Height float64
IsAdult bool
}
func main() {
// 1. 零值初始化
var p1 Person
fmt.Printf("零值结构体: %+v\n", p1)
// 2. 字段赋值
p1.Name = "张三"
p1.Age = 25
p1.Email = "zhangsan@example.com"
p1.Height = 1.75
p1.IsAdult = true
fmt.Printf("赋值后: %+v\n", p1)
// 3. 结构体字面量初始化
p2 := Person{
Name: "李四",
Age: 30,
Email: "lisi@example.com",
Height: 1.80,
IsAdult: true,
}
fmt.Printf("字面量初始化: %+v\n", p2)
// 4. 简化初始化(按字段顺序)
p3 := Person{"王五", 28, "wangwu@example.com", 1.70, true}
fmt.Printf("顺序初始化: %+v\n", p3)
// 5. 部分初始化
p4 := Person{
Name: "赵六",
Age: 22,
}
fmt.Printf("部分初始化: %+v\n", p4)
}匿名结构体
go
func main() {
// 匿名结构体定义和使用
student := struct {
ID int
Name string
Grades []int
}{
ID: 1001,
Name: "学生A",
Grades: []int{85, 92, 78, 96},
}
fmt.Printf("匿名结构体: %+v\n", student)
// 函数中使用匿名结构体
config := struct {
Host string
Port int
SSL bool
}{"localhost", 8080, false}
fmt.Printf("配置: %+v\n", config)
// 切片中的匿名结构体
users := []struct {
Name string
Role string
}{
{"管理员", "admin"},
{"用户1", "user"},
{"用户2", "user"},
}
for _, user := range users {
fmt.Printf("用户: %s, 角色: %s\n", user.Name, user.Role)
}
}🔧 结构体方法
方法定义
go
import (
"fmt"
"math"
)
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 (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
// 指针接收者方法
func (r *Rectangle) SetDimensions(width, height float64) {
r.Width = width
r.Height = height
}
func main() {
rect := Rectangle{Width: 10, Height: 5}
fmt.Printf("原始矩形: %+v\n", rect)
fmt.Printf("面积: %.2f\n", rect.Area())
fmt.Printf("周长: %.2f\n", rect.Perimeter())
// 调用指针接收者方法
rect.Scale(2)
fmt.Printf("放大2倍后: %+v\n", rect)
fmt.Printf("新面积: %.2f\n", rect.Area())
// 设置新尺寸
rect.SetDimensions(8, 6)
fmt.Printf("设置新尺寸后: %+v\n", rect)
}值接收者 vs 指针接收者
go
type Counter struct {
count int
}
// 值接收者:不会修改原始结构体
func (c Counter) IncrementValue() {
c.count++
fmt.Printf("值接收者内部: %d\n", c.count)
}
// 指针接收者:会修改原始结构体
func (c *Counter) IncrementPointer() {
c.count++
fmt.Printf("指针接收者内部: %d\n", c.count)
}
// 值接收者:获取当前值
func (c Counter) GetCount() int {
return c.count
}
func main() {
counter := Counter{count: 0}
fmt.Printf("初始值: %d\n", counter.GetCount())
// 值接收者调用
counter.IncrementValue()
fmt.Printf("值接收者调用后: %d\n", counter.GetCount())
// 指针接收者调用
counter.IncrementPointer()
fmt.Printf("指针接收者调用后: %d\n", counter.GetCount())
// 继续调用指针接收者
counter.IncrementPointer()
fmt.Printf("再次指针接收者调用后: %d\n", counter.GetCount())
}🏗️ 结构体嵌套
嵌套结构体
go
type Address struct {
Street string
City string
Province string
ZipCode string
}
type Contact struct {
Phone string
Email string
}
type Employee struct {
ID int
Name string
Address Address // 嵌套结构体
Contact Contact // 嵌套结构体
Salary float64
IsActive bool
}
func main() {
emp := Employee{
ID: 1001,
Name: "张三",
Address: Address{
Street: "中关村大街1号",
City: "北京",
Province: "北京市",
ZipCode: "100000",
},
Contact: Contact{
Phone: "13800138000",
Email: "zhangsan@company.com",
},
Salary: 8000.0,
IsActive: true,
}
fmt.Printf("员工信息: %+v\n", emp)
// 访问嵌套字段
fmt.Printf("员工姓名: %s\n", emp.Name)
fmt.Printf("员工城市: %s\n", emp.Address.City)
fmt.Printf("员工邮箱: %s\n", emp.Contact.Email)
// 修改嵌套字段
emp.Address.City = "上海"
emp.Contact.Phone = "13900139000"
fmt.Printf("修改后地址: %s\n", emp.Address.City)
fmt.Printf("修改后电话: %s\n", emp.Contact.Phone)
}匿名字段(嵌入)
go
type Person struct {
Name string
Age int
}
// Person 方法
func (p Person) Introduce() {
fmt.Printf("我是 %s,今年 %d 岁\n", p.Name, p.Age)
}
type Student struct {
Person // 匿名字段(嵌入)
StudentID string
Major string
}
// Student 特有方法
func (s Student) Study() {
fmt.Printf("%s 正在学习 %s\n", s.Name, s.Major)
}
type Teacher struct {
Person // 匿名字段(嵌入)
EmployeeID string
Subject string
}
// Teacher 特有方法
func (t Teacher) Teach() {
fmt.Printf("%s 正在教授 %s\n", t.Name, t.Subject)
}
func main() {
// 创建学生
student := Student{
Person: Person{
Name: "小明",
Age: 20,
},
StudentID: "S001",
Major: "计算机科学",
}
// 直接访问嵌入字段
fmt.Printf("学生姓名: %s\n", student.Name) // 直接访问,等价于 student.Person.Name
fmt.Printf("学生年龄: %d\n", student.Age)
fmt.Printf("学号: %s\n", student.StudentID)
// 调用嵌入类型的方法
student.Introduce() // 调用 Person 的方法
student.Study() // 调用 Student 的方法
// 创建教师
teacher := Teacher{
Person: Person{
Name: "王老师",
Age: 35,
},
EmployeeID: "T001",
Subject: "数学",
}
teacher.Introduce() // 调用 Person 的方法
teacher.Teach() // 调用 Teacher 的方法
}🔗 结构体标签
JSON 标签
go
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"-"` // 忽略字段
Age int `json:"age,omitempty"` // 空值时忽略
IsActive bool `json:"is_active"`
Profile string `json:"profile,omitempty"`
}
func main() {
user := User{
ID: 1,
Name: "张三",
Email: "zhangsan@example.com",
Password: "secret123",
Age: 0, // 空值
IsActive: true,
// Profile 未设置
}
// 结构体转 JSON
jsonData, err := json.Marshal(user)
if err != nil {
fmt.Printf("JSON 序列化错误: %v\n", err)
return
}
fmt.Printf("JSON 数据: %s\n", jsonData)
// JSON 转结构体
jsonStr := `{"id":2,"name":"李四","email":"lisi@example.com","age":25,"is_active":false,"profile":"软件工程师"}`
var newUser User
err = json.Unmarshal([]byte(jsonStr), &newUser)
if err != nil {
fmt.Printf("JSON 反序列化错误: %v\n", err)
return
}
fmt.Printf("反序列化结果: %+v\n", newUser)
}验证标签
go
import (
"fmt"
"reflect"
"strconv"
"strings"
)
type Product struct {
ID int `validate:"required,min:1"`
Name string `validate:"required,max:100"`
Price float64 `validate:"required,min:0"`
Category string `validate:"required"`
Description string `validate:"max:500"`
}
// 简单的验证器
func validateStruct(s interface{}) []string {
var errors []string
v := reflect.ValueOf(s)
t := reflect.TypeOf(s)
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fieldType := t.Field(i)
tag := fieldType.Tag.Get("validate")
if tag == "" {
continue
}
rules := strings.Split(tag, ",")
fieldName := fieldType.Name
for _, rule := range rules {
err := validateField(fieldName, field, rule)
if err != "" {
errors = append(errors, err)
}
}
}
return errors
}
func validateField(fieldName string, field reflect.Value, rule string) string {
switch {
case rule == "required":
if field.Kind() == reflect.String && field.String() == "" {
return fmt.Sprintf("%s is required", fieldName)
}
if field.Kind() == reflect.Int && field.Int() == 0 {
return fmt.Sprintf("%s is required", fieldName)
}
if field.Kind() == reflect.Float64 && field.Float() == 0 {
return fmt.Sprintf("%s is required", fieldName)
}
case strings.HasPrefix(rule, "min:"):
minStr := strings.TrimPrefix(rule, "min:")
min, _ := strconv.Atoi(minStr)
if field.Kind() == reflect.Int && field.Int() < int64(min) {
return fmt.Sprintf("%s must be at least %d", fieldName, min)
}
if field.Kind() == reflect.Float64 && field.Float() < float64(min) {
return fmt.Sprintf("%s must be at least %d", fieldName, min)
}
case strings.HasPrefix(rule, "max:"):
maxStr := strings.TrimPrefix(rule, "max:")
max, _ := strconv.Atoi(maxStr)
if field.Kind() == reflect.String && len(field.String()) > max {
return fmt.Sprintf("%s must be at most %d characters", fieldName, max)
}
}
return ""
}
func main() {
// 有效产品
validProduct := Product{
ID: 1,
Name: "笔记本电脑",
Price: 5999.99,
Category: "电子产品",
Description: "高性能笔记本电脑",
}
errors := validateStruct(validProduct)
if len(errors) == 0 {
fmt.Println("有效产品验证通过")
} else {
fmt.Printf("验证错误: %v\n", errors)
}
// 无效产品
invalidProduct := Product{
ID: 0, // 违反 min:1
Name: "", // 违反 required
Price: -100, // 违反 min:0
Category: "电子产品",
Description: "",
}
errors = validateStruct(invalidProduct)
if len(errors) > 0 {
fmt.Println("无效产品验证错误:")
for _, err := range errors {
fmt.Printf(" - %s\n", err)
}
}
}🎯 实际应用示例
学生管理系统
go
import (
"fmt"
"time"
)
// 成绩结构体
type Grade struct {
Subject string
Score float64
Date time.Time
}
// 学生结构体
type Student struct {
ID string
Name string
Age int
Email string
Grades []Grade
GPA float64
}
// 计算 GPA
func (s *Student) CalculateGPA() {
if len(s.Grades) == 0 {
s.GPA = 0
return
}
total := 0.0
for _, grade := range s.Grades {
total += grade.Score
}
s.GPA = total / float64(len(s.Grades))
}
// 添加成绩
func (s *Student) AddGrade(subject string, score float64) {
grade := Grade{
Subject: subject,
Score: score,
Date: time.Now(),
}
s.Grades = append(s.Grades, grade)
s.CalculateGPA()
}
// 获取最高分
func (s Student) GetHighestGrade() (Grade, bool) {
if len(s.Grades) == 0 {
return Grade{}, false
}
highest := s.Grades[0]
for _, grade := range s.Grades[1:] {
if grade.Score > highest.Score {
highest = grade
}
}
return highest, true
}
// 学生信息显示
func (s Student) DisplayInfo() {
fmt.Printf("=== 学生信息 ===\n")
fmt.Printf("学号: %s\n", s.ID)
fmt.Printf("姓名: %s\n", s.Name)
fmt.Printf("年龄: %d\n", s.Age)
fmt.Printf("邮箱: %s\n", s.Email)
fmt.Printf("GPA: %.2f\n", s.GPA)
fmt.Printf("成绩列表:\n")
for _, grade := range s.Grades {
fmt.Printf(" %s: %.1f (日期: %s)\n",
grade.Subject, grade.Score, grade.Date.Format("2006-01-02"))
}
if highest, ok := s.GetHighestGrade(); ok {
fmt.Printf("最高分: %s %.1f分\n", highest.Subject, highest.Score)
}
}
// 班级结构体
type Class struct {
Name string
Students []Student
}
// 添加学生
func (c *Class) AddStudent(student Student) {
c.Students = append(c.Students, student)
}
// 获取班级平均 GPA
func (c Class) GetAverageGPA() float64 {
if len(c.Students) == 0 {
return 0
}
total := 0.0
for _, student := range c.Students {
total += student.GPA
}
return total / float64(len(c.Students))
}
// 获取最优学生
func (c Class) GetTopStudent() (Student, bool) {
if len(c.Students) == 0 {
return Student{}, false
}
top := c.Students[0]
for _, student := range c.Students[1:] {
if student.GPA > top.GPA {
top = student
}
}
return top, true
}
func main() {
// 创建学生
student1 := Student{
ID: "S001",
Name: "张三",
Age: 20,
Email: "zhangsan@university.edu",
}
student2 := Student{
ID: "S002",
Name: "李四",
Age: 21,
Email: "lisi@university.edu",
}
// 添加成绩
student1.AddGrade("数学", 95)
student1.AddGrade("英语", 88)
student1.AddGrade("计算机", 92)
student2.AddGrade("数学", 87)
student2.AddGrade("英语", 94)
student2.AddGrade("计算机", 89)
// 显示学生信息
student1.DisplayInfo()
fmt.Println()
student2.DisplayInfo()
// 创建班级
class := Class{Name: "计算机科学2021级"}
class.AddStudent(student1)
class.AddStudent(student2)
fmt.Printf("\n=== 班级统计 ===\n")
fmt.Printf("班级: %s\n", class.Name)
fmt.Printf("学生人数: %d\n", len(class.Students))
fmt.Printf("班级平均GPA: %.2f\n", class.GetAverageGPA())
if top, ok := class.GetTopStudent(); ok {
fmt.Printf("班级第一名: %s (GPA: %.2f)\n", top.Name, top.GPA)
}
}🎓 小结
本章我们全面学习了 Go 语言的结构体:
- ✅ 结构体基础:定义、初始化、匿名结构体
- ✅ 结构体方法:值接收者、指针接收者
- ✅ 结构体嵌套:嵌套结构体、匿名字段
- ✅ 结构体标签:JSON 标签、验证标签
- ✅ 实际应用:学生管理系统实现
结构体是 Go 语言面向对象编程的基础,掌握结构体的使用对于构建复杂应用程序至关重要。
接下来,我们将学习 Go 语言集合(Map),了解键值对数据结构的使用。
结构体使用建议
- 选择合适的接收者类型(值 vs 指针)
- 合理使用结构体嵌套和组合
- 利用结构体标签进行数据验证和序列化
- 为结构体设计清晰的方法接口