结构体
什么是结构体
Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。
Go语言中通过结构体的内嵌再配合接口比面向对象具有更高的扩展性和灵活性。
结构体定义
基本实例化(方法1)
只有当结构体实例化时,才会真正地分配内存,也就是必须实例化后才能使用结构体的字段。
结构体本身也是一种类型,我们可以像声明内置类型一样使用 var
关键字声明结构体类型
new
实例化(方法2)
我们还可以通过使用 new
关键字对结构体进行实例化,得到的是结构体的地址
从打印的结果中我们可以看出 p2
是一个结构体指针。
注意:在 Golang 中支持对结构体指针直接使用.来访问结构体的成员。
p2.name = "张三"
其实在底层是 (*p2).name = "张三"
键值对初始化(方法3)
注意:最后一个属性的,要加上
使用:=
的方法
以上三种方式示例如下:
package main
import "fmt"
/*
这一块大家学会两个事情
第一:如何定义一个结构体
第二:初始化结构体的三种方法
*/
func main() {
// 结构体数据类型,可以存储json这种数据,绑定方法
// 1、通过var关键字来实例化结构体
var p1 Person
p1.Age = 28
p1.name = "zhangsan"
fmt.Println(p1)
fmt.Println(p1.name)
// 2、new方法实例化一个结构体(返回指针类型)
var p2 = new(Person)
p2.Age = 25
p2.name = "lisi"
fmt.Println(p2)
// 3、:= 键值对初始化
p3 := Person{
name: "wangwu",
Age: 35,
}
fmt.Println(p3)
}
/*
type: 关键字
Person: 结构体名字
struct: 定义结构体的关键字
*/
// golang中严格区分大小写(json序列化的时候,或者不同包访问是不能访问小写)
type Person struct {
name string
Age int
}
/*
1、结构体,与函数啥区别啊。什么时候用结构体呢?
结构体是go语言中实现类的一种方案,简单理解一个是类,一个是方法
*/
结构体方法和接收者
结构体说明
在 go 语言中,没有类的概念但是可以给类型(结构体,自定义类型)定义方法。
所谓方法就是定义了接收者的函数。
Go语言中的方法(Method)是一种作用于特定类型变量的函数。
这种特定类型变量叫做接收者(Receiver)。
接收者的概念就类似于其他语言中的this或者 self。
方法的定义格式如下:
func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
函数体
}
给结构体 Person 定义一个方法打印 Person 的信息
结构体方法和接收者
package main
import "fmt"
// 要求:只要能理解go语言中有一种调用方法的方式,可以把一个方法绑定到一个结构体中
// 然后通过结构体就可以调用这个方法
// 函数式编程和类编程只是两种编程思想,如果个别同学还没发理解这种思维
// - 先按照讲的四步把代码写出来,会写
// - 会用:看场景,看别人的代码
// 结构体用法:用来模拟其他语言中的类
// 函数调用方式:直接通过函数名字调用
// 结构体方法调用:第一、初始化一个结构体 第二、通过结构体这对象.方法名调用
func main() {
//第二步:实例化一个结构体(实例化一个类)
pxx := Person{
Name: "zhangsan",
Age: 28,
}
//fmt.Println(pxx)
// 第四步:调用结构的方法(通过类调用方法)
pxx.printInfo()
//px2 := Person{
// Name: "lisi",
// Age: 26,
//}
//px2.printInfo()
}
// PersonInfo
// 第一步:定义结构体(定义一个类)
type Person struct {
Name string
Age int
}
// 第三步:给结构体绑定方法和接收者
//func printInfo() { // 这样定义的是一个函数
//
//}
/*
(p Person) : 接收者
- p (变量名字)类似于我们python中类函数中的 self,或者js中类中的this
- Person 接收者的结构体
printInfo() : 一个方法绑定了结构体之后就叫做 结构体方法
*/
func (xx Person) printInfo() { // 这样定义的是一个函数
fmt.Println("name: ", xx.Name, xx.Age)
}
值类型和指针类型接收者
实例1:给结构体 Person 定义一个方法打印 Person 的信息
1、值类型的接收者
当方法作用于值类型接收者时,Go 语言会在代码运行时 将接收者的值复制一份 。
在值类型接收者的方法中可以获取接收者的成员值,但 修改操作只是针对副本 ,无法修改接收者变量本身。
2、指针类型的接收者
指针类型的接收者由一个结构体的指针组成
由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。
这种方式就十分接近于其他语言中面向对象中的 this 或者 self。
package main
import "fmt"
func main() {
// 2、实例化结构体(三种方法)
p := Person{
Name: "zhangsan",
Age: 24,
}
p.setInfo()
p.sayHi()
fmt.Println("main", p.Name) // 理论上是lisi
fmt.Println("main", p.Age)
}
// 1、定义一个结构体
type Person struct {
Name string
Age int
}
// 3、指针接收者绑定方法(*Person传递的是一个指针)
// 指针接收者才类似于python语言的self
func (xx *Person) setInfo() {
fmt.Println("通过结构体调用了方法")
xx.Name = "lisi"
}
// 值接收者(Person) 只传递的是拷贝的一份数据
// 值接收者修改数据,只是修改的是拷贝的那份数据,原始数据
func (xx Person) sayHi() {
fmt.Println("hello world")
xx.Age = 100
fmt.Println("sayHi", xx.Age)
}
encoding-json包
struct与json
比如我们 Golang 要给 App 或者小程序提供 Api 接口数据,这个时候就需要涉及到结构体和Json 之间的相互转换
GolangJSON 序列化是指把结构体数据转化成 JSON 格式的字符串
Golang JSON 的反序列化是指把 JSON 数据转化成 Golang 中的结构体对象
Golang 中 的 序 列 化 和 反 序 列 化 主 要 通 过 "encoding/json
" 包 中 的json.Marshal()
和 json.Unmarshal()
方法实现
1)struct转Json字符串
package main
import (
"encoding/json"
"fmt"
)
// 序列化:将结构体转化成json字符串
func main() {
// 2、初始化结构体
s := Student{
ID: 1,
Name: "zhangsan",
Address: "bj",
Age: 24,
}
fmt.Printf("%T %#v \n", s, s)
// 3、将结构体转化为json数据
/*
json.Marshal方法返回两个值
第一个值: sByte 是一个 []byte 对象(将结构体转化出来的数据)
第二个值: err json.Marshal转化失败,通过err接收
*/
sByte, err := json.Marshal(s)
if err != nil {
fmt.Println("json.Marshal err, ", err)
}
// 将string(sByte)类型转换为 string类型
fmt.Println(string(sByte))
/*
结构体数据:main.Student main.Student{ID:1, Name:"zhangsan", Age:24, Address:"bj"}
json数据:{"ID":1,"Name":"zhangsan","Age":24,"Address":"bj"}
*/
}
// 1、定义结构体
type Student struct {
ID int
Name string
Age int
Address string
}
2)Json字符串转struct
package main
import (
"encoding/json"
"fmt"
)
// 反序列化:将json字符串转换成struct对象
/*
结构体的作用有两个
第一:绑定方法和接收者,然后通过结构体调用方法(类方法)
第二:可以和json字符串进行相互转
*/
/*
序列化: 将struct对象转换成 json的string格式
反序列化:将一个json字符串转换成struct对象
*/
func main() {
//// 2、反序列化:结构体对应的json字符串数据(string)
//// 当字符串 本身有双引号,或者 多行的时候使用 ``
s := `{"ID":1,"Name":"zhangsan","Age":24,"Address":"bj"}`
// 3、将json字符串转换为struct结构体
var stu Student
// 将string类型的数据转换为一个 []byte类型数据
byteS := []byte(s)
err := json.Unmarshal(byteS, &stu)
if err != nil {
fmt.Println("json.Unmarshal err, ", err)
}
//// %#v 是将数据展开
//fmt.Printf("%T %#v \n", stu, stu)
//fmt.Println(stu.Name, stu.Address)
//// 4、结构体嵌套
//p := Person{
// Id: 1,
// Stu: []Student{
// {ID: 1, Name: "zs", Age: 24, Address: "bj"},
// {ID: 2, Name: "lisi", Age: 24, Address: "bj"},
// {ID: 3, Name: "wangwu", Age: 24, Address: "bj"},
// },
//}
//
//s, _ := json.Marshal(p.Stu)
//fmt.Println(string(s))
}
type Person struct {
Id int
Stu []Student
}
// 1、定义结构体
type Student struct {
ID int
Name string
Age int
Address string
}
struct tag
1)Tag标签说明
- Tag 是结构体的元信息,可以在运行的时候通过反射的机制读取出来。
- Tag 在结构体字段的后方定义,由一对反引号包裹起来
- 具体的格式如下:
key1:"value1" key2:"value2"
- 结构体 tag 由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。
- 同一个结构体字段可以设置多个键值对 tag,不同的键值对之间使用空格分隔。
- 注意事项:
- 为结构体编写 Tag 时,必须严格遵守键值对的规则。
- 结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。
- 例如不要在 key 和 value 之间添加空格。
2)Tag结构体转化Json字符串
package main
import (
"encoding/json"
"fmt"
)
func main() {
s1 := Student{
Name: "zhangsan",
Age: 24,
}
// 转json
s, _ := json.Marshal(s1)
fmt.Println(string(s))
}
// 定义结构体
type Student struct {
// `json:"xxx"` 定义tag 将Name字段给他与name做反射
// json是关键字
Name string `json:"xxx"`
//name string `json:"name"`
Age int `json:"age"`
}
3、Json字符串转成Tag结构体
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
ID int `json:"id"` //通过指定 tag 实现 json 序列化该字段时的 key
Gender string `json:"gender"`
Name string
Sno string
}
func main() {
var s2 Student
var str = `{"id":1,"gender":"男","Name":"李四","Sno":"s0001"}`
err := json.Unmarshal([]byte(str), &s2)
if err != nil {
fmt.Println(err)
} // main.Student{ID:1, Gender:"男", Name:"李四", Sno:"s0001"}
fmt.Printf("%#v", s2)
}