结构体
基础
结构体是一系列不同或者相同数据类型的数据结构构成的数据集合,概念有点像面向对象中的类,但是更轻量。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]}
}