【go語(yǔ)言學(xué)習(xí)】結(jié)構(gòu)體struct

Go語(yǔ)言中的基礎(chǔ)數(shù)據(jù)類型可以表示一些事物的基本屬性,但是當(dāng)我們想表達(dá)一個(gè)事物的全部或部分屬性時(shí),這時(shí)候再用單一的基本數(shù)據(jù)類型明顯就無(wú)法滿足需求了。
Go語(yǔ)言提供了一種自定義數(shù)據(jù)類型,可以封裝多個(gè)基本數(shù)據(jù)類型,這種數(shù)據(jù)類型叫結(jié)構(gòu)體,英文名稱struct。 也就是我們可以通過(guò)struct來(lái)定義自己的類型了。

一、結(jié)構(gòu)體的定義和初始化

1、結(jié)構(gòu)體的定義

結(jié)構(gòu)體的定義使用typestruct關(guān)鍵字

type 結(jié)構(gòu)體名 struct {
   字段名 字段類型;
   字段名 字段類型;
   ...
   字段名 字段類型;
}

其中:

  • 類型名:標(biāo)識(shí)自定義結(jié)構(gòu)體的名稱,在同一個(gè)包內(nèi)不能重復(fù)。通過(guò)首字母大小寫控制訪問(wèn)。
  • 字段名:表示結(jié)構(gòu)體字段名。結(jié)構(gòu)體中的字段名必須唯一。通過(guò)首字母大小寫控制訪問(wèn)。
  • 字段類型:表示結(jié)構(gòu)體字段的具體類型。
package main

// Person 一個(gè)結(jié)構(gòu)體,包含name, age兩個(gè)字段
type Person struct {
    Name string // 包外可訪問(wèn)
    age  int    // 包外不可訪問(wèn)
}

func main() {

}
2、結(jié)構(gòu)體的初始化
package main

import "fmt"

// Person 一個(gè)結(jié)構(gòu)體,包含name, age兩個(gè)字段
type Person struct {
    Name string // 包外可訪問(wèn)
    age  int    // 包外不可訪問(wèn)
}

func main() {
    // 方法一
    var p1 Person
    p1.Name = "tom"
    p1.age = 20
    fmt.Println(p1)
    // 方法二
    p2 := Person{}
    p2.Name = "marry"
    p2.age = 18
    fmt.Println(p2)
    // 方法三
    p3 := Person{
        Name: "jack",
        age:  23,
    }
    fmt.Println(p3)
    // 方法四
    p4 := Person{
        "lili",
        24,
    }
    fmt.Println(p4)
}

運(yùn)行結(jié)果

{tom 20}
{marry 18}
{jack 23}
{lili 24}

二、結(jié)構(gòu)體的使用

1、結(jié)構(gòu)體指針

數(shù)據(jù)類型:

  • 值類型:int,string,bool,float,arry,struct
  • 引用類型:slice,map,function,pointer,chan
package main

import "fmt"

// Person 一個(gè)結(jié)構(gòu)體,包含name, age兩個(gè)字段
type Person struct {
    Name string // 包外可訪問(wèn)
    age  int    // 包外不可訪問(wèn)
}

func main() {
    p := Person{
        Name: "tom",
        age:  20,
    }
    // 方法一 直接創(chuàng)建結(jié)構(gòu)體指針
    var ptr1 *Person
    fmt.Printf("%T, %v\n", ptr1, ptr1)
    ptr1 = &p
    // (*ptr1).Name = "jack"
    ptr1.Name = "jack"
    fmt.Println(p)
    // 方法二 使用new創(chuàng)建結(jié)構(gòu)體指針
    ptr2 := new(Person)
    fmt.Printf("%T, %v\n", ptr2, ptr2)
    ptr2 = &p
    ptr2.Name = "lili"
    fmt.Println(p)
    // 方法三 使用&地址符創(chuàng)建結(jié)構(gòu)體指針
    ptr3 := &Person{}
    fmt.Printf("%T, %v\n", ptr3, ptr3)
    ptr3 = &p
    ptr3.Name = "marry"
    fmt.Println(p)
}

運(yùn)行結(jié)果

*main.Person, <nil>
{jack 20}
*main.Person, &{ 0}
{lili 20}
*main.Person, &{ 0}
{marry 20}
2、匿名結(jié)構(gòu)體

沒(méi)有名字的結(jié)構(gòu)體,在創(chuàng)建結(jié)構(gòu)體的時(shí)候同時(shí)創(chuàng)建對(duì)象。

