结构体的概念

Go语言中不存在Class类的概念,但是可以通过结构体struct来实现。结构体就是一种相同类型,或者不同类型的数据构成的数据的集合。里面的每一个变量叫做成员变量。也就是结构体的字段。每一个字段拥有自己的数据类型和数值。
结构体定义之后也只是确定了这个结构长什么样子,都有哪些字段,并没有真实的数据,所以需要使用结构体时必须先实例化结构体。赋予结构体真实存在的意义。

结构体的实例化

package structDemo

import "fmt"

// 定义结构体
type Person struct {
    name    string
    age     int
    address string
    phone   string
}

func main() {
    // 实例化后并使用结构体
    p := Person{} // 使用简短声明方式,后面加上{}代表这是结构体

    // 为结构体赋值
    p.name = "张三"
    p.age = 18
    p.address = "北京"
    p.phone = "123456789"

    // 打印结构体
    fmt.Println(p.name, p.age, p.address, p.phone) // 张三 18 北京 123456789
}

还可以在结构体后面大括号内,直接给结构体成员变量赋值。

// 直接给成员变量赋值
p2 := Person{name: "李四", age: 20, address: "上海", phone: "987654321"}
fmt.Println(p2.name, p2.age, p2.address, p2.phone) // 李四 20 上海 987654321

在Go语言中有一个关键字new可以用来实例化结构体。本质上是分配了一个某种类型的内存空间,所以使用new关键字默认就会返回一个指针。使用new创建结构体,默认就是一个指针类型的结构体。

package main

import "fmt"

type Person struct {
    name    string
    age     int
    address string
    phone   string
}

func main() {
    p2 := Person{name: "李四", age: 20, address: "上海", phone: "987654321"}

    // 1、使用结构体指针
    var p *Person
    p = &p2
    fmt.Println(p) // &{李四 20 上海 987654321}
    p.name = "王五"
    fmt.Println(p2) // {王五 20 上海 987654321}

    // 2、使用new 创建结构体指针
    p3 := new(Person)
    fmt.Println(p3) // &{ 0  }
    p3.name = "赵六"
    p3.age = 30
    p3.address = "广州"
    p3.phone = "123456789"
    fmt.Println(p3) // &{赵六 30 广州 123456789}
}

Go语言中,使用&符号取地址时候,默认就对该类型进行了一次实例化操作。在开发过程中经常会以下面这种使用函数封装写法,来实例化一个结构体。

package main

type Person struct {
    name    string
    age     int
    address string
}

func main() {
    p := newPerson("张三", 18, "北京")
    println(p.name, p.age, p.address) // 张三 18 北京
}

// 使用函数来实例化结构体
func newPerson(name string, age int, address string) *Person {
    return &Person{
        name:    name,
        age:     age,
        address: address,
    }
}

结构体初始化

结构体内的每一个字段,都有自己相应的数据类型,如果结构体被实例化后,字段的默认值就是该字段类型的零值,int就是0string就是"",如果是指针类型,默认就是nil

package main

type Person struct {
    name    string
    age     int
    address string
}

func main() {
    p := Person{name: "张三", address: "北京"}
    // 年龄未初始化,默认为零值
    println(p.name, p.age, p.address) // 张三 0 北京
}

初始化时可以忽略成员内的字段名,但是必须初始化所有的字段,必须和结构体内字段顺序一致,并且不能和有字段的初始化方法混用。

package main

type Person struct {
    name    string
    age     int
    address string
}

func main() {
    p := Person{"张三", 18, "北京"}
    println(p.name, p.age, p.address) // 张三 18 北京
}

匿名结构体

匿名结构体就是没有类型名称,也不需要type关键字可以直接使用。

package main

import "fmt"

func main() {
    p := struct {
        name    string
        age     int
        address string
    }{
        name:    "张三",
        age:     18,
        address: "北京",
    }

    fmt.Println(p.name, p.age, p.address) // 张三 18 北京
}

结构体嵌套

结构体可以包含多个字段,每一个字段都需要相应的数据类型,结构体也属于一种数据类型,所以结构体内部也可以包含另一个结构体。

package main

import "fmt"

// Person 结构体
type Person struct {
    name    string
    age     int
    person2 Person2
}

// 结构体2
type Person2 struct {
    name string
    age  int
}

// 也可以嵌套结构体指针
type Person3 struct {
    name    string
    age     int
    person2 *Person2
}

func main() {
    p := Person{}
    p.name = "张三"
    p.age = 18
    p.person2 = Person2{
        name: "李四",
        age:  20,
    }
    fmt.Println(p) // {张三 18 {李四 20}}

    // 第二种初始化的方式,定义好结构体之后使用重新赋值的方式:使用(变量.字段名=值)的格式
    p2 := Person2{}
    p2.name = "王五"
    p2.age = 30

    pre := Person3{}
    pre.name = "赵六"
    pre.age = 40
    pre.person2 = &p2
    fmt.Println(pre) // {赵六 40 0xc00000e0c0}
}

结构体与Json数据的相互转换

JSON是一种特殊格式的字符串,用来传输和存储数据,在使用api服务开发提供给前端的数据时,更多使用json数据交互。Go语言标准库中提供了json解析的包,使用之前导入包。

import "encoding/json"

结构体转为json字符串

需要注意的是将结构体转换为Json数据时候,定义结构体的字段必须首字母大写。否则无法正常解析。

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name    string
    Age     int
    Person2 *Person2
}

type Person2 struct {
    Name string
    Age  int
}

func main() {
    p := Person{}
    p.Name = "张三"
    p.Age = 18
    p.Person2 = &Person2{
        Name: "李四",
        Age:  20,
    }
    buf, err := json.Marshal(p) // 替换为JSON,返回两个结果
    if err != nil {
        fmt.Println("err = ", err)
        return
    }
    fmt.Println("json = ", string(buf)) // json =  {"Name":"张三","Age":18,"Person2":{"Name":"李四","Age":20}}
}

可以看出其中json字符中每一个key的首字母也是大写,最后一个没有设置数据的字段的结果为null。那么如何强制将他变为小写的。并且将不需要显示的字段隐藏掉。就需要在结构体上添加标记。

type Person struct {
    Name    string `json:"name"` // 重新指定json字段为小写输出
    Age     int    `json:"age"`
    Address string `json:"address"`
    Phone   string `json:"phone"`
    Name2   string `json:"-"`               // 不输出到json
    Name3   string `json:"name3,omitempty"` // 如果为空则不输出到json
    Name4   string `json:"name4,string"`    // 强制转换为字符串输出到json
}

json字符串转为结构体

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string `json:"name"` // 重新指定json字段为小写输出
    Age  int    `json:"age"`  // 重新指定json字段为小写输出
}

func main() {
    jsonstr := `{"name":"张三","age":18}`
    p := Person{}
    // 反序列化
    err := json.Unmarshal([]byte(jsonstr), &p)
    if err != nil {
        fmt.Println("err = ", err)
        return
    }
    fmt.Println("p = ", p) // p =  {张三 18}
}

Last modification:November 17, 2022
If you think my article is useful to you, please feel free to appreciate