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