01. 结构体定义和实例化
Go 语言不是面向对象语言,但用结构体和方法模拟了面向对象。struct 是一组字段的集合,用 type 关键字定义。实例化有好几种方式:字面量初始化、new 函数返回指针、直接声明后赋值。Go 没有构造函数,习惯用一个 NewXxx 工厂函数来创建和初始化结构体。
go
type User struct {
ID int
Name string
Email string
Age int
}
// 实例化
u1 := User{1, "小明", "[email protected]", 18}
u2 := User{Name: "小红", Email: "[email protected]"} // 指定字段
u3 := new(User) // 返回指针
// 工厂函数(惯例)
func NewUser(name string) *User {
return &User{Name: name, Age: 0}
}结构体字段名首字母大写表示公开,小写表示私有(包外无法访问)。
02. 方法:给 struct 绑定行为
方法就是带接收者的函数。接收者写在 func 和函数名之间,可以是值接收者也可以是指针接收者。值接收者操作的是副本,不会修改原值。指针接收者操作的是原对象,可以修改字段。大部分情况用指针接收者,避免拷贝和能修改原值。
go
type Counter struct {
count int
}
// 指针接收者 - 能修改原值
func (c *Counter) Increment() {
c.count++
}
// 值接收者 - 不修改原值
func (c Counter) Value() int {
return c.count
}
c := Counter{}
c.Increment() // count 变成 1
c.Increment() // count 变成 2简单判断:如果方法要修改接收者,或者接收者是个大结构体,用指针接收者。
03. 结构体嵌套和组合
Go 没有继承,用的是组合(Composition)。一个结构体可以嵌套另一个结构体,嵌入式字段的方法和属性会被提升到外层。这跟继承很像但不是继承,底层还是两个独立的结构体。组合比继承更灵活,不会有深层次继承的复杂性。
go
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "..."
}
type Dog struct {
Animal // 嵌入式字段
Breed string
}
func (d Dog) Speak() string {
return "汪汪"
}
d := Dog{Animal{Name: "旺财"}, "金毛"}
fmt.Println(d.Name) // 自动提升,直接用
fmt.Println(d.Speak()) // "汪汪",覆盖了Animal的Speak嵌入式字段实现了类似继承的效果,但本质上还是组合,Go 的设计哲学是组合优于继承。
04. 接口:隐式实现
Go 的接口是隐式实现的,不需要显式声明 implements。只要你这个类型实现了接口要求的所有方法,你就自动实现了这个接口。这种鸭子类型的设计让代码非常灵活,不依赖具体类型只依赖行为。空接口 interface{} 可以接收任何值,类似 Java 的 Object。
go
type Speaker interface {
Speak() string
}
func Greet(s Speaker) {
fmt.Println(s.Speak())
}
// Dog 有 Speak() 方法,自动实现了 Speaker 接口
Greet(Dog{})
// 空接口
var anything interface{}
anything = 42
anything = "hello"
anything = User{}接口要小而精,单一职责。标准库的 io.Reader 只有一个 Read 方法,无比强大。
05. 结构体标签 Tag
结构体 Tag 是写在字段后面的字符串,反射可以读取。最常用的是 JSON 序列化:json:"fieldname" 指定 JSON 字段名,json:"-" 表示忽略,json:"name,omitempty" 表示空值时不输出。还有用于数据库映射、表单验证等场景的标签。
go
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Password string `json:"-"` // 不序列化
Email string `json:"email,omitempty"` // 空就不输出
Age int `json:"age" validate:"min=0,max=120"`
}
u := User{ID: 1, Name: "小明"}
b, _ := json.Marshal(u)
// {"id":1,"name":"小明","age":0}
// 注意 Password 被忽略了,Email 为空也忽略了json:"name,omitempty" 在 name 为零值时不输出,对指针类型要注意 nil 和空字符串的区别。
知识测验
第 1/4 题正确 0
Go 结构体方法的值接收者和指针接收者的区别?
下一节
下一节 并发编程