定義結構體
Go語言中通過關鍵字type定義自定義類型,結構體定義需要使用type和struct關鍵字。
文法:
type 結構體名 struct {
成員變量1 類型1
成員變量2 類型2
成員變量3 類型3
...
}
解釋:
- 結構體名在同一個包内必須唯一,不能重複。首字母可以大寫也可以小寫,大寫表示這個結構體是公有的,在其它的包裡面也可以使用,小寫表示結構體屬于私有的,在其它地方不能使用
- 結構體的字段必須唯一,不能重複
- 同類型的結構體字段可以放在一行定義
示例:
type Person struct {
name, sex string
age int
}
執行個體化結構體
結構體執行個體化時,會真正地配置設定記憶體。
Go語言執行個體化結構體主要有以下三種方式:
- 标準執行個體化
- new函數執行個體化
- 取位址執行個體化
示例:
type Person struct {
name, sex string
age int
}
func main() {
// 标準執行個體化
var p1 Person
// new函數執行個體化,執行個體化完成後會傳回結構體地指針類型
p2 := new(Person)
// 取位址執行個體化傳回的也是結構體指針類型
p3 := &Person{}
fmt.Println(p1, p2, p3)
}
初始化結構體
鍵值對格式初始化
結構體執行個體 := 結構體類型{
成員變量1:值1,
成員變量2:值2,
成員變量3:值3,
}
清單格式初始化
結構體執行個體 := 結構體類型{
值1,
值2,
值3,
}
示例:
type Person struct {
name, sex string
age int
}
func main() {
// 鍵值對格式初始化
p1 := Person{name: "孫權", sex: "男", age: 18}
// 清單格式初始化
p2 := Person{"曹操", "男", 58}
fmt.Println(p1, p2)
}
結構體内嵌
Go語言的結構體内嵌是一種組合特性,使用結構體内嵌可建構一種面向對象程式設計思想中的繼承關系。
結構體執行個體化後,可直接通路内嵌結構體的所有成員變量和方法。
示例:
type Person struct {
name, sex string
age int
}
type Man struct {
Person
drink int
}
func main() {
p1 := Man{Person: Person{name: "buddha", age: 18, sex: "男"}, drink: 2}
fmt.Println(p1)
}
結構體方法
方法和函數比較像,差別是函數屬于包,通過包調用函數,而方法屬于結構體,通過結構體變量調用。所謂方法就是定義了接收者的函數。接收者的概念就類似于其他語言中的this 或者self。
文法:
func (變量名 結構體類型) 方法名(參數清單) (傳回值清單) {
// 方法體
}
解釋:
- 對于結構體方法,接收者可以是結構體類型的值或指針。
- 接收者變量:接收者中的參數變量名在命名時,官方建議使用接收者類型名的第一個小寫字母,而不是self、this之類的命名。例如,Person類型的接收者變量應該命名為p,Connector類型的接收者變量應該命名為c等。
- 非指針類型:表示不修改結構體的内容
- 指針類型:表示修改結構體中的内容
- 方法名、參數清單、傳回參數:具體格式與函數定義相同
示例:
// Person /*
type Person struct {
name string
age int
sex string
}
// PrintInfo /*
func (p Person) PrintInfo() {
fmt.Print(" 姓名: ", p.name)
fmt.Print(" 年齡: ", p.age)
fmt.Print(" 性别: ", p.sex)
fmt.Println()
}
func (p *Person) SetInfo(name string, age int, sex string) {
p.name = name
p.age = age
p.sex = sex
}
func main() {
var person = Person{
"曹操",
58,
"男",
}
person.PrintInfo()
person.SetInfo("孫權", 18, "男")
person.PrintInfo()
}
運作結果為:
姓名: 曹操 年齡: 58 性别: 男
姓名: 孫權 年齡: 18 性别: 男
給任意類型添加方法
在Go語言中,接收者的類型可以是任何類型,不僅僅是結構體,任何類型都可以擁有方法。
type myInt int
func (i myInt) PrintInfo() {
fmt.Println("hello,world", i)
}
func main() {
var a myInt = 10
a.PrintInfo()
}
type關鍵字定義int新的自定義類型,然後添加方法
結構體的匿名字段
結構體允許其成員字段在聲明時沒有字段名而隻有類型,這種沒有名字的字段就被稱為匿名字段
type Person struct {
string
int
}
func main() {
p := Person{"buddha", 18}
fmt.Println(p)
}
匿名字段預設采用類型名作為字段名,結構體要求字段名稱必須唯一,是以一個結構體中同種類型的匿名字段隻能一個
結構體的字段類型可以是:基本資料類型,也可以是切片、Map 以及結構體
如果結構體的字段類似是:指針、slice、和 map 的零值都是nil,即還沒有配置設定空間
如果需要使用這樣的字段,需要先make,才能使用
/**
定義結構體
*/
type Person struct {
name string
age int
hobby []string
mapValue map[string]string
}
func main() {
var person = Person{}
person.name = "張三"
person.age = 10
// 給切片申請記憶體空間
person.hobby = make([]string, 4, 4)
person.hobby[0] = "睡覺"
person.hobby[1] = "吃飯"
person.hobby[2] = "打豆豆"
// 給map申請存儲空間
person.mapValue = make(map[string]string)
person.mapValue["address"] = "北京"
person.mapValue["phone"] = "123456789"
// 列印完整資訊
fmt.Printf("%#v", person)
}
同時我們還支援結構體的嵌套,如下所示
// 使用者結構體
type User struct {
userName string
password string
sex string
age int
address Address // User結構體嵌套Address結構體
}
// 收貨位址結構體
type Address struct {
name string
phone string
city string
}
func main() {
var u User
u.userName = "moguBlog"
u.password = "123456"
u.sex = "男"
u.age = 18
var address Address
address.name = "張三"
address.phone = "110"
address.city = "北京"
u.address = address
fmt.Printf("%#v", u)
}
嵌套結構體的字段名沖突
嵌套結構體内部可能存在相同的字段名,這個時候為了避免歧義,需要指定具體的内嵌結構體的字段。(例如,父結構體中的字段 和 子結構體中的字段相似)
預設會從父結構體中尋找,如果找不到的話,再去子結構體中在找
如果子類的結構體中,同時存在着兩個相同的字段,那麼這個時候就會報錯了,因為程式不知道修改那個字段的為準。
結構體的繼承
結構體的繼承,其實就類似于結構體的嵌套,如下所示,我們定義了兩個結構體,分别是Animal 和 Dog,其中每個結構體都有各自的方法,然後通過Dog結構體 繼承于 Animal結構體
// 父結構體
type Animal struct {
name string
}
func (a Animal) run() {
fmt.Printf("%v 在運動 \n", a.name)
}
// 子結構體
type Dog struct {
age int
// 通過結構體嵌套,完成繼承
Animal
}
func (dog Dog) cry() {
fmt.Printf("%v 在汪汪叫 \n", dog.name)
}
func main() {
var dog = Dog{
age: 10,
Animal: Animal{
name: "泰迪",
},
}
dog.run()
dog.cry()
}
運作後,發現Dog擁有了父類的方法
泰迪 在運動
泰迪 在汪汪叫
結構體和Json互相轉換
Go語言JSON序列化是指把結構體資料轉化成JSON格式的字元串,Go語言JSON的反序列化是指把JSON資料轉化成結構體對象。序列化和反序列化通過encoding/json包中的json.Marshal() 和 son.Unmarshal()
結構體轉json字元串
// 定義一個學生結構體,注意結構體的首字母必須大寫,代表公有,否則将無法轉換
type Student struct {
ID string
Gender string
Name string
Sno string
}
func main() {
var s1 = Student{
ID: "12",
Gender: "男",
Name: "李四",
Sno: "s001",
}
// 結構體轉換成Json(傳回的是byte類型的切片)
jsonByte, _ := json.Marshal(s1)
jsonStr := string(jsonByte)
fmt.Printf(jsonStr)
}
json字元串轉結構體
// 定義一個學生結構體,注意結構體的首字母必須大寫,代表公有,否則将無法轉換
type Student struct {
ID string
Gender string
Name string
Sno string
}
func main() {
// Json字元串轉換成結構體
var str = `{"ID":"12","Gender":"男","Name":"李四","Sno":"s001"}`
var s2 = Student{}
// 第一個是需要傳入byte類型的資料,第二參數需要傳入轉換的位址
err := json.Unmarshal([]byte(str), &s2)
if err != nil {
fmt.Printf("轉換失敗 \n")
} else {
fmt.Printf("%#v \n", s2)
}
}
要實作結構體轉換成字元串,必須保證結構體中的字段是公有的,也就是首字母必須是大寫的。
結構體标簽Tag
Tag是結構體的元資訊,可以在運作的時候通過反射的機制讀取出來。Tag在結構體字段的後方定義,由一對反引号包裹起來,具體的格式如下:
key1:"value1" key2:"value2"
結構體tag由一個或多個鍵值對組成。鍵與值使用冒号分隔,值用雙引号括起來。同一個結構體字段可以設定多個鍵值對tag,不同的鍵值對之間使用空格分隔。
注意事項:為結構體編寫Tag時,必須嚴格遵守鍵值對的規則。結構體标簽的解析代碼的容錯能力很差,一旦格式寫錯,編譯和運作時都不會提示任何錯誤,通過反射也無法正确取值。例如不要在key和value之間添加空格。
如下所示,我們通過tag标簽,來轉換字元串的key
// 定義一個Student體,使用結構體标簽
type Student2 struct {
Id string `json:"id"` // 通過指定tag實作json序列化該字段的key
Gender string `json:"gender"`
Name string `json:"name"`
Sno string `json:"sno"`
}
func main() {
var s1 = Student2{
Id: "12",
Gender: "男",
Name: "李四",
Sno: "s001",
}
// 結構體轉換成Json
jsonByte, _ := json.Marshal(s1)
jsonStr := string(jsonByte)
fmt.Println(jsonStr)
// Json字元串轉換成結構體
var str = `{"Id":"12","Gender":"男","Name":"李四","Sno":"s001"}`
var s2 = Student2{}
// 第一個是需要傳入byte類型的資料,第二參數需要傳入轉換的位址
err := json.Unmarshal([]byte(str), &s2)
if err != nil {
fmt.Printf("轉換失敗 \n")
} else {
fmt.Printf("%#v \n", s2)
}
}
嵌套結構體和Json序列化反序列化
// 定義一個Student結構體
type Student3 struct {
Id int
Gender string
Name string
}
// 定義一個班級結構體
type Class struct {
Title string
Students []Student3
}
func main() {
var class = Class{
Title: "1班",
Students: make([]Student3, 0),
}
for i := 0; i < 10; i++ {
s := Student3{
Id: i + 1,
Gender: "男",
Name: fmt.Sprintf("stu_%v", i + 1),
}
class.Students = append(class.Students, s)
}
fmt.Printf("%#v \n", class)
// 轉換成Json字元串
strByte, err := json.Marshal(class)
if err != nil {
fmt.Println("列印失敗")
} else {
fmt.Println(string(strByte))
}
}