跳到主要内容

结构体

基础

结构体是一系列不同或者相同数据类型的数据结构构成的数据集合,概念有点像面向对象中的类,但是更轻量。go语言的结构体跟c语言比较相似,都是通过struct关键字去构建。

Go语言中声明一个结构体的基本语法如下:

type 类型名 struct {
字段1 字段1类型
字段2 字段2类型
...
字段n 字段n类型
}

声明一个结构体方法的基本语法如下:

func (对象名 结构体类型) 方法名(方法参数) 返回值类型 {
// 方法体
}

// 示例
type Student struct {
Name string
Age int
}
func (sth Student) CreateNew(name string,age int) Student {
stu.Name = name
sth.Age = age

return stu
}

func main() {
stu1 := Student{}
stu1 = stu.CreateNew("zhangsan",33)
fmt.Printf("%+v", stu)

// 按顺序实例化结构体
stu2 := Student{"LiSi",23}

// 按键值方式实例化结构体
stu3 := Student{
Name: "WangWu",
Age: 24,
}

// 指向结构体的指针
stu4 := &Student{"ZhaoLiu",25}
// 此处有一个go的语法糖,对于指向结构体的指针,stu4.Name会自动转换成(*stu4).Name
fmt.Println(stu4.Name,stu4.Age)
}

结构体嵌套

Go语言结构体的嵌套可以实现类似面向对象继承的效果。

示例代码:

type Address struct {
Province string
City string
Steet string
}
type Student struct {
Name string
Age int
Address Address
}
func main() {
stu1 := Student{}
stu1.Name = "zhangsan"
stu1.Age = 33
stu1.Address.Province = "AH"
stu1.Address.City = "HF"
stu1.Address.Street = "BuXing"
fmt.Printf("%+v", stu1)
}

匿名结构体

Go语言还支持一种匿名结构体,定义时不用type关键字,于定义普通类型的变量一样。如果在函数体外定义匿名结构体变量,那么就需要var关键字,但在函数体类可省略。基本语法如下:

var 变量名 struct {
字段1 字段1类型
字段2 字段2类型
...
字段n 字段n类型
}

匿名结构体一般用作全局的配置数据存储结构。示例代码:

// 匿名结构体
var mysqlConfig struct {
user string
password string
host string
port int
}
func main() {
mysqlConfig.user = "root"
mysqlConfig.password = "123456"
mysqlConfig.host = "127.0.0.1"
mysqlConfig.port = 3306

// 用短变量的方式定义一个匿名结构体,声明结构的同时还要进行初始化
config2 := struct {
user string
password string
host string
port int
}{"root","123456","192.168.0.2",3306}
}

结构体的大小

  • string类型占16个字节
  • int64占8个字节
  • 指针占8个字节
  • float64占8个字节
package main
import (
"fmt"
"unsafe"
)

type Course struct {
name string
price int
url string
}

func main() {
var c1 Course = Course{}
fmt.Println(unsafe.Sizeof(c1))
}

结构体方法

  • 示例代码
type Course struct {
name string
price int
}

// 接收器以值传递的形式
func (c Course) setPrice1(newPrice int) {
c.price = newPrice
}

// 接收器以指针传递的形式
func (c *Course) setPrice2(newPrice int) {
c.price = newPrice
}

func main() {
var c1 Course = Course{
name: "go语言",
price: 100,
}
c1.printInfo() // 100
c1.setPrice1(123)
c1.printInfo() // 100
c1.setPrice2(321)
c1.printInfo() // 321
}

结构体方法只能和结构体在同一个包下。

结构体方法的接收器有两种形式:值传递和指针传递。值接收器方法不会改变结构体内容,只对副本操作。指针接收器方法可以修改结构体内容,因为操作的是结构体的内存地址。

  • 使用值传递的场景
    • 结构体比较小,拷贝成本低。
    • 不需要修改结构体的内容
    • 希望让方法在不同的接收器之间保持一致性
  • 使用指针接收器的场景
    • 需要修改结构体的内容
    • 结构体较大,避免拷贝开销
    • 需要确保方法对所有调用者生效,尤其是在多个函数中传递结构体时。

结构体标签

结构体的字段除了变量名和类型外,还有一个可选的标签(tag)。它是一个附属于字段的字符串,可以是文档或其他重要的标记。在json序列化中常见。 oitempty标签的作用:忽略0值或空值。 -的作用是不进行序列化。

package main

import (
"encoding/json"
"fmt"
)

type JsonExample struct {
Name string `json:"name,omitempty"` // 序列化后显示为name,若其值为空,则不显示该字段
Age int `json:"age"` // 序列化后显示为age
School string `json:"university"` // 序列化后显示为university
Class []string `json:"class"`
}

// 对象序列化为json
func JsonMarshal() {
var jex JsonExample = JsonExample{
Name: "Go",
Age: 12,
School: "TsingHua",
Class: []string{"YuWen", "ShuXue", "YingYu"},
}
by, _ := json.Marshal(jex)
fmt.Println(string(by))
}

// json反序列化为对象
func JsonUnmarshal() {
var v JsonExample
by := []byte(`{"name": "python","age":18,"university": "ustc","class": ["WuLi","HuaXue","ShengWu"]}`)
json.Unmarshal(by, &v)
fmt.Printf("%+v\n", v)
}

func main() {
JsonMarshal() // {"name":"Go","age":12,"university":"TsingHua","class":["YuWen","ShuXue","YingYu"]}
fmt.Println("=============")
JsonUnmarshal() // {Name:python Age:18 School:ustc Class:[WuLi HuaXue ShengWu]}
}