青训营go语言基础总结三:结构体| 豆包MarsCode AI 刷题

120 阅读7分钟

go语言结构体

在Go语言中,结构体(struct)是一种数据类型,可以用于将多个不同类型的变量组织在一起。下面我们详细介绍结构体的定义、声明、初始化以及使用。

1. 定义结构体

在go语言中用 type 关键字和 struct 关键字来定义结构体。例如,我们定义一个 Person 结构体:

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

2. 声明结构体变量

在go语言中结构体变量的声明有多种方式,可以声明未初始化的结构体变量,也可以声明并初始化结构体变量。

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    // 声明未初始化的结构体变量
    var person1 Person
    fmt.Println(person1) // 输出: { 0}

    // 声明并同时初始化结构体变量
    person2 := Person{Name: "Alice", Age: 30}
    fmt.Println(person2)
}

当输出一个未初始化的结构体变量的时候,会输出各个变量对应的零值。

3. 初始化结构体

3.1 使用字段名进行初始化

在初始化时可以指定字段名,这对于长结构体或者部分字段初始化很有用。

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    person1 := Person{Name: "Bob", Age: 25}
    person2 := Person{Name: "Charlie", City: "New York"}
    fmt.Println(person1) // 输出: {Bob 25 }
    fmt.Println(person2) // 输出: {Charlie 0 New York}
}

3.2 按位置顺序初始化

另一种方式是不使用字段名直接按位置初始化。这需要确保所有字段都按照定义顺序提供。

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    person1 := Person{"Alice", 30}
    fmt.Println(person1) // 输出: {Alice 30}
}

3.3 使用指针初始化结构体

结构体指针可以通过 new 关键字或者取地址符 & 初始化。

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    personPtr := new(Person)
    personPtr.Name = "Eve"
    personPtr.Age = 28
    fmt.Println(*personPtr) // 输出: {Eve 28}
    
    personPtr2 := &Person{Name: "Frank", Age: 35}
    fmt.Println(*personPtr2) // 输出: {Frank 35}
}

4. 使用结构体

结构体的使用包括访问和修改字段,结构体作为函数参数和返回值等。

4.1 访问和修改结构体字段

使用点号 . 来访问和修改结构体的字段。

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    person := Person{Name: "Alice", Age: 30}
    fmt.Println(person.Name) // 输出: Alice
    fmt.Println(person.Age)  // 输出: 30

    person.Age = 31
    fmt.Println(person) // 输出: {Alice 31}
}

使用点号 . 来访问和修改结构体的字段,无论是结构体变量还是结构体指针变量都可以。

4.2 结构体作为函数参数和返回值

可以通过值传递或指针传递将结构体作为函数参数。

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

// 通过值传递
func printPerson(p Person) {
    fmt.Println(p)
}

// 通过指针传递
func updatePerson(p *Person) {
    p.Age = 35
}

func main() {
    person := Person{Name: "Alice", Age: 30}

    printPerson(person) // 输出: {Alice 30}

    updatePerson(&person)
    fmt.Println(person) // 输出: {Alice 35}
}

5. 结构体内嵌(匿名字段)

Go语言中支持结构体内嵌(匿名字段),用于实现简单的继承。

package main

import "fmt"

type Human struct {
    Name string
    Age  int
}

type Employee struct {
    Human  // 匿名字段
    ID     int
    Office string
}

func main() {
    emp := Employee{
        Human:  Human{Name: "George", Age: 40},
        ID:     1001,
        Office: "HQ",
    }
    fmt.Println(emp) // 输出: {{George 40} 1001 HQ}
}

6. 结构体标签

结构体标签为字段添加元信息,常用于序列化和反序列化,如JSON:

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    jsonData := `{"name": "Hank", "age": 33}`
    
    var person Person
    json.Unmarshal([]byte(jsonData), &person)
    fmt.Println(person) // 输出: {Hank 33}
}

结构体与json

上面讲到了tag,在这里讲一下结构体和json。在Go语言中,结构体(struct)和JSON之间的转换非常常见,特别是在处理API响应或生成API请求时。go语言中内置了encoding/json包,可以方便用来做转换。下面将详细介绍如何在Go语言中使用结构体与JSON进行相互转换。 例如,我们定义一个叫Person的结构体:

type Person struct {
    Name    string `json:"name"`
    Age     int    `json:"age"`
    Email   string `json:"email"`
}

