200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > go语言基础(二)函数 数组 指针 结构体

go语言基础(二)函数 数组 指针 结构体

时间:2024-06-27 17:05:54

相关推荐

go语言基础(二)函数 数组 指针 结构体

目录

函数匿名函数函数闭包的简单使用以及获取键盘输入闭包加上函数多返回值为什么闭包不会被垃圾回收defer内置函数panic、recover语言变量作用域数组数组定义数组+函数+宏定义二维数组指针make和new的区别二级指针的使用空指针指针数组将指针传入函数进行值的交换结构体自定义类型和类型别名结构体初始化匿名结构体结构体的内存布局结构体内存对齐各变量类型所占用空间结构体内存对齐构造函数结构体指针方法和接收者方法与函数的区别什么时候应该使用指针类型接收者关于方法和接收者地址传递深究三种方法更改年龄自定义类型和方法接收者,方法结合匿名字段结构体嵌套简单使用结构体嵌套+匿名字段访问结构体继承结构体字段可见性和JSON序列化反序列化结构体标签(Tag)

函数

函数定义格式

func function_name( [parameter list] ) [return_types] {

函数体

}

package mainimport "fmt"func test(num1 int,num2 int) int{return num1+num2}func main() {fmt.Printf("total num = %d",test(1,2))}

Opetion results:

total num = 3

go语言的函数返回值是可以多个函数返回值的

package mainimport "fmt"// 函数名 参数 参数类型 返回值类型func test(num1 int,num2 int) (int,string){return num1+num2,"nihao"}func main() {a,b := test(1,2)fmt.Printf("total num = %d ,string = %s",a,b)}

Operation results:

total num = 3 ,string = nihao

匿名函数

