结构体
结构体是Go语言中用于组合不同类型数据的自定义类型。
定义结构体
基本定义
go
type Person struct {
Name string
Age int
City string
}创建结构体实例
go
package main
import "fmt"
type Person struct {
Name string
Age int
City string
}
func main() {
// 按顺序初始化
person1 := Person{"张三", 25, "北京"}
fmt.Println(person1)
// 使用字段名初始化(推荐)
person2 := Person{
Name: "李四",
Age: 30,
City: "上海",
}
fmt.Println(person2)
// 部分初始化
person3 := Person{Name: "王五"}
fmt.Println(person3) // {王五 0 }
// 零值创建
var person4 Person
fmt.Println(person4) // { 0 }
// 使用new创建指针
person5 := new(Person)
fmt.Println(person5) // &{ 0 }
}访问和修改字段
go
person := Person{Name: "张三", Age: 25}
// 访问字段
fmt.Println(person.Name) // 张三
fmt.Println(person.Age) // 25
// 修改字段
person.Age = 26
fmt.Println(person.Age) // 26
// 通过指针访问
ptr := &person
fmt.Println(ptr.Name) // 张三(自动解引用)
ptr.Age = 27
fmt.Println(person.Age) // 27结构体方法
值接收者方法
go
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() {
rect := Rectangle{Width: 10, Height: 5}
fmt.Printf("面积: %.2f\n", rect.Area())
fmt.Printf("周长: %.2f\n", rect.Perimeter())
}指针接收者方法
go
type Rectangle struct {
Width float64
Height float64
}
// 指针接收者方法(可以修改原值)
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
func main() {
rect := Rectangle{Width: 10, Height: 5}
rect.Scale(2)
fmt.Printf("缩放后的宽度: %.2f, 高度: %.2f\n", rect.Width, rect.Height)
// 输出: 缩放后的宽度: 20.00, 高度: 10.00
}值接收者 vs 指针接收者
| 特性 | 值接收者 | 指针接收者 |
|---|---|---|
| 修改原值 | 否 | 是 |
| 性能 | 小结构体适合 | 大结构体适合 |
| 一致性 | - | 推荐(同一类型的方法保持一致) |
结构体嵌套
匿名字段
go
type Address struct {
City string
Country string
}
type Person struct {
Name string
Age int
Address // 匿名字段(嵌入)
}
func main() {
p := Person{
Name: "张三",
Age: 25,
Address: Address{
City: "北京",
Country: "中国",
},
}
// 直接访问嵌入字段
fmt.Println(p.City) // 北京
fmt.Println(p.Country) // 中国
}字段提升
go
type Inner struct {
X int
}
type Outer struct {
Inner
X int
}
func main() {
o := Outer{
Inner: Inner{X: 10},
X: 20,
}
fmt.Println(o.X) // 20 (外层字段)
fmt.Println(o.Inner.X) // 10 (内层字段)
}结构体标签
结构体标签用于为字段添加元数据:
go
type User struct {
Name string `json:"name" validate:"required"`
Age int `json:"age" validate:"min=0,max=150"`
}
func main() {
u := User{Name: "张三", Age: 25}
// 使用json标签序列化
data, _ := json.Marshal(u)
fmt.Println(string(data)) // {"name":"张三","age":25}
}构造函数
Go没有构造函数,通常使用工厂函数:
go
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.Println(p)
}结构体比较
go
type Point struct {
X, Y int
}
func main() {
p1 := Point{1, 2}
p2 := Point{1, 2}
p3 := Point{2, 3}
fmt.Println(p1 == p2) // true
fmt.Println(p1 == p3) // false
}注意
如果结构体包含不可比较的字段(如切片、Map),则结构体本身也不可比较。
结构体方法集
go
type MyInt int
func (m MyInt) ValueMethod() {
fmt.Println("值方法")
}
func (m *MyInt) PointerMethod() {
fmt.Println("指针方法")
}
func main() {
var v MyInt
var p *MyInt = &v
// 值变量可以调用值方法
v.ValueMethod()
// 值变量也可以调用指针方法(自动取地址)
v.PointerMethod()
// 指针变量可以调用所有方法
p.ValueMethod()
p.PointerMethod()
}实用示例
链表节点
go
type Node struct {
Value int
Next *Node
}
func main() {
// 创建链表: 1 -> 2 -> 3
n3 := &Node{Value: 3}
n2 := &Node{Value: 2, Next: n3}
n1 := &Node{Value: 1, Next: n2}
// 遍历链表
for node := n1; node != nil; node = node.Next {
fmt.Println(node.Value)
}
}二叉树节点
go
type TreeNode struct {
Value int
Left *TreeNode
Right *TreeNode
}
func (t *TreeNode) Insert(value int) {
if value < t.Value {
if t.Left == nil {
t.Left = &TreeNode{Value: value}
} else {
t.Left.Insert(value)
}
} else {
if t.Right == nil {
t.Right = &TreeNode{Value: value}
} else {
t.Right.Insert(value)
}
}
}