Go語言中沒有“類”的概念,也不支援“類”的繼承等面向對象的概念。Go語言中通過結構體的内嵌再配合接口比面向對象具有更高的擴充性和靈活性。
結構體
Go語言中的基礎資料類型可以表示一些事物的基本屬性,但是當我們想表達一個事物的全部或部分屬性時,這時候再用單一的基本資料類型明顯就無法滿足需求了,Go語言提供了一種自定義資料類型,可以封裝多個基本資料類型,這種資料類型叫結構體,英文名稱
struct
。 也就是我們可以通過
struct
來定義自己的類型了。
Go語言中通過
struct
來實作面向對象。
一.結構體的定義
使用
type
和
struct
關鍵字來定義結構體,具體代碼格式如下:
type 類型名 struct {
字段名 字段類型
字段名 字段類型
…
}
其中:
- 類型名:辨別自定義結構體的名稱,在同一個包内不能重複。
- 字段名:表示結構體字段名。結構體中的字段名必須唯一。
- 字段類型:表示結構體字段的具體類型。
舉個例子,我們定義一個
Person
(人)結構體,代碼如下:
type person struct {
name string
city string
age int8
}
同樣類型的字段也可以寫在一行,
type person1 struct {
name, city string
age int
}
這樣我們就擁有了一個
person
的自定義類型,它有
name
、
city
age
三個字段,分别表示姓名、城市和年齡。這樣我們使用這個
person
結構體就能夠很友善的在程式中表示和存儲人資訊了。
語言内置的基礎資料類型是用來描述一個值的,而結構體是用來描述一組值的。比如一個人有名字、年齡和居住城市等,本質上是一種聚合型的資料類型
二.結構體執行個體化
隻有當結構體執行個體化時,才會真正地配置設定記憶體。也就是必須執行個體化後才能使用結構體的字段。
結構體本身也是一種類型,我們可以像聲明内置類型一樣使用
var
關鍵字聲明結構體類型。
var 結構體執行個體 結構體類型
1.基本執行個體化
舉個例子:
type person struct {
name string
city string
age int8
}
func main() {
var p1 person
p1.name = "沙河娜紮"
p1.city = "北京"
p1.age = 18
fmt.Printf("p1=%v\n", p1) //p1={沙河娜紮 北京 18}
fmt.Printf("p1=%#v\n", p1) //p1=main.person{name:"沙河娜紮", city:"北京", age:18}
}
我們通過
.
來通路結構體的字段(成員變量),例如
p1.name
p1.age
等。
2.匿名結構體
在定義一些臨時資料結構等場景下還可以使用匿名結構體。
package main
import (
"fmt"
)
func main() {
var user struct{Name string; Age int}
user.Name = "小王子"
user.Age = 18
fmt.Printf("%#v\n", user)
}
3.建立指針類型結構體
我們還可以通過使用
new
關鍵字對結構體進行執行個體化,得到的是結構體的位址。 格式如下:
var p2 = new(person)
fmt.Printf("%T\n", p2) //*main.person
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}
從列印的結果中我們可以看出
p2
是一個結構體指針。
需要注意的是在Go語言中支援對結構體指針直接使用
.
來通路結構體的成員。
var p2 = new(person)
p2.name = "小王子"
p2.age = 28
p2.city = "上海"
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"小王子", city:"上海", age:28}
4.取結構體的位址執行個體化
&
對結構體進行取位址操作相當于對該結構體類型進行了一次
new
執行個體化操作。
p3 := &person{}
fmt.Printf("%T\n", p3) //*main.person
fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"", city:"", age:0}
p3.name = "七米"
p3.age = 30
p3.city = "成都"
fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"七米", city:"成都", age:30}
p3.name = "七米"
其實在底層是
(*p3).name = "七米"
,這是Go語言幫我們實作的文法糖。
三.結構體初始化
沒有初始化的結構體,其成員變量都是對應其類型的零值。
type person struct {
name string
city string
age int8
}
func main() {
var p4 person
fmt.Printf("p4=%#v\n", p4) //p4=main.person{name:"", city:"", age:0}
}
1.使用鍵值對初始化
使用鍵值對,對結構體進行初始化時,鍵對應結構體的字段,值對應該字段的初始值。
//使用鍵值的形式對結構體進行初始化指派
p4 := people{
name: "哈哈",
city: "上海",
age: 22,
}
也可以對結構體指針進行鍵值對初始化,例如:
p4 :=&people{
name: "哈哈",
city: "上海",
age: 22,
}
fmt.Println(p4)
當某些字段沒有初始值的時候,該字段可以不寫。此時,沒有指定初始值的字段的值就是該字段類型的零值。
2.使用值的清單初始化
初始化結構體的時候可以簡寫,也就是初始化的時候不寫鍵,直接寫值:
p8 := &person{
"沙河娜紮",
"北京",
28,
}
fmt.Printf("p8=%#v\n", p8) //p8=&main.person{name:"沙河娜紮", city:"北京", age:28}
使用這種格式初始化時,需要注意:
- 必須初始化結構體的所有字段。
- 初始值的填充順序必須與字段在結構體中的聲明順序一緻。
- 該方式不能和鍵值初始化方式混用。
四.結構體記憶體布局
結構體占用一塊連續的記憶體。
type test struct {
a int8
b int8
c int8
d int8
}
n := test{
1, 2, 3, 4,
}
fmt.Printf("n.a %p\n", &n.a)
fmt.Printf("n.b %p\n", &n.b)
fmt.Printf("n.c %p\n", &n.c)
fmt.Printf("n.d %p\n", &n.d)
輸出:
n.a 0xc0000a0060
n.b 0xc0000a0061
n.c 0xc0000a0062
n.d 0xc0000a0063
空結構體
空結構體是不占用空間的。
var v struct{}
fmt.Println(unsafe.Sizeof(v)) // 0