package main

import "fmt"

func main() {
    p := struct {
        name string
        age  int
    }{
        name: "tom",
        age:  20,
    }
    fmt.Println(p)
}

運(yùn)行結(jié)果

{tom 20}
3、匿名字段

在結(jié)構(gòu)體中,不寫字段的名字,只寫類型。匿名字段不允許重復(fù)。

package main

import "fmt"

type worker struct {
    string
    int
}

func main() {
    w := worker{
        "tom",
        20,
    }
    fmt.Println(w)
    fmt.Println(w.string, w.int)
}

運(yùn)行結(jié)果

{tom 20}
tom 20
4、結(jié)構(gòu)體嵌套

一個(gè)結(jié)構(gòu)體中的字段,是另一個(gè)結(jié)構(gòu)體。

package main

import "fmt"

// Address 結(jié)構(gòu)體
type Address struct {
    Province string
    City     string
}

// User 結(jié)構(gòu)體
type User struct {
    Name string
    Age  int
    // Address Address
    Address //嵌套匿名字段
}

func main() {
    u := User{
        Name: "tom",
        Age:  20,
        Address: Address{
            Province: "江蘇省",
            City:     "蘇州市",
        },
    }
    fmt.Printf("%+v\n", u)
}

運(yùn)行結(jié)果

{Name:tom Age:20 Address:{Province:江蘇省 City:蘇州市}}

嵌套結(jié)構(gòu)體字段訪問(wèn)

  • 在結(jié)構(gòu)體中屬于匿名結(jié)構(gòu)體的字段稱為提升字段,因?yàn)樗鼈兛梢员辉L問(wèn),就好像它們屬于擁有匿名結(jié)構(gòu)字段的結(jié)構(gòu)一樣。
  • 嵌套結(jié)構(gòu)體內(nèi)部可能存在相同的字段名。在這種情況下為了避免歧義需要通過(guò)指定具體的內(nèi)嵌結(jié)構(gòu)體字段名。
package main

import "fmt"

// Address 結(jié)構(gòu)體
type Address struct {
    Province   string
    City       string
    CreateTime string
}

// Email 結(jié)構(gòu)體
type Email struct {
    Email      string
    CreateTime string
}

// User 結(jié)構(gòu)體
type User struct {
    Name string
    Age  int
    // Address Address
    Address
    Email
}

func main() {
    u := User{
        Name: "tom",
        Age:  20,
        Address: Address{
            Province:   "江蘇省",
            City:       "蘇州市",
            CreateTime: "2020.09.26",
        },
        Email: Email{
            Email:      "tom@github.com.cn",
            CreateTime: "2020.09.25",
        },
    }
    fmt.Printf("%+v\n", u)
    fmt.Println(u.City)  //City是提升字段
    fmt.Println(u.Email) //Email是提升字段
    fmt.Println(u.Email.CreateTime)
    fmt.Println(u.Address.CreateTime)
}

運(yùn)行結(jié)果

{Name:tom Age:20 Address:{Province:江蘇省 City:蘇州市 CreateTime:2020.09.26} Email:{Email:tom@github.com.cn CreateTime:2020.09.25}}
蘇州市
{tom@github.com.cn 2020.09.25}
2020.09.25
2020.09.26
5、結(jié)構(gòu)體與json序列化

go語(yǔ)言通過(guò)json包下的Marshal(v interface{}) ([]byte, error)函數(shù)實(shí)現(xiàn)結(jié)構(gòu)體對(duì)象的json序列化。
通過(guò)json包下的Unmarshal(data []byte, v interface{}) error函數(shù)實(shí)現(xiàn)json的反序列化。

package main

import (
    "encoding/json"
    "fmt"
)

// Student 結(jié)構(gòu)體
type Student struct {
    Name    string `json:"name"`
    Age     int    `json:"age"`
    Address string `json:"address"`
}

func main() {
    s1 := Student{
        Name:    "tom",
        Age:     20,
        Address: "蘇州市",
    }
    data, err := json.Marshal(s1)
    if err != nil {
        fmt.Println("json.marshal failed, err=", err)
        return
    }
    fmt.Printf("json:%s\n", data)
    str := `{"name":"jack","age":21,"address":"上海市"}`
    s2 := new(Student)
    err = json.Unmarshal([]byte(str), s2) // s2需要傳入一個(gè)指針
    if err != nil {
        fmt.Println("json.unmarshal failed, err=", err)
        return
    }
    fmt.Printf("%+v\n", s2)
}