注意结构体字段标签(tag),如json:"name",这些标签用于指定JSON键名。如果省略标签,默认会使用结构体字段的名字。

结构体转JSON

可以使用json.Marshal函数将结构体转换为JSON。此函数返回编码后的JSON字节切片和可能的错误。

func main() {
    // 创建一个结构体实例
    person := Person{
        Name:  "John Doe",
        Age:   30,
        Email: "john@example.com",
    }

    // 将结构体转换为JSON
    personJSON, err := json.Marshal(person)
    if err != nil {
        fmt.Println("Error marshalling to JSON:", err)
        return
    }

    // 将JSON字节切片转换为字符串并输出
    fmt.Println(string(personJSON))
}

JSON转结构体

可以使用json.Unmarshal函数将JSON字节数据转换为结构体。此函数需要传递JSON字节切片和一个指向目标结构体的指针。

func main() {
    // JSON数据
    jsonString := `{"name":"John Doe","age":30,"email":"john@example.com"}`

    // 创建一个空的结构体实例
    var person Person

    // 将JSON转换为结构体
    err := json.Unmarshal([]byte(jsonString), &person)
    if err != nil {
        fmt.Println("Error unmarshalling JSON:", err)
        return
    }

    // 输出结构体
    fmt.Printf("%+v\n", person)
}

处理嵌套结构体

如果JSON中包含嵌套对象,可以在结构体中定义嵌套的结构体。例如:

type Address struct {
    Street string `json:"street"`
    City   string `json:"city"`
}

type Person struct {
    Name    string  `json:"name"`
    Age     int     `json:"age"`
    Email   string  `json:"email"`
    Address Address `json:"address"`
}

然后JSON数据也需要包含嵌套结构:

func main() {
    // 嵌套JSON数据
    jsonString := `{"name":"John Doe","age":30,"email":"john@example.com","address":{"street":"123 Main St","city":"Anytown"}}`

    // 创建一个空的结构体实例
    var person Person

    // 将JSON转换为结构体
    err := json.Unmarshal([]byte(jsonString), &person)
    if err != nil {
        fmt.Println("Error unmarshalling JSON:", err)
        return
    }

    // 输出结构体
    fmt.Printf("%+v\n", person)
}

Go语言的结构体总结

1. 类比 Java 的类
  • 定义与封装

    • 在Java中,类使用 class 关键字定义,包含属性和方法,可以实现复杂的数据封装和功能封装。而在Go语言中,结构体使用 type 和 struct 关键字定义,主要作为数据的容器,用于封装多个不同类型的字段,但不直接包含方法。
  • 实例化

    • Java通过 new 关键字来实例化类的对象并初始化其属性。Go语言则可以通过语法糖直接初始化结构体实例,也可以使用 new 关键字获取结构体的指针。
  • 方法绑定

    • Java类包含方法,方法可以操作该类的实例,具备面向对象的特性。在Go语言中,尽管结构体本身不包含方法,但可以通过定义函数,并将其“绑定”到结构体类型上,模拟类似Java类的行为。
2. 类比 C 的结构体
  • 定义和使用

    • 在C语言中,结构体使用 struct 关键字定义,是一种将不同类型的数据组合在一起的方式。在Go语言中,结构体的定义和使用与C语言相似,但语法上更加简洁,并且没有 struct 关键字的重复使用。
  • 内存操作与指针

    • C语言处理结构体时经常使用指针操作,以节省内存和提高效率。Go语言中也使用指针来操作结构体,支持面向底层编程的需求,类似于C语言,但更安全,内建垃圾回收机制。
3. 结构体标签与JSON
  • 结构体标签

    • Go语言提供了结构体标签功能,通过在定义结构体字段时添加标签,可以为字段添加元信息。这在序列化和反序列化如JSON时非常有用,可以指定字段在JSON中的名称、格式或其他属性。
  • 与JSON互转

    • Go语言的 encoding/json 包简化了结构体和JSON数据之间的互转。通过标签定义和内建函数,可以轻松实现结构体到JSON和JSON到结构体的转换,这对于处理API请求和响应数据极为方便。

总结

Go语言的结构体结合了Java类的复杂数据封装与C语言结构体的简洁性和灵活性,是一种高效的复合数据类型。它能够有效组织和封装数据,同时支持方法绑定和内存指针操作,为编写高性能和高可维护性的代码提供了极大的便利。结构体标签和与JSON的互转功能进一步增强了结构体的应用场景,使其成为Go语言中不可或缺的数据结构。