整体介绍
虽然 Go 语言不是传统意义上的面向对象语言,但它提供了结构体(struct)来组织数据,并且可以为结构体绑定方法,从而达到面向对象的部分效果。
关键知识点包括:
结构体定义与实例化
- 定义结构体时使用
type ... struct{}
语法,字段可以是各种数据类型。 - 实例化结构体可以使用字面量、
new
函数或匿名结构体语法。
- 定义结构体时使用
方法绑定
- 为结构体绑定方法时可以选择值接收者或指针接收者。
- 值接收者方法操作的是副本,指针接收者方法可以修改原始数据。
匿名结构体
- 无需单独定义类型,定义时直接实例化。
- 匿名字段:结构体中直接嵌入字段(类型名作为字段名),简化代码。
空结构体
- 空结构体
struct{}
占用 0 内存,可用于实现方法接收者、作为集合的值(类似只有 key 的字典)或作为信号通道的数据类型。
- 空结构体
接下来按照这些知识点整理代码示例。
1. 结构体的定义与实例化
1.1 定义结构体和使用字面量实例化(指针类型和非指针类型)
代码示例:
// 文件名: 01_struct_definition.go
package main
import "fmt"
// 定义一个结构体 Student
type Student struct {
id int
name string
age int
school string
}
func main() {
// 使用字面量实例化结构体,并取指针(指针类型实例)
pan := &Student{
id: 1,
name: "luobozi",
age: 18,
school: "znlsw",
}
// 使用指针访问字段(语法糖支持直接 . 操作)
fmt.Println("pan.name:", pan.name)
// 修改字段
pan.age = 20
fmt.Printf("pan: %+v\n", pan)
// 直接实例化结构体(值类型实例)
bo := Student{
id: 3,
name: "luobozi",
age: 18,
}
fmt.Printf("bo: %+v\n", bo)
}
说明:
- 使用
&Student{...}
得到结构体指针;直接写Student{...}
得到值类型实例。 - 使用字面量可以直接初始化各个字段。
1.2 使用 new 函数实例化结构体
代码示例:
// 文件名: 01_struct_new.go
package main
import "fmt"
type Student struct {
id int
name string
age int
}
func main() {
// 使用 new 函数实例化,返回的是指针类型
luo := new(Student)
luo.id = 2
luo.name = "luobozi"
fmt.Printf("luo: %+v\n", luo)
}
说明:
new(Student)
分配内存并返回指针,适用于结构体实例化;与字面量方式相比,new 返回的所有字段都是零值。
2. 值类型与指针类型的赋值
代码示例:
// 文件名: 01_struct_assignment.go
package main
import "fmt"
type Student struct {
id int
name string
age int
}
func main() {
// 指针类型赋值:两个指针引用同一对象,修改其中一个会影响另一方
luo := new(Student)
luo.id = 2
luo.name = "luobozi"
luo2 := luo
luo.id = 999
fmt.Println("luo2.id:", luo2.id, "luo.id:", luo.id)
// 值类型赋值:会拷贝一份数据,互不影响
bo := Student{
id: 3,
name: "luobozi",
age: 18,
}
bo2 := bo
bo2.name = "张三"
fmt.Println("bo2.name:", bo2.name, "bo.name:", bo.name)
}
说明:
- 指针赋值只是复制地址,值类型赋值会拷贝整个结构体数据。
3. 方法绑定(值接收者与指针接收者)
代码示例:
// 文件名: 01_struct_method.go
package main
import "fmt"
type Student struct {
id int
name string
age int
}
// 值接收者方法:对副本操作,修改不会反映到原变量
func (s Student) ChangeName(name string) {
fmt.Println("调用方法 ChangeName (值接收者)")
s.name = name
}
// 指针接收者方法:修改原结构体数据
func (s *Student) ChangeName2(name string) {
fmt.Println("调用方法 ChangeName2 (指针接收者)")
s.name = name
}
func main() {
bo := Student{
id: 3,
name: "luobozi",
age: 18,
}
luo := new(Student)
luo.id = 2
luo.name = "luobozi"
// 调用值接收者方法(修改不会影响原变量)
bo.ChangeName("milamn")
fmt.Println("bo after ChangeName:", bo)
// 调用指针接收者方法(原变量被修改)
luo.ChangeName("luji")
fmt.Println("luo after ChangeName (值方式调用):", luo)
luo.ChangeName2("mikod")
fmt.Println("luo after ChangeName2:", luo)
}
说明:
- 使用值接收者时,方法操作的是结构体副本;而指针接收者方法可以直接修改原数据。
4. 匿名结构体
代码示例:
// 文件名: 01_struct_anonymous.go
package main
import "fmt"
func main() {
// 定义并实例化匿名结构体,不需要提前定义类型
abc := struct {
abcid int
abcaddr string
}{
abcid: 10,
abcaddr: "长沙",
}
fmt.Println("匿名结构体 abcaddr:", abc.abcaddr)
}
说明:
- 匿名结构体适用于一次性使用,不需要复用结构体类型的场景。
5. 匿名字段
代码示例:
// 文件名: 01_struct_anonymousField.go
package main
import "fmt"
// 定义结构体时,直接嵌入类型作为匿名字段
type Person struct {
string
int
}
func main() {
// 匿名字段的赋值按照字段顺序进行
qwe := Person{
"niming", 2,
}
fmt.Printf("匿名字段 Person: %+v\n", qwe)
}
说明:
- 匿名字段使得结构体中的字段没有显式的名字,访问时直接用类型名(或通过顺序),可以简化代码结构。
6. 空结构体及其应用
6.1 空结构体作为方法接收者
代码示例:
// 文件名: 01_struct_empty.go
package main
import "fmt"
// 定义空结构体 Temp
type Temp struct{}
func (t *Temp) Call() {
fmt.Println("call for temp")
}
func main() {
t := &Temp{}
t.Call()
}
说明:
- 空结构体
struct{}
占用内存为 0,经常用作占位符或实现接口的方法接收者。
6.2 空结构体实现集合(Set)
代码示例:
// 文件名: 01_struct_set.go
package main
import "fmt"
// 定义 Set 类型,其本质上是 map[string]struct{},空结构体不占用内存
type Set map[string]struct{}
// 为 Set 添加元素
func (s Set) Append(k string) {
s[k] = struct{}{}
}
func main() {
s := make(Set)
s.Append("hello")
s.Append("world")
// 输出集合中所有的 key
for key := range s {
fmt.Println("set key:", key)
}
}
说明:
- 使用
map[string]struct{}
实现集合(只关心 key,value 为空结构体),这种方式占用内存极小,非常适合存储唯一且不可重复的键。
总结
本篇笔记涵盖了 Go 语言中结构体的核心知识点:
- 结构体定义与实例化:包括指针与值的实例化方式以及使用
new
和字面量。 - 赋值方式:指针赋值(共享同一内存)与值赋值(拷贝数据)。
- 方法绑定:值接收者与指针接收者的区别。
- 匿名结构体:直接定义并实例化一次性使用。
- 匿名字段:在结构体中嵌入类型作为字段。
- 空结构体及集合应用:空结构体用于方法接收者、占位或实现集合。