Go语言学习19-结构体的总结复习回顾

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)
}

image-20220226120606369

接收者为指针类型

下面这段代码,就比上面多加了一个*,在第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)
}

image-20220226120701950

接收者指针类型使用场景

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)
}

image-20220226123016462

0x04 JSON序列化与反序列化

两个很容易错误的地方

1、结构体内部的字段首字母要大写!不大写别人是访问不到的

2、序列化时转换要用string转换类型

3、反序列化时要传递指针

序列化

image-20220226124632192

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这个值,也就是错误信息;反之正常。

序列化打印的细节

可以看到上面运行的代码,结果为空。这是为什么呢?因为字段没有大写,所以没有调用成功。

image-20220226124920155

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类型
}

image-20220226125154940

自定义名称

因为上面打印出来的结果就是大写的,我能不能自定义个?

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))			//因为打印出来是一个切片类型,需要强转为字符串
}

image-20220226125403654

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(),则结果为:

image-20220226133651991

image-20220226133658303

反序列化

由一个字符串,得出一个结构体的过程,就是反序列化。

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()
}
posted @ 2022-02-26 15:55  sukusec不觉水流  阅读(14)  评论(0编辑  收藏  举报