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

函数

函数定义格式
func function_name( [parameter list] ) [return_types] {
函数体
}

package main

import "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 main

import "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 main

import "fmt"

func main() {
	func() {
		fmt.Println("匿名函数")
	}() // 这个小括号代表匿名函数直接运行

	//或者这样使用
	test := func() {
		fmt.Println("匿名函数")
	}
	test()
}

Operation results:
匿名函数
匿名函数

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

闭包 = 函数 + 外层变量引用
为什么要使用函数闭包?
因为使用函数闭包可以减少全局变量使用,下一次进入闭包函数,闭包函数中的变量值不会更改,相当于全局变量了

简单使用:

package main

import "fmt"

func test() func() string{
	return func() string{
		var name string
		fmt.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 main

import (
	"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加到5
	for i := 1; i <= 5; i++ {
		myAdder(i)
	}
	
	fmt.Println(myAdder(0))
	// 再加上5
	fmt.Println(myAdder(5))
}

Operation results:
10
25
30

闭包加上函数多返回值

package main

import "fmt"

func calc(base int) (func(int) int, func(int) int) {
	add := func(a int) int {
		base += a
		return base
	}

	sub := func(b int) int {
		base -= b
		return base
	}

	return add, sub
}

func main() {
	x, y := calc(200)
	fmt.Println(x(200))
	fmt.Println(y(100))
}

Operation results:
400
300

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

为什么闭包不会被垃圾回收
因此闭包会长期占用内存资源,可能会导致内存泄漏

defer

在这里插入图片描述

package main

import "fmt"

func calc(index string, a, b int) int {
	ret := a + b
	fmt.Println(index, a, b, ret)
	return ret
}

/*
x = 1 ,y = 2
defer calc外面那层,里面那层不受外面defer影响 CALC("A", 1, 2)
输出 A 1 2 3

X = 10
calc("B", 10, 2)
输出 B 10 2 12

y = 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 := 1
	y := 2
	defer calc("AA", x, calc("A", x, y))
	x = 10
	defer 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 main

import "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 main

import (
	"fmt"
)

var a int = 10

func main() {
	var a int = 20
	fmt.Println(a) 
}

Operation results:
20

数组

数组定义

package main

import (
	"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 int
	fmt.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 main

import (
	"fmt"
)
//Macro definition
const (Arraysize = 5)

//Func
func test(Array [Arraysize]int, Size int)int{
	i := 0
	var ArrayTotal int = 0
	for i=0;i<Size;i++{
		fmt.Println(Array[i])
		ArrayTotal += Array[i]
	}
	return ArrayTotal
}

//Main
func 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 main

import "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 main

import "fmt"

func main() {
	var a int = 10
	var p *int = &a
	p2 := new(int)
	p2 = &a

	fmt.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

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

二级指针的使用

package main

import "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 main

import "fmt"

func main() {
   var p1 *int

   fmt.Println(p1)
}

Operation results:
< nil >

指针数组

package main

import "fmt"

const MAX int = 3

func main() {
   a := []int{10,100,200}
   var i int
   var 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 main

import "fmt"

func exchange(p1 *int, p2 *int)int{
	*p1 , *p2 = *p2 , *p1

	return 1
}

func main() {
	var a int = 10
	var b int =2022
	var p1 *int = &a
	var p2 *int = &b

	fmt.Println(*p1)
	fmt.Println(*p2)
	if exchange(p1, p2) == 1{
		fmt.Println("exchange ok!")
	}
	fmt.Println(*p1)
	fmt.Println(*p2)
}

Operation results:
10
2022
exchange ok!
2022
10

结构体

自定义类型和类型别名

package main

import "fmt"

//自定义类型
type UINT16 uint16

//类型别名
type UINT8 = uint8

func main() {
	var a UINT16
	var b UINT8
	fmt.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 main

import "fmt"

type Human struct {
	name, city string
	age        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 main

import "fmt"

func main() {
	//匿名结构体
	var HideStruct struct {
		name string
		age  uint8
	}

	HideStruct.name = "Finny"
	HideStruct.age = 21

	fmt.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 main

import "fmt"

type Human struct {
	a int8
	b int8
	c int8
	d 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 main

import (
	"fmt"
	"unsafe"
)

func main() {
	var UINT8T uint8
	var UINT16T uint16
	var UINT32T uint32
	var UINT64T uint64

	var INT8T int8
	var INT16T int16
	var INT32T int32
	var INT64T int64

	var STRING string
	var BOOL bool
	var INT int
	var UINT uint
	var UINTPTR uintptr

	var FLOAT32 float32
	var FLOAT64 float64

	var INTPOINT *int
	var UINTPOINT *uint
	var CHARPOINT *string
	var FLOAT64POINT *float64

	fmt.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 main

import (
	"fmt"
	"unsafe"
)

type Test struct {
	//四字节对齐
	a uint8   //1---3
	b float32 //4---0
	c uint8   //1---3
}

type Test2 struct {
	//四字节对齐
	a uint8
	c uint8   //1+1 --- 2
	b float32 //4   --- 0
}

type Test3 struct {
	//八字节对齐
	a uint8   //1  --- 7
	b float64 //8  --- 0
	c string  //16 --- 0
}

type Test4 struct {
	//八字节对齐
	a uint8   //1  --- 7
	b float64 //8  --- 0
	c float64 //8  --- 0
	d string  //16 --- 0
}

type Test5 struct {
	//八字节对齐
	b float64 //8  --- 0
	c float64 //8  --- 0
	d string  //16 --- 0
	a uint8   //1  --- 7
}

type Test6 struct {
	//八字节对齐
	a uint8  //1  --- 7
	b string //16 --- 0
}

type Test7 struct {
	//按照四字节对齐
	a Test  //12(4+4+4) --- 0
	b uint8 //1         --- 3
}

type Test8 struct {
	//按照四字节对齐
	a Test2 //8(4+4) --- 0
	b uint8 //1      --- 3
}

type Test9 struct {
	//按照八字节对齐
	a Test5 //40 --- 0
	b uint8 //1  --- 7
}

type Test10 struct {
	//数组只是多个而已同类型变量
	//最大是1字节,所以是按照1字节对齐
	b uint8
	a [10]uint8
}

type Test11 struct {
	//int类型是8字节,对齐是八字节对齐 10*8 + 1偏移7 = 88
	b uint8
	a [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 main

import "fmt"

type Person struct {
	name string
	age  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 main

import "fmt"

type Human struct{
	name string
	age  int
}

func PutStructData(data *Human){
	fmt.Println(data.name)
	fmt.Println(data.age)

	data.age = 98
}

func main() {
	var zhangsan Human

	zhangsan.name = "zhangsan"
	zhangsan.age  = 21

	PutStructData(&zhangsan)

	fmt.Println("name:",zhangsan.name,"age:",zhangsan.age)
}

Operation results:
zhangsan
21
name: zhangsan age: 98

方法和接收者

在这里插入图片描述

package main

import "fmt"

type Person struct {
	name string
	age  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 main

import "fmt"

type Person struct {
	name string
	age  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 main

import "fmt"

type Person struct {
	name string
	age  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 = NewAge

	return &p
}

func (p Person) ChangeAge3(NewAge uint8) Person {
	p.age = NewAge

	return 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 main

import "fmt"

type MEMsensingString string

func (Name MEMsensingString) PrintHello() {
	fmt.Println(Name, "say hello!")
}

func main() {
	var myName MEMsensingString = "Finny"
	myName.PrintHello()
}

Operation results:
Finny say hello!

匿名字段

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

package main

import "fmt"

type Person struct {
	string
	int
}

func main() {
	p1 := Person{
		"Finny",
		21,
	}

	fmt.Println(p1)
}

Operation results:
{Finny 21}

结构体嵌套

简单使用

package main

import "fmt"

type PersonAddress struct {
	Province string
	City     string
}

type Person struct {
	Name    string
	Age     int
	Address 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 main

import "fmt"

type PersonAddress struct {
	Province string
	City     string
}

type Person struct {
	Name string
	Age  int
	PersonAddress
}

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 main

import "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 main

import (
	"encoding/json"
	"fmt"
)

//结构体字段的可见性与JSON序列化

//如果一个Go语言包中定义的标识符是首字母大写的,那么就是对外可见的。
//如果结构体中的字段名首字母是大写的,那么该字符就是对外可见的。
//(例如把class结构体的Title换成小写的,那么其他包根本访问不到结构体这个字段)

//构造函数
func NewStudent(id int, name string) student {
	return student{
		Id:   id,
		Name: name,
	}
}

type student struct {
	Id   int
	Name string
}

type class struct {
	Title    string
	Students []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 class
	err = 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 main

import (
	"encoding/json"
	"fmt"
)

//结构体字段的可见性与JSON序列化

//如果一个Go语言包中定义的标识符是首字母大写的,那么就是对外可见的。
//如果结构体中的字段名首字母是大写的,那么该字符就是对外可见的。
//(例如把class结构体的Title换成小写的,那么其他包根本访问不到结构体这个字段)

//构造函数
func NewStudent(id int, name string) student {
	return student{
		Id:   id,
		Name: name,
	}
}

type student struct {
	Id   int
	Name 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”}]}


版权声明:本文为qq_44610809原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。