结构体(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:}
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)
}
注意:这种方式必须按字段顺序赋值,不推荐使用。
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)
}
| 对比项 | 值接收者 | 指针接收者 |
|---|---|---|
| 修改原对象 | 不能 | 能 |
| 复制开销 | 复制整个结构体 | 只复制指针 |
| 使用场景 | 小结构体、只读 | 大结构体、需要修改 |
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)
}
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()
}
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、取地址 |
| 方法 | 值接收者、指针接收者 |
| 嵌套 | 组合实现代码复用 |
| 标签 | 为字段添加元信息 |