0x00 结构体
基本数据类型——整型。布尔型,字符串等。表示现实生活中的物件有局限性,满足不了我们了,所以我们就要找新的东西来帮助我们。
编程使用代码解决现实世界的问题,我就想在代码中表示一个人,怎么表示呢?
结构体,是一种数据类型,一种我们自己造的可以保存多个维度数据的类型。只要涉及到保存多个维度数据,就用结构体。
type person struct{
name string
number int
addr string
...
...
...
}
结构体4种初始化方式
//第一种方式:类型推导
p1 := person{
name: "haolo",
age: 10,
number: 3,
}
fmt.Println(p1.name)
fmt.Println(p1.age)
fmt.Println(p1.number)
//第二种方式,简写
p2 := person{
"cao",
10,
10,
}
fmt.Println(p2.name)
fmt.Println(p2.age)
fmt.Println(p2.number)
//第三种方式
var p3 person
p3.name = "dehad"
p3.age = 10
p3.number = 102
fmt.Println(p3.name)
fmt.Println(p3.age)
fmt.Println(p3.number)
//第四种方式,构造函数
p4 := newPerson("woca", 10, 20)
fmt.Println("p4:", p4.age)
fmt.Println("p4:", p4.number)
fmt.Println("p4:", p4.name)
匿名结构体
无名称,多用于临时场景,只用一次。我只用一次,肯定就不要多次使用声明了。
type a = struct{
x int
}
0x01 构造函数
构造(结构体变量的)函数,返回值是对应的结构体类型。这是最基础的函数写法。
func newPerson(n string,i int)(p person){
p = person{
name : n,
age : i,
}
return p
}
优化一下上面的代码,其实就变成了不太好理解的:
func newPerson(n string,i int)person{
return person{
name : n,
age : i,
}
}
0x02 方法和接收者
方法是有接收者的函数,接收者指的是哪个类型的变量可以调用这个函数
func (p person) dream(str string){
fmt.Printf("%s的梦想是%s.\n", p.name,str)
}
//(p person)是指哪个类型可以调用这个函数,这就是接收者
//dream函数就是这个字符串
在Go语言中有两个类型,一个是值类型,一个是引用类型。
值类型说白了就是复制粘贴,而引用类型就像指针这样,指向原始的数据。
接收者为值类型
仔细看一下下面这段代码,结果是一样的。就是因为,之前的内存分析,函数changeAge()
在结束后,会销毁栈帧,里面的内容都会消失。改变的p.age
仅仅是副本,相当于拷贝进去了一份值,修改完后并没有返回,况且栈帧也被销毁。所以在调用changeAge()
函数前后结果是一样的。
type person struct {
name string
age int
}
func (p person) changeAge() {
p.age++
}
func main() {
p1 := person{
name: "beijing",
age: 10,
}
fmt.Println(p1.age)
p1.changeAge()
fmt.Println(p1.age)
}
接收者为指针类型
下面这段代码,就比上面多加了一个*
,在第6行。为什么结果就变了??
首先要明白,*person
表示的是person类型的指针,那么p就是一个person类型的指针。它所指向的就是person类型数据的p.age
,所以实际上就是原本的p.age
。那么这时候进行p.age++
,就是将原本的数据加1。
type person struct {
name string
age int
}
func (p *person) changeAge() {
p.age++
}
func main() {
p1 := person{
name: "beijing",
age: 10,
}
fmt.Println(p1.age)
p1.changeAge()
fmt.Println(p1.age)
}
接收者指针类型使用场景
1、需要修改结构体变量的值时,要使用指针接收者。
2、结构体本身比较大,拷贝的内存开销比较大时也要使用指针接收者。
3、保持一致性:如果有一个方法使用了指针接收者,其他的方法为了统一也要使用指针接收者。
0x03 结构体的嵌套
匿名嵌套结构体
type address struct{
province string
city string
}
type student struct{
name string
address //这里没有任何名字,就是匿名结构体,就使用类型做名称
}
普通嵌套结构体
type address struct{
province string
city string
}
type student struct{
name string //前面是名字,后面是类型
addr address
}
嵌套结构体初始化
无论是匿名结构体,还是普通结构体,开始调用的时候,都必须在下面的第14行中,写上key:value
键值对的形式,不然会报错。
type address struct {
province string
city string
}
type person struct {
name string
address
}
func main() {
p1 := person{
name: "Jackie Chen",
address: address{
province: "hebei",
city: "beijing",
},
}
fmt.Println(p1)
}
0x04 JSON序列化与反序列化
两个很容易错误的地方
1、结构体内部的字段首字母要大写!不大写别人是访问不到的
2、序列化时转换要用string转换类型
3、反序列化时要传递指针
序列化
type person struct {
name string
age int
}
func main() {
p1 := person{
"name",
10,
}
j1, err := json.Marshal(p1)
if err != nil {
fmt.Println("这个的结果错误为:", err)
}
fmt.Println(string(j1)) //因为结果为[]byte需要转换成string类型
}
因为在Go语言中,错误信息是返回的,需要接收。所以我们可以给出个判断,如果接受回来的错误不为空,就打印出err这个值,也就是错误信息;反之正常。
序列化打印的细节
可以看到上面运行的代码,结果为空。这是为什么呢?因为字段没有大写,所以没有调用成功。
type person struct {
Name string
Age int //注意这里,变为大写,这时就会调用成功
}
func main() {
p1 := person{
"name",
10,
}
j1, err := json.Marshal(p1)
if err != nil {
fmt.Println("这个的结果错误为:", err)
}
fmt.Println(string(j1)) //因为结果为[]byte需要转换成string类型
}
自定义名称
因为上面打印出来的结果就是大写的,我能不能自定义个?
type person struct {
Name string `json:"hhhhh"` //表示json包中均为这样定义输出
Age int `json:"wwwwwww"`
}
func main() {
p1 := person{
"name",
10,
}
j1, err := json.Marshal(p1)
if err != nil {
fmt.Println("这个的结果错误为:", err)
}
fmt.Println(string(j1)) //因为打印出来是一个切片类型,需要强转为字符串
}
0x05 练习
package main
import (
"encoding/json"
"fmt"
)
type Person3 struct {
Name string `json:"name"`
Sex string `json:"gender"`
}
// # 结构体实例化对象转JSON字符串
func serialize() {
// 示例1.字段首字母大小写影响的可见性
person3 := &Person3{"WeiyiGeek", "男人"}
//序列化
p3, err := json.Marshal(person3)
if err != nil {
fmt.Printf("Marshal Failed :%v", err)
return
}
// 由于返回是一个字节切片,所以需要强转为字符串
fmt.Printf("person3 -> %v\n", string(p3))
}
func main() {
serialize()
}
如果输出的时候不加上string()
,则结果为:
反序列化
由一个字符串,得出一个结构体的过程,就是反序列化。
package main
import (
"encoding/json"
"fmt"
)
// # JSON字符串转结构体实例化对象
type Person4 struct {
Name string `json:"name"`
Sex string `json:"sex"`
// Addr [3]string `json:"addr"`
}
func unserialize() {
jsonStr := `{"name": "WeiyiGeek","sex": "man"}`
// p4 := Person4{}
var p4 Person4
// 在其内部修改p4的值
err := json.Unmarshal([]byte(jsonStr), &p4)
if err != nil {
fmt.Printf("Unmarshal Failed: %v", err)
return
}
fmt.Printf("jsonStr -> Person4 : %#v\nPerson4.name : %v\n", p4, p4.Name)
}
func main() {
unserialize()
}