在 Go 语言中,结构体(struct) 是一种用户自定义的聚合数据类型,它可以将多个不同类型的字段组合在一起,形成一个整体。结构体类似于其他编程语言中的“类”或“记录”,但 Go 语言本身不支持类和继承,而是通过结构体和接口来实现面向对象编程的特性。
1. 定义结构体
使用 type 关键字定义结构体,并使用 struct 关键字表示结构体类型。例如:
package main
import "fmt"
// 定义一个结构体
type Person struct {
Name string
Age int
}
func main() {
// 创建结构体实例
p := Person{"张三", 25}
fmt.Println(p) // 输出: {张三 25}
}
2. 结构体字段访问和修改
可以使用 点号(.) 访问和修改结构体的字段:
p := Person{"李四", 30}
fmt.Println(p.Name) // 访问字段
p.Age = 35 // 修改字段
fmt.Println(p.Age) // 输出: 35
3. 结构体实例化的方式
3.1 直接赋值
p1 := Person{"王五", 40}
3.2 指定字段名称
p2 := Person{Name: "赵六", Age: 28}
3.3 使用new
p3 := new(Person) // 返回的是指针 *Person
p3.Name = "小明"
p3.Age = 22
fmt.Println(*p3) // 输出: {小明 22}
3.4 取地址符&
p4 := &Person{"小红", 18}
fmt.Println(p4.Name) // Go 自动解引用
4. 结构体方法
结构体可以定义方法,方法的接收者可以是 值类型 或 指针类型:
package main
import "fmt"
type Person struct {
Name string
Age int
}
// 方法(值类型接收者)
func (p Person) SayHello() {
fmt.Println("Hello, 我是", p.Name)
}
// 方法(指针类型接收者,修改值)
func (p *Person) GrowUp() {
p.Age += 1
}
func main() {
p := Person{"小明", 20}
p.SayHello() // 输出: Hello, 我是 小明
p.GrowUp()
fmt.Println(p.Age) // 输出: 21
}
注意:
- 如果方法的接收者是值类型 func (p Person) Method(),它不会修改原始结构体。
- 如果方法的接收者是指针 func (p *Person) Method(),它可以修改原始结构体。
5. 结构体嵌套和继承(模拟继承)
Go 语言没有类和继承,但可以通过结构体嵌套来实现类似继承的功能:
type Animal struct {
Name string
}
func (a Animal) Speak() {
fmt.Println(a.Name, "会叫")
}
type Dog struct {
Animal // 结构体嵌套
Breed string
}
func main() {
d := Dog{Animal{"小狗"}, "哈士奇"}
d.Speak() // 小狗 会叫
fmt.Println(d.Breed) // 哈士奇
}
这里 Dog 继承了 Animal 的 Speak 方法。
6. 结构体与json互转
结构体可以与 JSON 相互转换:
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
// 结构体转 JSON
p := Person{"小李", 26}
jsonData, _ := json.Marshal(p)
fmt.Println(string(jsonData)) // 输出: {"name":"小李","age":26}
// JSON 转结构体
jsonString := `{"name":"小王","age":30}`
var p2 Person
json.Unmarshal([]byte(jsonString), &p2)
fmt.Println(p2) // 输出: {小王 30}
}
注意:
- 通过 json:"name" 指定 JSON 字段名,防止 Go 结构体字段大小写问题。
7. 结构体标签(Tag)
结构体的字段可以带 Tag,用于 JSON、数据库等格式转换:
type User struct {
ID int `json:"id" db:"user_id"`
Name string `json:"name" db:"username"`
}
- json:"id" 用于 JSON 序列化时字段名称。
- db:"user_id" 用于数据库 ORM 访问时的字段名称。
8. 结构体比较
Go 语言的结构体可以直接使用 == 或 != 进行比较(但结构体不能包含不可比较的字段,如 map 或 slice):
type Point struct {
X, Y int
}
p1 := Point{1, 2}
p2 := Point{1, 2}
fmt.Println(p1 == p2) // 输出: true
9. 结构体作为函数参数
结构体可以作为函数参数,通常有两种方式:
9.1 传值(不会影响原结构体)
func PrintPerson(p Person) {
fmt.Println(p.Name, p.Age)
}
调用时:
PrintPerson(p) // 传递的是副本
9.2 传指针(可以修改原结构体)
func UpdateAge(p *Person) {
p.Age += 1
}
调用时:
UpdateAge(&p) // 传递的是地址
10. 结构体内存对齐
Go 语言的结构体在内存中会进行 内存对齐,优化性能。一般来说:
- 较大的字段应放前面,减少内存浪费。
- 使用 unsafe.Sizeof() 查看结构体大小:
import "unsafe"
fmt.Println(unsafe.Sizeof(Person{})) // 输出结构体所占字节数
总结
结构体特性 | 说明 |
定义结构体 | type StructName struct {} |
访问字段 | 使用 . 访问字段 |
结构体方法 | 使用 (s Struct) Method() 或 (*s Struct) Method() |
结构体嵌套 | 通过嵌套结构体实现类似继承 |
结构体与 JSON | json.Marshal() / json.Unmarshal() |
结构体标签 | json:"name" / db:"column_name" |
结构体比较 | == / !=(不可包含 map/slice) |
结构体传参 | 传值(副本) / 传指针(修改原结构体) |
结构体是 Go 语言中非常重要的特性,它提供了类似面向对象编程的功能,但同时保持了 Go 语言的简洁性和高效性。