整體介紹
雖然 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
和字面量。 - 賦值方式:指針賦值(共享同一內存)與值賦值(拷貝數據)。
- 方法綁定:值接收者與指針接收者的區別。
- 匿名結構體:直接定義并實例化一次性使用。
- 匿名字段:在結構體中嵌入類型作為字段。
- 空結構體及集合應用:空結構體用于方法接收者、占位或實現集合。