目录

  • 函数
    • 匿名函数
    • 函数闭包的简单使用以及获取键盘输入
      • 闭包加上函数多返回值
      • 为什么闭包不会被垃圾回收
    • 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 = 2
defer calc外面那层,里面那层不受外面defer影响 CALC("A", 1, 2)
输出 A 1 2 3X = 10
calc("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 definition
const (Arraysize = 5)//Func
func 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
}//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 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

  1. 二者都是用来做内存分配的。
  2. make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
  3. 而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 =2022var 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
2022
exchange ok!
2022
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 stringCity     string
}type Person struct {Name    stringAge     intAddress 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 stringCity     string
}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”}]}

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

  1. C语言动态二维数组,结构体动态申请内存空间

    在C语言中,申请一般数组时,数组大小必须是常量.即使是赋了特定值的变量,也不行,比如:int n=3;int a[3];这样的做法是错误的,我们需要动态申请数组内存,如下申请一个动态整数(int)二维 ...

  2. NEFU 大一寒假培训【一】二维数组、结构体

    主题:二维数组.结构体 A 二维矩阵对角线和 Description 计算m×m的方阵的对角线所有元素的和 Input 输入数据有多组,每组第1行为m (2<=m<=10),接下来有m行m ...

  3. acm新手小白必看系列之(1)——二维数组及结构体精讲附带例题

    *acm新手小白必看系列之(1)--二维数组及结构体 ** c++准备工作** (可能小白像我一样也是学习的c语言) 万能头文件,放在第一行 #include<bits/stdc++.h> ...

  4. 【HDU】1251统计难题 (字典树:二维数组,结构体数组,链表,map)

    使用二维数组或者结构体数组都可以,但是在计数的时候有一点点小区别 一.结构体数组 #include <cstdio> #include <cstring> #include & ...

  5. Go语言基础第06天(结构体和指针)

    1.练习 package mainimport "fmt"func main0101() {//ssdjfhjkahsjdsdjvar arr [20]bytefor i := 0 ...

  6. C语言一维数组、二维数组、结构体的初始化

    C语言数组的初始化表示方法 一.C语言一维数组初始化: (1)在定义数组时对数组元素赋以初值.如: static int a[10]={0,1,2,3,4,5,6,7,8,9}; 经过上面的定义和初始 ...

  7. 函数指针数组指针+结构体数组

    /***************** 结构体数组 计算一个班学生四门课程平均分 ************************/#include <stdio.h> #include & ...

  8. Go语言基础(七):结构体

    文章目录 一.类型别名和自定义类型 1.自定义类型 2.类型别名 3.类型定义和类型别名的区别 二.结构体 1.结构体定义 2.结构体实例化 (1)基本实例化 (2)匿名结构体 (3)创建指针类型结构 ...

  9. C语言基础12——自定义类型。结构体、位段、枚举、联合体。通讯录

    目录 结构体 结构体的声明 结构体自引用 结构体变量的定义.初始化以及访问 结构体内存对齐 结构体传参 位段 枚举 枚举是什么? 枚举的声明 枚举的优点 枚举类型的大小 联合体 联合体类型的定义 联合 ...

  10. c语言中结构体变量怎么初始化为0,C语言高级编程:数组和结构体初始化为0的方法...

    测试平台:64位 X86 Ubuntu 1. 代码: #include #include struct st_stu { char *name; int age; int class; }; void ...

最新文章

  1. C语言 链表的创建--打印--逆置--新增--删除--排序--释放
  2. 李笑来 css,李笑来都想投资千万美金的ACSS通证即将强势登陆奇点交易所
  3. codevs 1557 热浪
  4. php连接中文数据库乱码问题,如何解决php的数据库中文乱码问题
  5. 经典的K-means聚类算法
  6. java编程思想泛型对混入的详细探讨
  7. linux远程连接 ipv6,Ansible 配置 IPv6 连接
  8. mysql 8 nosql_MySQL8.0-NoSQL和SQL的对比及MySQL的优势
  9. 力扣题目——637. 二叉树的层平均值
  10. php的变量引用与销毁机制
  11. 龙芯mips64 Javajdk下载
  12. web中使用阿里巴巴矢量库作为图标管理
  13. Java对接企业微信消息推送
  14. 如何在word中的图片上画圈标注_word图片上画圈 如何在word图片中进行画圈标示...
  15. python下载迅雷资源助手_NAS下载丨比第三方好用100倍!威联通Download Station支持PT下载教程...
  16. 《软件设计师》复习总结
  17. python简单制作节日贺卡
  18. ubuntu软件包降级命令_如何在Ubuntu上降级软件包
  19. 一杯苦咖啡 | 公司来了个漂亮女实习程序员
  20. C++动态开辟数组空间

热门文章

  1. Android基础之intent-filter、action、category标签使用
  2. 【超详细】2021最新版Redis安装教程(基于Redis 6.2.x版本)
  3. 纯 html 实现一个简单的个人简历
  4. 最近读书得感悟 (二) 为自己工作的必要性
  5. 裸函数 __declspec(naked)
  6. PowerPC Figure – PPC入门与优化
  7. Pyserial 实例教程详细用法
  8. vivado对mcs文件固化
  9. Elasticsearch快速检索之倒排索引算法
  10. HCIP考试考哪三门你知道么?