package mainimport "fmt"func main() {func() {fmt.Println("匿名函数")}() // 这个小括号代表匿名函数直接运行//或者这样使用test := func() {fmt.Println("匿名函数")}test()}

Operation results:

匿名函数

匿名函数

函数闭包的简单使用以及获取键盘输入

闭包 = 函数 + 外层变量引用

为什么要使用函数闭包?

因为使用函数闭包可以减少全局变量使用,下一次进入闭包函数,闭包函数中的变量值不会更改,相当于全局变量了

简单使用:

package mainimport "fmt"func test() func() string{return func() string{var name stringfmt.Println("Please input name")fmt.Scanf("%s",&name)fmt.Println(name)return name}}func main(){name := test()fmt.Println(name())}

Operation results:

Please input name

Finny

Finny

Finny

闭包意义体现,以及执行步骤:

看完下面例子,会发现闭包函数中的变量不会释放

package mainimport ("fmt")//定义了一个闭包函数func adder(a int) func(int) int {num := a //变量初始化fmt.Println(num)return func(x int) int {//闭包函数,需要注意的是闭包函数返回值num += x// fmt.Println(num)return num}}func main() {myAdder := adder(10)// 从1加到5for i := 1; i <= 5; i++ {myAdder(i)}fmt.Println(myAdder(0))// 再加上5fmt.Println(myAdder(5))}

Operation results:

10

25

30

闭包加上函数多返回值

package mainimport "fmt"func calc(base int) (func(int) int, func(int) int) {add := func(a int) int {base += areturn base}sub := func(b int) int {base -= breturn base}return add, sub}func main() {x, y := calc(200)fmt.Println(x(200))fmt.Println(y(100))}

Operation results:

400

300

为什么闭包不会被垃圾回收

为什么闭包不会被垃圾回收

因此闭包会长期占用内存资源,可能会导致内存泄漏

defer

package mainimport "fmt"func calc(index string, a, b int) int {ret := a + bfmt.Println(index, a, b, ret)return ret}/*x = 1 ,y = 2defer calc外面那层,里面那层不受外面defer影响 CALC("A", 1, 2)输出 A 1 2 3X = 10calc("B", 10, 2)输出 B 10 2 12y = 20因为最先defer的最后执行所以执行calc("BB", x, calc("B", x, y)) 也就是 calc("BB", 10, 12)输出 BB 10 12 22最后执行calc("AA", x, calc("A", x, y)) 也就是 calc("AA", 1, 3)输出 AA 1 3 4值还是那时候的值,注意是延迟执行!*/func main() {x := 1y := 2defer calc("AA", x, calc("A", x, y))x = 10defer calc("BB", x, calc("B", x, y))y = 20}

Operation results:

A 1 2 3

B 10 2 12

BB 10 12 22

AA 1 3 4

内置函数panic、recover

panic 和 recover 常搭配 defer使用,recover只在defer中有效,defer,recover组合要在panic之前使用,要不直接异常退出了,这三个配合有点像java里面的try catch

Recover 是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,recover 仅在延迟函数 defer 中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果,如果当前的 goroutine 陷入恐慌,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行。

通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态,如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。

提示

在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch 机制捕获异常,没有被捕获的严重异常会导致宕机,捕获的异常可以被忽略,让代码继续运行。

Go语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制。

package mainimport "fmt"func testFuncA() {fmt.Println("testFuncA OK!")}func testFuncB() {defer func() {err := recover()if err != nil {fmt.Println("Func B error")}}()panic("testFuncB Exception")}func testFuncC() {fmt.Println("testFuncA OK!")}func main() {testFuncA()testFuncB()testFuncC()}

Operation results:

testFuncA OK!

Func B error

testFuncA OK!

语言变量作用域

Go 语言中变量可以在三个地方声明:

函数内定义的变量称为局部变量

函数外定义的变量称为全局变量

函数定义中的变量称为形式参数

需要注意的一点是,go语言可以定义同名的全局变量和局部变量,但是局部变量的优先级大于全局变量的优先级

package mainimport ("fmt")var a int = 10func main() {var a int = 20fmt.Println(a) }

Operation results:

20

数组

数组定义

package mainimport ("fmt")func main() {var a [3]int //数组定义了未初始化var b = [3]int{1,2,3} //数组定义了按顺序初始化var c = [3]int{1:3,2:1} //数组定义了按索引初始化var d = [3]int{1,2:19} //数组定义了混合初始化(顺序和索引)//可以使用编译器推导长度 var b = [...]int{1,2,3}var i intfmt.Println("Array output of A")for i=0 ; i<3 ; i++{fmt.Println(a[i])}fmt.Println("Array output of B")for i=0 ; i<3 ; i++{fmt.Println(b[i])}fmt.Println("Array output of C")for i=0 ; i<3 ; i++{fmt.Println(c[i])}fmt.Println("Array output of D")for i=0 ; i<3 ; i++{fmt.Println(d[i])}}

Operation results:

Array output of A

0

0

0

Array output of B

1

2

3

Array output of C

0

3

1

Array output of D

1

0

19

数组+函数+宏定义

似乎go语言没有宏定义这个概念,但是我觉得const关键字相当于C语言宏定义define 只是从使用上来看哦,底层原理应该是不一样的。

package mainimport ("fmt")//Macro definitionconst (Arraysize = 5)//Funcfunc test(Array [Arraysize]int, Size int)int{i := 0var ArrayTotal int = 0for i=0;i<Size;i++{fmt.Println(Array[i])ArrayTotal += Array[i]}return ArrayTotal}//Mainfunc main() {var Array = [Arraysize]int{11,2,45,6,3} fmt.Println("Total array =",test(Array, Arraysize),"ok!")}

Operation results:

11

2

45

6

3

Total array = 67 ok!

二维数组

package mainimport "fmt"func main() {var Array = [3][2]string{{"北京", "上海"},{"苏州", "无锡"},{"广州", "深圳"},}fmt.Println(Array)//二维数组遍历for _,v1 := range Array{for _,v2 := range v1{fmt.Println(v2)}}}

Operation results:

[[北京 上海] [苏州 无锡] [广州 深圳]]

北京

上海

苏州

无锡

广州

深圳

指针

跟C语言一样,只需要记住指针也是个变量,跟int整形一样,只不过他是存放地址的变量

*在定义的时候只代表这是个几级指针,不代表取值

&是取地址

记清楚这两个符号,玩指针基本没问题

new(int)函数和直接定义的区别就是,new函数会初始化,直接定义的不会初始化

package mainimport "fmt"func main() {var a int = 10var p *int = &ap2 := new(int)p2 = &afmt.Printf("Variable address: %x\n", &a)fmt.Printf("Variable address: %x\n", p)fmt.Printf("Variable address: %x\n", p2)}

Operation results:

Variable address: c0000aa058

Variable address: c0000aa058

Variable address: c0000aa058

make和new的区别

make也是用于内存分配的,区别于new,它只用于slice、map以及chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。func make(t Type, size …IntegerType) Type

func make(t Type, size …IntegerType) Type

二者都是用来做内存分配的。make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

二级指针的使用

package mainimport "fmt"func main() {var a int = 10//整形var p1 *int = &a //一级指针var p2 **int = &p1 //二级指针fmt.Printf("Variable address: %x\n", &a )fmt.Printf("Variable address: %x\n", p1 )fmt.Printf("Variable address: %x\n", &p1 )fmt.Printf("Variable address: %x\n", p2 )fmt.Printf("Variable address: %x\n", *p2 )fmt.Printf("Variable content: %d\n", a )fmt.Printf("Variable content: %d\n", *p1 )fmt.Printf("Variable content: %d\n", **p2 )}

Operation results:

Variable address: c000012088

Variable address: c000012088

Variable address: c000006028

Variable address: c000006028

Variable address: c000012088

Variable content: 10

Variable content: 10

Variable content: 10

空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil。

nil 指针也称为空指针。

nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

一个指针变量通常缩写为 ptr

package mainimport "fmt"func main() {var p1 *intfmt.Println(p1)}

Operation results:

< nil >

指针数组

package mainimport "fmt"const MAX int = 3func main() {a := []int{10,100,200}var i intvar ptr [MAX]*int;for i = 0; i < MAX; i++ {ptr[i] = &a[i] /* 整数地址赋值给指针数组 */}for i = 0; i < MAX; i++ {fmt.Printf("a[%d] = %d\n", i,*ptr[i] )}}

Operation results:

a[0] = 10

a[1] = 100

a[2] = 200

将指针传入函数进行值的交换

package mainimport "fmt"func exchange(p1 *int, p2 *int)int{*p1 , *p2 = *p2 , *p1return 1}func main() {var a int = 10var b int =var p1 *int = &avar p2 *int = &bfmt.Println(*p1)fmt.Println(*p2)if exchange(p1, p2) == 1{fmt.Println("exchange ok!")}fmt.Println(*p1)fmt.Println(*p2)}

Operation results:

10

exchange ok!

10

结构体

自定义类型和类型别名

package mainimport "fmt"//自定义类型type UINT16 uint16//类型别名type UINT8 = uint8func main() {var a UINT16var b UINT8fmt.Printf("Type = %T Value = %v\n", a, a)fmt.Printf("Type = %T Value = %v\n", b, b)}

Operation results:

Type = main.UINT16 Value = 0

Type = uint8 Value = 0

结构体初始化

package mainimport "fmt"type Human struct {name, city stringage uint8}func main() {//结构体键值对初始化man1 := Human{name: "zhangsan",age: 18,}fmt.Printf("%#v\n", man1)man2 := Human{name: "zhangsan",city: "suzhou",age: 32,}fmt.Printf("%#v\n", man2)//值初始化,要按照顺序woman1 := Human{"wangwu","beijing",21,}fmt.Printf("%#v\n", woman1)}

Operation results:

main.Human{name:“zhangsan”, city:"", age:0x12}

main.Human{name:“zhangsan”, city:“suzhou”, age:0x20}

main.Human{name:“wangwu”, city:“beijing”, age:0x15}

匿名结构体

package mainimport "fmt"func main() {//匿名结构体var HideStruct struct {name stringage uint8}HideStruct.name = "Finny"HideStruct.age = 21fmt.Printf("%#v\n", HideStruct)fmt.Println(HideStruct.name)fmt.Println(HideStruct.age)}

Operation results:

struct { name string; age uint8 }{name:“Finny”, age:0x15}

Finny

21

结构体的内存布局

package mainimport "fmt"type Human struct {a int8b int8c int8d int8}func main() {n := Human{1, 2, 3, 4,}b := Human{1, 2, 3, 4,}fmt.Printf("n.a %p\n", &n.a)fmt.Printf("n.b %p\n", &n.b)fmt.Printf("n.c %p\n", &n.c)fmt.Printf("n.d %p\n", &n.d)fmt.Printf("b.a %p\n", &b.a)fmt.Printf("b.b %p\n", &b.b)fmt.Printf("b.c %p\n", &b.c)fmt.Printf("b.d %p\n", &b.d)}

Operation results:

n.a 0xc000012088

n.b 0xc000012089

n.c 0xc00001208a

n.d 0xc00001208b

b.a 0xc00001208c

b.b 0xc00001208d

b.c 0xc00001208e

b.d 0xc00001208f

结构体内存对齐

各变量类型所占用空间

package mainimport ("fmt""unsafe")func main() {var UINT8T uint8var UINT16T uint16var UINT32T uint32var UINT64T uint64var INT8T int8var INT16T int16var INT32T int32var INT64T int64var STRING stringvar BOOL boolvar INT intvar UINT uintvar UINTPTR uintptrvar FLOAT32 float32var FLOAT64 float64var INTPOINT *intvar UINTPOINT *uintvar CHARPOINT *stringvar FLOAT64POINT *float64fmt.Println("uint8 sizeof:", unsafe.Sizeof(UINT8T))fmt.Println("uint16 sizeof:", unsafe.Sizeof(UINT16T))fmt.Println("uint32 sizeof:", unsafe.Sizeof(UINT32T))fmt.Println("uint64 sizeof:", unsafe.Sizeof(UINT64T))fmt.Println("----------------->")fmt.Println("int8 sizeof:", unsafe.Sizeof(INT8T))fmt.Println("int16 sizeof:", unsafe.Sizeof(INT16T))fmt.Println("int32 sizeof:", unsafe.Sizeof(INT32T))fmt.Println("int64 sizeof:", unsafe.Sizeof(INT64T))fmt.Println("----------------->")fmt.Println("string sizeof:", unsafe.Sizeof(STRING))fmt.Println("bool sizeof:", unsafe.Sizeof(BOOL))fmt.Println("INT sizeof:", unsafe.Sizeof(INT))fmt.Println("UINT sizeof:", unsafe.Sizeof(UINT))fmt.Println("UINTPTR sizeof:", unsafe.Sizeof(UINTPTR))fmt.Println("----------------->")fmt.Println("FLOAT32 sizeof:", unsafe.Sizeof(FLOAT32))fmt.Println("FLOAT64 sizeof:", unsafe.Sizeof(FLOAT64))fmt.Println("----------------->")fmt.Println("INTPOINT sizeof:", unsafe.Sizeof(INTPOINT))fmt.Println("UINTPOINT sizeof:", unsafe.Sizeof(UINTPOINT))fmt.Println("CHARPOINT sizeof:", unsafe.Sizeof(CHARPOINT))fmt.Println("FLOAT64POINT sizeof:", unsafe.Sizeof(FLOAT64POINT))}

Operation results:

uint8 sizeof: 1

uint16 sizeof: 2

uint32 sizeof: 4

uint64 sizeof: 8

----------------->

int8 sizeof: 1

int16 sizeof: 2

int32 sizeof: 4

int64 sizeof: 8

----------------->

string sizeof: 16

bool sizeof: 1

INT sizeof: 8

UINT sizeof: 8

UINTPTR sizeof: 8

----------------->

FLOAT32 sizeof: 4

FLOAT64 sizeof: 8

----------------->

INTPOINT sizeof: 8

UINTPOINT sizeof: 8

CHARPOINT sizeof: 8

FLOAT64POINT sizeof: 8

结构体内存对齐

我突然想试试go语言会不会有内存对齐

Go struct 内存对齐

详聊内存对齐

package mainimport ("fmt""unsafe")type Test struct {//四字节对齐a uint8 //1---3b float32 //4---0c uint8 //1---3}type Test2 struct {//四字节对齐a uint8c uint8 //1+1 --- 2b float32 //4 --- 0}type Test3 struct {//八字节对齐a uint8 //1 --- 7b float64 //8 --- 0c string //16 --- 0}type Test4 struct {//八字节对齐a uint8 //1 --- 7b float64 //8 --- 0c float64 //8 --- 0d string //16 --- 0}type Test5 struct {//八字节对齐b float64 //8 --- 0c float64 //8 --- 0d string //16 --- 0a uint8 //1 --- 7}type Test6 struct {//八字节对齐a uint8 //1 --- 7b string //16 --- 0}type Test7 struct {//按照四字节对齐a Test //12(4+4+4) --- 0b uint8 //1 --- 3}type Test8 struct {//按照四字节对齐a Test2 //8(4+4) --- 0b uint8 //1--- 3}type Test9 struct {//按照八字节对齐a Test5 //40 --- 0b uint8 //1 --- 7}type Test10 struct {//数组只是多个而已同类型变量//最大是1字节,所以是按照1字节对齐b uint8a [10]uint8}type Test11 struct {//int类型是8字节,对齐是八字节对齐 10*8 + 1偏移7 = 88b uint8a [10]int}func main() {fmt.Println("T1", unsafe.Sizeof(Test{}))fmt.Println("T2", unsafe.Sizeof(Test2{}))fmt.Println("T3", unsafe.Sizeof(Test3{}))fmt.Println("T4", unsafe.Sizeof(Test4{}))fmt.Println("T5", unsafe.Sizeof(Test5{}))fmt.Println("T6", unsafe.Sizeof(Test6{}))fmt.Println("T7", unsafe.Sizeof(Test7{}))fmt.Println("T8", unsafe.Sizeof(Test8{}))fmt.Println("T9", unsafe.Sizeof(Test9{}))fmt.Println("T10", unsafe.Sizeof(Test10{}))fmt.Println("T11", unsafe.Sizeof(Test11{}))}

Operation results:

T1 12

T2 8

T3 32

T4 40

T5 40

T6 24

T7 16

T8 12

T9 48

T10 11

T11 88

构造函数

package mainimport "fmt"type Person struct {name stringage uint8}//构造函数 :构造一个结构体示例的函数func NewPerson(NewName string, NewAge uint8) Person {return Person{name: NewName,age: NewAge,}}func main() {structerone := NewPerson("Finny", 21)fmt.Println(structerone)}

Operation results:

{Finny 21}

结构体指针

package mainimport "fmt"type Human struct{name stringage int}func PutStructData(data *Human){fmt.Println(data.name)fmt.Println(data.age)data.age = 98}func main() {var zhangsan Humanzhangsan.name = "zhangsan"zhangsan.age = 21PutStructData(&zhangsan)fmt.Println("name:",zhangsan.name,"age:",zhangsan.age)}

Operation results:

zhangsan

21

name: zhangsan age: 98

方法和接收者

package mainimport "fmt"type Person struct {name stringage int8}//NewPerson 构造函数func NewPerson(name string, age int8) *Person {return &Person{name: name,age: age,}}//Dream Person做梦的方法func (p Person) Dream() {fmt.Printf("%s的梦想是学好Go语言!\n", p.name)}func main() {p1 := NewPerson("张三", 25)p2 := NewPerson("李四", 25)p1.Dream()p2.Dream()}

Operation results:

张三的梦想是学好Go语言!

李四的梦想是学好Go语言!

方法与函数的区别

函数不属于任何类型,方法属于特定的类型。

什么时候应该使用指针类型接收者

1.需要修改接收者中的值

2.接收者是拷贝代价比较大的大对象

3.保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。

关于方法和接收者地址传递深究

package mainimport "fmt"type Person struct {name stringage uint8}//NewPerson 构造函数func NewPerson(name string, age uint8) *Person {return &Person{name: name,age: age,}}func (p *Person) ChangeAge(NewAge uint8) {fmt.Printf("address %p\n", &p)fmt.Printf("address %p\n", p)p.age = NewAge}func main() {p1 := NewPerson("张三", 25)p1.ChangeAge(21)fmt.Printf("address %p\n", p1)fmt.Printf("address %p\n", &p1)}

Operation results:

address 0xc000006030

address 0xc000004078

address 0xc000004078

address 0xc000006028

三种方法更改年龄

package mainimport "fmt"type Person struct {name stringage uint8}//NewPerson 构造函数func NewPerson(name string, age uint8) *Person {return &Person{name: name,age: age,}}func (p *Person) ChangeAge(NewAge uint8) {p.age = NewAge}func (p Person) ChangeAge2(NewAge uint8) *Person {p.age = NewAgereturn &p}func (p Person) ChangeAge3(NewAge uint8) Person {p.age = NewAgereturn p}func main() {//p1,p2这时候是指针,存放着构造出来的结构体地址p1 := NewPerson("Finny", 25)fmt.Println(*p1)p1.ChangeAge(21)fmt.Println(*p1)p1 = p1.ChangeAge2(43)fmt.Println(*p1)*p1 = p1.ChangeAge3(87)fmt.Println(*p1)}

Operation results:

{Finny 25}

{Finny 21}

{Finny 43}

{Finny 87}

自定义类型和方法接收者,方法结合

package mainimport "fmt"type MEMsensingString stringfunc (Name MEMsensingString) PrintHello() {fmt.Println(Name, "say hello!")}func main() {var myName MEMsensingString = "Finny"myName.PrintHello()}

Operation results:

Finny say hello!

匿名字段

字段就是变量名字,匿名字段要求类型唯一,感觉有点鸡肋。。

package mainimport "fmt"type Person struct {stringint}func main() {p1 := Person{"Finny",21,}fmt.Println(p1)}

Operation results:

{Finny 21}

结构体嵌套

简单使用

package mainimport "fmt"type PersonAddress struct {Province stringCitystring}type Person struct {Name stringAgeintAddress PersonAddress}func main() {p1 := Person{Name: "Finny",Age: 21,Address: PersonAddress{Province: "Jiangsu",City:"Suzhou",},}fmt.Println(p1)}

Operation results:

{Finny 21 {Jiangsu Suzhou}}

结构体嵌套+匿名字段访问

package mainimport "fmt"type PersonAddress struct {Province stringCitystring}type Person struct {Name stringAge intPersonAddress}func main() {p1 := Person{Name: "Finny",Age: 21,PersonAddress: PersonAddress{Province: "Jiangsu",City:"Suzhou",},}fmt.Printf("%#v\n", p1)fmt.Println(p1.PersonAddress.Province) //通过嵌套的匿名结构体字段访问其内部的字段fmt.Println(p1.Province)//直接访问匿名结构体中的字段}

Operation results:

main.Person{Name:“Finny”, Age:21, PersonAddress:main.PersonAddress{Province:“Jiangsu”, City:“Suzhou”}}

Jiangsu

Jiangsu

结构体继承

package mainimport "fmt"//结构体继承type Animal struct {name string}func (a *Animal) move() {fmt.Printf("%s会动~\n", a.name)}type Dog struct {Feet int8*Animal //匿名嵌套,而且嵌套的是一个结构体指针}func (d *Dog) wang() {fmt.Printf("%s是狗会汪汪汪, 有%d条腿\n", d.name, d.Feet)}func main() {d1 := &Dog{Feet: 4,Animal: &Animal{name: "西巴",},}d1.wang()d1.move()}

Operation results:

西巴是狗会汪汪汪, 有4条腿

西巴会动~

结构体字段可见性和JSON序列化反序列化

结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。

package mainimport ("encoding/json""fmt")//结构体字段的可见性与JSON序列化//如果一个Go语言包中定义的标识符是首字母大写的,那么就是对外可见的。//如果结构体中的字段名首字母是大写的,那么该字符就是对外可见的。//(例如把class结构体的Title换成小写的,那么其他包根本访问不到结构体这个字段)//构造函数func NewStudent(id int, name string) student {return student{Id: id,Name: name,}}type student struct {Id intName string}type class struct {Title stringStudents []student}func main() {//创建班级c1 := class{Title: "宇宙班",Students: make([]student, 0, 20),}//添加学生for i := 0; i < 10; i++ {tmp := NewStudent(i, fmt.Sprintf("stu%02d", i))c1.Students = append(c1.Students, tmp)}fmt.Println(c1.Students)//JSON序列化:GO语言中的数据 -> JSON格式的字符串data, err := json.Marshal(c1)if err != nil {fmt.Println("json marshal failed", err)}fmt.Println("------JSON 序列化------")fmt.Printf("%T\n", data)fmt.Printf("%s\n", data)//JSON反序列化JSONStr := `{"Title":"宇宙班","Students":[{"Id":0,"Name":"stu00"},{"Id":1,"Name":"stu01"},{"Id":2,"Name":"stu02"},{"Id":3,"Name":"stu03"},{"Id":4,"Name":"stu04"},{"Id":5,"Name":"stu05"},{"Id":6,"Name":"stu06"},{"Id":7,"Name":"stu07"},{"Id":8,"Name":"stu08"},{"Id":9,"Name":"stu09"}]}`var c2 classerr = json.Unmarshal([]byte(JSONStr), &c2)if err != nil {fmt.Println("json unmarshal error")}fmt.Println("------JSON 反序列化------")fmt.Printf("%#v\n", c2)}

Operation results:

[{0 stu00} {1 stu01} {2 stu02} {3 stu03} {4 stu04} {5 stu05} {6 stu06} {7 stu07} {8 stu08} {9 stu09}]

------JSON 序列化------

[]uint8

{“Title”:“宇宙班”,“Students”:[{“Id”:0,“Name”:“stu00”},{“Id”:1,“Name”:“stu01”},{“Id”:2,“Name”:“stu02”},{“Id”:3,“Name”:“stu03”},{“Id”:4,“Name”:“stu04”},{“Id”:5,“Name”:“stu05”},{“Id”:6,“Name”:“stu06”},{“Id”:7,“Name”:“stu07”},{“Id”:8,“Name”:“stu08”},{“Id”:9,“Name”:“stu09”}]}

------JSON 反序列化------

main.class{Title:“宇宙班”, Students:[]main.student{main.student{Id:0, Name:“stu00”}, main.student{Id:1, Name:“stu01”}, main.student{Id:2, Name:“stu02”}, main.student{Id:3, Name:“stu03”}, main.student{Id:4, Name:“stu04”}, main.student{Id:5, Name:“stu05”}, main.student{Id:6, Name:“stu06”}, main.student{Id:7, Name:“stu07”}, main.student{Id:8, Name:“stu08”}, main.student{Id:9, Name:“stu09”}}}

结构体标签(Tag)

package mainimport ("encoding/json""fmt")//结构体字段的可见性与JSON序列化//如果一个Go语言包中定义的标识符是首字母大写的,那么就是对外可见的。//如果结构体中的字段名首字母是大写的,那么该字符就是对外可见的。//(例如把class结构体的Title换成小写的,那么其他包根本访问不到结构体这个字段)//构造函数func NewStudent(id int, name string) student {return student{Id: id,Name: name,}}type student struct {Id intName string}type class struct {Title string `json:"jsonTitle"`Students []student `json:"jsonStudents"`}func main() {//创建班级c1 := class{Title: "宇宙班",Students: make([]student, 0, 20),}//添加学生for i := 0; i < 10; i++ {tmp := NewStudent(i, fmt.Sprintf("stu%02d", i))c1.Students = append(c1.Students, tmp)}fmt.Println(c1.Students)//JSON序列化:GO语言中的数据 -> JSON格式的字符串data, err := json.Marshal(c1)if err != nil {fmt.Println("json marshal failed", err)}fmt.Println("------JSON 序列化------")fmt.Printf("%T\n", data)fmt.Printf("%s\n", data)}

Operation results:

[{0 stu00} {1 stu01} {2 stu02} {3 stu03} {4 stu04} {5 stu05} {6 stu06} {7 stu07} {8 stu08} {9 stu09}]

------JSON 序列化------

[]uint8

{“jsonTitle”:“宇宙班”,“jsonStudents”:[{“Id”:0,“Name”:“stu00”},{“Id”:1,“Name”:“stu01”},{“Id”:2,“Name”:“stu02”},{“Id”:3,“Name”:“stu03”},{“Id”:4,“Name”:“stu04”},{“Id”:5,“Name”:“stu05”},{“Id”:6,“Name”:“stu06”},{“Id”:7,“Name”:“stu07”},{“Id”:8,“Name”:“stu08”},{“Id”:9,“Name”:“stu09”}]}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。