Go语言中的结构体

定义结构体

Go语言中通过关键字type定义自定义类型,结构体定义需要使用type和struct关键字。

语法:

type 结构体名 struct {
    成员变量1 类型1
    成员变量2 类型2
    成员变量3 类型3
    ...
}

解释:

示例:

type Person struct {
    name, sex string
    age       int
}

实例化结构体

结构体实例化时,会真正地分配内存。

Go语言实例化结构体主要有以下三种方式:

示例:

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 (变量名 结构体类型) 方法名(参数列表) (返回值列表) {
    // 方法体
}

解释:

示例:

// 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 在运动 
", a.name)
}

// 子结构体
type Dog struct {
    age int
    // 通过结构体嵌套,完成继承
    Animal
}

func (dog Dog) cry() {
    fmt.Printf("%v 在汪汪叫 
", 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("转换失败 
")
    } else {
        fmt.Printf("%#v 
", 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("转换失败 
")
    } else {
        fmt.Printf("%#v 
", 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 
", class)

    // 转换成Json字符串
    strByte, err := json.Marshal(class)
    if err != nil {
        fmt.Println("打印失败")
    } else {
        fmt.Println(string(strByte))
    }
}



展开阅读全文

页面更新:2024-05-04

标签:结构   接收者   嵌套   字段   字符串   变量   实例   定义   语言   类型   方法

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top