運(yùn)行結(jié)果

json:{"name":"tom","age":20,"address":"蘇州市"}
&{Name:jack Age:21 Address:上海市}

三、構(gòu)造函數(shù)

Go語(yǔ)言的結(jié)構(gòu)體沒(méi)有構(gòu)造函數(shù),我們可以自己實(shí)現(xiàn)。

package main

import "fmt"

// Student 結(jié)構(gòu)體
type Student struct {
    Name    string
    Age     int
    Address string
}

// NewStudent 構(gòu)造函數(shù)
func NewStudent(name string, age int, address string) *Student {
    return &Student{
        name,
        age,
        address,
    }
}

func main() {
    s := NewStudent("lili", 20, "上海市")
    fmt.Println(s)
}

運(yùn)行結(jié)果

&{lili 20 上海市}

四、方法

Go語(yǔ)言中的方法(Method)是一種作用于特定類型變量的函數(shù)。這種特定類型變量叫做接收者(Receiver)。接收者的概念就類似于其他語(yǔ)言中的this或者 self。

func (t type) methodName(parameter list) (return list) {

}

其中

  • t接收者變量:接收者中的參數(shù)變量名在命名時(shí),官方建議使用接收者類型名稱首字母的小寫,而不是self、this之類的命名。例如,Person類型的接收者變量應(yīng)該命名為 p,Connector類型的接收者變量應(yīng)該命名為c等。
  • type接收者類型:接收者類型和參數(shù)類似,可以是指針類型和非指針類型。
  • 方法名、參數(shù)列表、返回參數(shù):具體格式與函數(shù)定義相同。
package main

import "fmt"

// Student 結(jié)構(gòu)體
type Student struct {
    Name    string
    Age     int
    Address string
}

// NewStudent 構(gòu)造函數(shù)
func NewStudent(name string, age int, address string) *Student {
    return &Student{
        name,
        age,
        address,
    }
}

// Learn 結(jié)構(gòu)體Student的方法
func (s Student) Learn(class string) {
    fmt.Printf("%v正在努力學(xué)習(xí)%v課\n", s.Name, class)
}
func main() {
    s := NewStudent("lili", 20, "上海市")
    fmt.Println(s)
    s.Learn("english")
}

運(yùn)行結(jié)果

&{lili 20 上海市}
lili正在努力學(xué)習(xí)english課

方法與函數(shù)的區(qū)別是,函數(shù)不屬于任何類型,方法屬于特定的類型。

+ 指針類型的接受者

指針類型的接收者由一個(gè)結(jié)構(gòu)體的指針組成,由于指針的特性,調(diào)用方法時(shí)修改接收者指針的任意成員變量,在方法結(jié)束后,修改都是有效的。

package main

import "fmt"

// Student 結(jié)構(gòu)體
type Student struct {
    Name    string
    Age     int
    Address string
}

// SetAge 結(jié)構(gòu)體Student的方法
func (s *Student) SetAge(age int) {
    s.Age = age
}

func main() {
    s := Student{
        "lili",
        20,
        "蘇州市",
    }
    fmt.Println(s)
    s.SetAge(18)
    fmt.Println(s)
}

運(yùn)行結(jié)果

{lili 20 蘇州市}
{lili 18 蘇州市}

+ 值類型接受者

當(dāng)方法作用于值類型接收者時(shí),Go語(yǔ)言會(huì)在代碼運(yùn)行時(shí)將接收者的值復(fù)制一份。在值類型接收者的方法中可以獲取接收者的成員值,但修改操作只是針對(duì)副本,無(wú)法修改接收者變量本身。

package main

import "fmt"

// Student 結(jié)構(gòu)體
type Student struct {
    Name    string
    Age     int
    Address string
}

// SetAddress 結(jié)構(gòu)體Student的方法
func (s Student) SetAddress(address string) {
    s.Address = address
}
func main() {
    s := Student{
        "lili",
        20,
        "蘇州市",
    }
    fmt.Println(s)
    s.SetAddress("上海市")
    fmt.Println(s)
}

運(yùn)行結(jié)果

{lili 20 蘇州市}
{lili 20 蘇州市}

+ 什么時(shí)候應(yīng)該使用指針類型接收者

  • 需要修改接收者中的值
  • 接收者是拷貝代價(jià)比較大的大對(duì)象
  • 保證一致性,如果有某個(gè)方法使用了指針接收者,那么其他的方法也應(yīng)該使用指針接收者。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容