第一篇
一:GO基本概念
Golang⽬录结构划分
- 个人项目

- 公司项目

包的概念
1. 和python⼀样,把相同功能的代码放到⼀个⽬录,称之为包
2. 包可以被其他包引⽤
3. main包是⽤来⽣成可执⾏⽂件,每个程序只有⼀个main包
4. 包的主要⽤途是提⾼代码的可复⽤性
Go 基本命令介绍
a. go run 快速执⾏go⽂件,就像执⾏脚本⼀样
b. go build 编译程序,⽣成⼆进制可执⾏⽂件
c. go install 安装可执⾏⽂件到bin⽬录
d. go test 执⾏单元测试或压⼒测试
e. go env 显示go相关的环境变量
f. go fmt 格式化源代码
Go程序结构
a. go源码按package进⾏组织,并且package要放到⾮注释的第⼀⾏
b. ⼀个可执⾏程序只有⼀个main包和⼀个main函数
c. main函数是程序的执⾏⼊⼝
golang语⾔特性
1. 垃圾回收
a. 内存⾃动回收,再也不需要开发⼈员管理内存
b. 开发⼈员专注业务实现,降低了⼼智负担
c. 只需要new分配内存,不需要释放 golang语⾔特性
2. 天然并发
a. 从语⾔层⾯⽀持并发,⾮常简单。只需要go⼀下
b. goroutine,轻量级线程,创建成千上万个goroute成为可能
func calc() { //⼤量计算 }
func main() { go calc() }
3. channel
a. 管道,类似unix/linux中的pipe
b. 多个goroute之间通过channel进⾏通信
c. ⽀持任何类型
4. 多返回值
a. ⼀个函数返回多个值
5. 编译型语⾔
a. 性能只⽐C语⾔差10%
b. 开发效率和python、php差不多
二:GO 数据类型 变量 常量
go中标识符和关键字
1. 标识符是⽤来表示Go中的变量名或者函数名,以字⺟或_开头。后⾯跟着字⺟ 、_或数字
A. 88ab(错) B. _ab28 C. ab_28
2. 关键字是Go语⾔预先定义好的,有特殊含义的标识符。共25 个

3 变量
语法:var identifier type
var a int
var b string
var c bool
var d int = 8
var e string = “hello”
或者是
var (
a int //0
b string //“”
c bool //false
d int = 8 // 8
e string = “hello” //hello
)
4 常量
常量使⽤const 修饰,代表永远是只读的,不能修改。
语法:const identifier [type] = value,其中type可以省略。
举例: const b string = “hello world”
const b = “hello world”
const Pi = 3.1414926
const a = 9/3
优雅的写法:
const(
a = 1
b = 2
c = 3
)
更加专业的写法
const (
a = iota
b
c
)
const(
a = 1 << iota
b
c
)
三 go中的数据类型与操作符
1 布尔类型
2. 整数和浮点数类型
3. 字符串类型
1. 布尔类型
a. var b bool 和 var b bool = true 和 var b = false
b. 操作符 == 和 !=
c. 取反操作符: !b 数据类型和操作符
d. && 和 || 操作符
e. 格式化输出占位符: %t
2. 整数和浮点数类型

http://www.360doc.com/content/20/0218/08/33093582_892845322.shtml
a. int8、int16、int32、int64
b. uint8、uint16、uint32、uint64
c. int 和 uint,和操作系统平台相关
d. float32 和 float64浮点类型
e. 所有整数 初始化为0,所有浮点数初始化为0.0,布尔类型初始化为false
a. Go是强类型语⾔言,不不同类型相加以及赋值是不不允许的
b. 那怎么样才能实现,不不同类型相加呢?
c. 输出占位符:整数%d,%x⼗十六进制,%f浮点数 3. 字符串类型
a. var str string
b. var str string = “hello world”
c. 字符串串输出占位符 %s
d. 万能输出占位符: %v
%c
字符串的两种表示方式
a. 双引号, “”,可以包含控制字符
b. 反引号, ``,所有字符都是原样输出
字符串串常⽤用操作
a. ⻓长度:len(str)
b. 拼接:+,fmt.Sprintf
c. 分割:strings.Split
d. 包含: strings.Contains
e. 前缀或后缀判断:strings.HasPrefix, strings.HasSuffix
f. ⼦子串串出现的位置: strings.Index(), strings.LastIndex()
g. join操作: strings.Join(a[]string, sep string)
操作符
a. 逻辑操作符, == 、 != 、<、<=、>=
b. 算数操作符, +、-、*、/、%
go程序的基本结构
1 任何⼀一个代码⽂文件⾪隶属于⼀一个包
package main
import “fmt”
func main() {
fmt.Println(“hello, world”)
}
2. import 关键字,引⽤用其他包:
3. 开发可执⾏行行程序,package main, 并且有且只有⼀一个main⼊入⼝口函数
4. 包中函数调⽤用:
a. 同⼀一个包中函数,直接⽤用函数名调 b. 不不同包中函数,通过包名+点+
函数名进⾏行行调⽤用
5. 包访问控制规则:
a. ⼤大写意味着这个函数/变量量是可导出 b. ⼩小写意味着这个函数/变量量是私有的
包外部不不能访问
四: 字符串原理 时间 日期
1.字符串串原理理解析
1. 字符串串底层就是⼀一个byte数组,所以可以和[]byte类型互相转换
2. 字符串串之中的字符是不不能修改的,那怎么修改呢 转成字符切片
3. 字符串串是由byte字节组成,所以字符串串的⻓长度是byte字节的⻓长度
4. rune类型⽤用来表示utf8字符,⼀一个rune字符由1个或多个byte组成
2 时间和日期类型
1. time包
2. time.Time类型,⽤用来表示时间
3. 获取当前时间, now := time.Now()
4. time.Now().Day(),time.Now().Minute(),time.Now().Month(),time.Now().Year()
5. 格式化,fmt.Printf(“%02d/%02d%02d %02d:%02d:%02d”, now.Year()...)
6. 获取当前时间戳,time.Now().Unix()。
7. 时间戳转Time类型。
8. time.Duration⽤用来表示纳秒

五: 流程控制
if condition {
//do something
} else if condition {
//do something
} else {
//do something
}
或者:
if statement; condition {
}
package main
import (
"fmt"
)
func main() {
if num:=10; num % 2 == 0 { //checks if number is even
fmt.Println(num,"is even")
} else{
fmt.Println(num,"is odd")
}
}
循环
for initialisation; condition; post {
}
例子
package main
import (
"fmt"
)
func main() {
for i:=1; i<= 10;i ++ {
fmt.Printf(" %d",i)
}
}
break,终⽌止循环
package main
import (
"fmt"
)
func main() {
for i:=1; i<=10;i++ {
if i > 5 {
break
}
fmt.Printf("%d",i)
}
fmt.Printf("nline after for loop")
}
12345 "nline after for loop"
continue,终⽌止本次循环
package main
import ( "fmt" )
func main() {
for i :=1;i<=10;i++ {
if i%2 == 0 {
continue
}
fmt.Printf("%d", i)
}
}
13579
练习一: 可以在上面定义i初始值
package main
import (
"fmt"
)
function main() {
i :=0
for ;i<=10; {
fmt.Printf("%d", i)
i += 2
}
}
0 2 4 6 8 10
练习二
package main
import ('fmt')
func main() {
i:=0
for i <= 10 {
fmt.Printf('%d ', i)
i += 2
}
}
0 2 4 6 8 10
练习三 多个变量
package main
import (fmt')
func main() {
for no, i:=10,1; i<=10 && no <=19; i, no = i+1,no+1 {
fmt.Printf("%d * %d = %dn", no, i, no*i)
}
}
Switch 语句
package main
import (
"fmt"
)
func main() {
finger:=4
switch finger {
case 1:
fmt.Println("Thumb")
case 2:
fmt.Println("Index")
case 3:
fmt.Println("Middle")
case 4:
fmt.Println("Ring")
case 5:
fmt.Println("Pinky")
}
}
package main
import (
"fmt"
)
func main() {
switch finger
case 1:
fmt.Println("Thumb")
case 2:
fmt.Println("Index")
case 3:
fmt.Println("Middle")
case 4:
fmt.Println("Ring")
case 5:
fmt.Println("Pinky")
default: //default case
fmt.Println("incorrect finger number")
}
六: 函数介绍
1. 定义:有输⼊入、有输出,⽤用来执⾏行行⼀一个指定任务的代码块。
func functionname([parametername type]) [returntype] {
//function body
}
//其中参数列列表和返回值列列表是可选
例如:
func add(a int, b int) int {
Return a + b
}
⽆无参数和返回值的函数
func functionname() {
//function body
}
如果连续的一系列参数的类型是一样,前面的类型可以不写,例如:
func add(a, b int) int {
Return a + b
}
函数调用
func add(a, b int) int {
Return a + b
}
func main() {
sum := add(2, 3)
}
多返回值
func calc(a, b int) (int, int) {
sum := a + b
sub := a - b
return sum, sub
}
func main() {
sum, sub := add(2, 3)
}
对返回值进行命名
func calc(a, b int) (sum int, sub int) {
sum = a + b
sub = a - b
return
}
func main() {
sum, sub := add(2, 3)
}
_标识符 这样就会忽略sub的值
func calc(a, b int) (sum int, sub int) {
sum = a + b
sub = a - b
return
}
func main() {
sum, _ := add(2, 3)
}
可变参数
func calc_v1(b ...int) (sum int, sub int) {
sum:=0
for i :=0; i<len(b);i++ {
sum = sum =b[i]
}
return sum
}
func calc_v2(a int, b ...int) (sum int, sub int) {
return
}
func calc_v3(a int, b int, c ...int) (sum int, sub int) {
return
}
b其实是数组
defer语句
延迟执行语句,在函数返回之后会调用,用来释放一些资源
func calc_v1(b ...int) (sum int, sub int) {
defer fmt.Println(“defer”)
return
}
func testDefer () {
defer fmt.PrintIn("hello")
fmt.PrintIn("aaaa")
fmt.PrintIn("bbbb")
}
打印顺序是 aaaa bbbb hello
多个defer语句句,遵循栈的特性:先进后出
func calc_v1(b ...int) (sum int, sub int) {
defer fmt.Println(“defer1”)
defer fmt.Println(“defer2”)
return
}
打印顺序是 defer2 defer1
内置函数
1. close:主要⽤用来关闭channel
2. len:⽤用来求⻓长度,⽐比如string、array、slice、map、channel
3. new:⽤用来分配内存,主要⽤用来分配值类型,⽐比如int、struct。返回的是指针 4. make:⽤用来分配内存,主要⽤用来分配引⽤用类型,⽐比如chan、map、slice
5. append:⽤用来追加元素到数组、slice中
6. panic和recover:⽤用来做错误处理理
函数变量作用域和可见性
1. 全局变量量,在程序整个⽣生命周期有效。
var a int = 100
2. 局部变量量,分为两种:1)函数内定义,2)语句句块内定义。函数执行后就销毁
func add(a int, b int) int {
var sum int = 0
//sum是局部变量量
if a > 0 {
var c int = 100
//c是局部变量量,仅在if语句句块有效
}
}
for d:=100;d>0 {
}
for 循环结束 d也被销毁
3. 可⻅见性,包内任何变量量或函数都是能访问的。包外的话,⾸首字⺟母⼤大写是可导出的 能够被其他包访问或调⽤用。⼩小写表示是私有的,不不能被外部的包访问。
func add(a int, b int) int {
}
//add这个函数只能在包内部调⽤用,是私有的,不不能被外部的包调⽤用
var a int = 100 仅在当前包调用
var A int = 200 可以在其他包使用


4 匿名函数,即没有名字的函数
f1:= func (a,b int) int {
return a + b
}
func test () {
var i int =0
defer fmt.PrintIn("i=%dn",i)
i =100
fmt.PrintIn("i=%dn",i)
return
}
打印结果 i=100 i=0,当defer执行的时候 i就被赋值了,所以ki为0
改成
func test () {
var i int =0
defer func () {
fmt.PrintIn("i=%dn",i)
}()
i =100
fmt.PrintIn("i=%dn",i)
return
}
当前匿名函数执行的时候i就变为100了,所以两次输出的都是100
1. 函数也是⼀一种类型,因此可以定义⼀一个函数类型的变量量
4. 函数作为⼀一个参数
func calc (a,b int, op func(int,int)int) int {
return op(a,b)
}
sum:=calc(100,200,add)
func add (a,b int) int{
return a+b
}
闭包
闭包:⼀一个函数和与其相关的引⽤用环境组合⽽而成的实体
package main
import “fmt”
func main() {
var f = Adder()
fmt.Print(f(1),” - “)
fmt.Print(f(20),” - “)
fmt.Print(f(300))
}
func Adder() func(int) int {
var x int
return func(d int) int {
x += d
return x
}
}
闭包实例:
package main
func add(base int) func(int) int {
return func(i int) int {
base += i
return base
}
}
func main() {
tmp1 := add(10)
fmt.Println(tmp(1), tmp(2))
tmp2 := add(100)
fmt.Println(tmp2(1), tmp2(2))
}
例子2:
func calc(base int) (func(int) int, func(int) int) {
add := func(i int) int {
base += i
return base
}
sub := func(i int) int {
base -= i
return base
}
return add, sub
}
func main() {
f1, f2 := calc(10)
fmt.Println(f1(1), f2(2))
fmt.Println(f1(3), f2(4))
fmt.Println(f1(5), f2(6))
fmt.Println(f1(7), f2(8))
}
例子3:
闭包的副作用
package main
import (
"fmt"
"time"
)
func main() {
for i:=0; i<5; i++ {
go func(){
fmt.Println(i)
}()
}
time.Sleep(time.Second)
}
只会打印 5, 如果想要 0-4 应该将i传入匿名函数
------_---------------------------------------------------------------------------------
第二篇
数组
数组是同⼀一类型的元素集合。 var a [3]int //定义⼀一个数组
Go中数组下标从0开始,因此⻓长度为n的数组下标范围:[0,n-1] 整数数组中的元素默认初始化为0,字符串串数组中的元素默认初始化为”” 数组初始化:
var a [3]int
a[0] = 10
a[1] = 20
var a [3]int = [3]int{10, 20, 30}
//定义时数组初始化
//数组初始化
var a [3]int = [3]int{10, 20, 30}
//定义时数组初始化
a := [3]int{10, 20, 30}
//定义时数组初始化
a := [...]int{10, 20, 30}
//定义时数组初始化
a := [3]int{10}
//定义时数组初始化
a := [3]int{2:10}
//定义时数组初始化
数组⻓长度是类型的⼀一部分
var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
var b [5]int
b=a
//a、b是不不同类型的数组,不不能赋值
len 内置函数
var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
fmt.Printf(“len:%dn”, len(a))
//a、b是不不同类型的数组,不不能赋值
数组的遍历:
var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
for i := 0; i < len(a); i++ {
}
var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
for index, val := range a {
}
var a [3][2]int
a[0][0] = 10
a[0][1] = 20
a[1][0] = 30
a[1][1] = 30
a[2][0] = 30
a[2][1] = 30
for index, val := range a {
}
//a、b是不不同类型的数组,不不能赋值
数组的拷贝和传参
1. 数组是值类型
var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
b := a
//b拷⻉贝了了数组a中所有元素
b[0] = 1000
a: [10,20,30]
b: [1000,20,30]
2. 数组是值类型,函数传参也会拷⻉贝
package main
import ("fmt")
func main() {
var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
modify(a)
fmt.Println(a) [10,20,30]
}
func modify(b [3]int) {
b[0] = 1000
fmt.Println(b) [1000,20,30]
return
}
切片
1. 切⽚片是基于数组类型做的⼀一层封装。它⾮非常灵活,可以⾃自动扩容。
var a []int
//定义⼀一个int类型的空切⽚片
2. 切⽚片初始化, a[start:end]创建⼀一个包括从start到end-1的切⽚片。
package main
import (
"fmt"
)
func main() {
a:= [5]int{76, 77, 78, 79, 80}
var b []int = a[1: 4] //基于数组a
// 创建⼀一个切⽚片,包括元素a[1] a[2] a[3]
fmt.Println(b)
}
切片初始化方法2:
package main
import (
"fmt"
)
func main() {
c:=[]int{6, 7, 8} //创建⼀一个数组并返回⼀一个切⽚片
fmt.Println(c)
}
数组切⽚片的基本操作
a) arr[start:end]:包括start到end-1(包括end-1)之间的所有元素 b) arr[start:]:包括start到arr最后⼀一个元素(包括最后⼀一个元素)之间的所有元素 c) arr[:end]:包括0到end-1(包括end-1)之间的所有元素 d) arr[:]:包括整个数组的所有元素
切⽚片修改
package main
import (
"fmt"
)
func main() {
//创建⼀一个数组,其中[...]是编译器器确定数组的⻓长度,darr的⻓长度是9
darr:=[...]int{57, 89, 90, 82, 100, 78, 67, 69, 59}
//基于darr创建⼀一个切⽚片dslice,包括darr[2],darr[3],darr[4]三个元素
dslice:=darr[2:5]
fmt.Println("array before",darr)
for i:=range dslice {
//对于dslice中每个元素进⾏行行+1,其实修改是darr[2],darr[3],darr[4]
dslice[i]++
}
fmt.Println("array after",darr)
}
array before [57 89 90 82 100 78 67 69 59]
array after [57 89 91 83 101 78 67 69 59]
切片例子2:
package main
import (
"fmt"
)
func main() {
numa:=[3]int{78, 79 ,80}
//创建⼀一个切⽚片,包含整个数组的所有元素
nums1 := numa[:]
nums2 := numa[:]
fmt.Println("array before change 1",numa)
nums1[0]= 100
fmt.Println("array after modification to slice nums1", numa)
nums2[1]=101
fmt.Println("array after modification to slice nums2", numa)
}
切片是数组的引用,当两个切片源于一个数组,改变其中一个数组,另一个数组也响应的进行的修改。
使⽤用make创建切⽚片
package main
import (
"fmt"
)
func main() {
//[]中没有⻓长度 第一个5 是切片的长度,第二个 5是切片的容量
i:=make([]int, 5, 5)
fmt.Println(i)
}
切⽚片的⻓度和容量

package main
import (
"fmt"
)
func main() {
fruitarray :=[...]string{
"apple", "orange", "grape",
"mango", "water melon",
"pine apple", "chikoo"}
fruitslice:=fruitarray[1:3]//⻓长度是2,容量量is 6
fmt.Printf("length of slice %d capacity %d",len(fruitslice), cap(fruitslice))
}
9. 切⽚片的再切⽚片
package main
import (
"fmt"
)
func main() {
fruitarray:=[...]string{
"apple", "orange", "grape", "mango",
"water melon", "pine apple", "chikoo"}
fruitslice:=fruitarray[1:3]
//⻓长度是2, 容量量是6
fmt.Printf("length of slice %d capacity %dn",len(fruitslice), cap(fruitslice))
//再重新进⾏行行切⽚片,不不能⼤大于数组fruitarray的⻓长度,否则越界
fruitslice = fruitslice[:cap(fruitslice)]
fmt.Println("After re-slicing length is",len(fruitslice), "and capacity is",cap(fruitslice))
}
length of slice 2 capacity 6
After re-slicing length is 6 and capacity is 6
10.append操作
package main
import ( "fmt")
func main() {
cars:=[]string{"Ferrari", "Honda", "Ford"}
//⻓长度和容量量都等于3
fmt.Println("cars:", cars, "has old length",
len(cars), "and capacity", cap(cars))
cars=append(cars, "Toyota")
//容量量等于6
fmt.Println("cars:", cars, "has new length",len(cars), "and capacity", cap(cars))
}
cars: [Ferrari Honda Ford] has old length 3 and capacity 3
cars: [Ferrari Honda Ford Toyota] has new length 4 and capacity 6
11. 空切⽚片
package main
import (
"fmt"
)
func main() {
//定义names是⼀一个空切⽚片,⻓长度和容量量都等于0
//不不能对空切⽚片进⾏行行访问,否则panic
var names []string
if names == nil {
fmt.Println("slice is nil going to append")
names = append(names, "John", "Sebastian", "Vinay")
fmt.Println("names contents:",names)
}
}
12. append⼀一个切⽚片
package main
import (
"fmt"
)
func main() {
veggies:=[]string{"potatoes","tomatoes","brinjal"}
fruits :=[]string{"oranges","apples"}
//fruits后⾯面的3个点表示展开fruits切⽚片成⼀一个个元素
food:= append( veggies, fruits...)
fmt.Println("food:",food)
}
food: [potatoes tomatoes brinjal oranges apples]
13 切片传参
package main
import (
"fmt"
)
//在函数内部修改numbers切⽚片的值
func subtactOne(numbers []int) {
for i:= range numbers {
numbers[i]-=2
}
}
func main() {
nos:=[]int{8, 7, 6}
fmt.Println("slice before function call", nos)
subtactOne(nos)
//nos修改⽣生效了了,说明切⽚片是引⽤用类型
fmt.Println("slice after function call", nos)
}
切片拷贝
package main
import (
"fmt"
)
func main() {
veggies :=[]string{"potatoes","tomatoes","brinjal"}
fruits := []string{"oranges","apples"}
copy(veggies, fruits)
fmt.Println(veggies, fruits)
}
切片遍历
var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
B := a[:]
for index, val := range b {
}
//和数组遍历是⼀一样的
16. make为内建类型slice、map和channel分配内存。
//初始化⼀一个切⽚片
s := make([]int, 10, 30)
17. new⽤用于各种类型的内存分配,new返回是⼀一个指针。
----------------------------------------------------------------------------------------
第三篇
变量和内存地址
1. 每个变量量都有内存地址,可以说通过变量量来操作对应⼤大⼩小的内存 。
注意:通过&符号可以获取变量量的地址
package main
import (
"fmt"
)
func main() {
var a int32
a = 100
fmt.Printf("%dn", a)
fmt.Printf("%pn", &a)
}
100
0xc00001608c
2. 普通变量量存储的是对应类型的值,这些类型就叫值类型 例: var a int32
100
0xc00001608c
3. 指针类型的变量量存储的是⼀一个地址,所以⼜又叫指针类型或引⽤用类型
在定义时,前方加*就变为引用类型
var a *int32, a中存储的是一个地址,我们称为引用类型或者指针类型

4. 指针类型定义,var 变量量名 *类型
package main
import (
"fmt"
)
func main() {
b:=255
var a *int = &b
fmt.Printf("Type of a is %Tn", a)
fmt.Println("address of b is", a)
}
Type of a is *int
address of b is 0xc00009a008
5. 指针类型变量量的默认值为nil,也就是空地址
package main
import (
"fmt"
)
func main() {
a := 25
var b *int
if b == nil {
fmt.Println("b is", b)
b = &a
fmt.Println("b after initialization is", b)
}
}
b is <nil>
b after initialization is 0xc0000a2008
6. 如果操作指针变量量指向的地址⾥里里⾯面的值呢? 注意:通过* 符号可以获取指针变量量指向的变量量。
package main
import (
"fmt"
)
func main() {
b := 255
a := &b
fmt.Println("address of b is", a)
fmt.Println("value of b is",*a)
}
address of b is 0xc00009a008
value of b is 255
7. 通过指针修改变量量的值
package main
import (
"fmt"
)
func main() {
b := 255
a := &b
fmt.Println("address of b is", a)
fmt.Println("value of b is", *a)
*a++
fmt.Println("new value of b is",b)
}
address of b is 0xc00009a008
value of b is 255
new value of b is 256
8. 指针变量量传参
package main
import (
"fmt"
)
func change (val *int){
*val = 55
}
func main() {
a := 255
fmt.Println("value of a before function call is",a)
b := &a
change(b)
fmt.Println("value of a after function call is", a)
}
value of a before function call is 255
value of a after function call is 55
例子2:
package main
import (
"fmt"
)
func change (arr *[3]int){
(*arr)[0] = 90
}
func main() {
a:=[3]int{89, 90, 91}
change(&a)
fmt.Println(a)
}
[90 90 91]
10.切⽚片传参 注意:切⽚片是引⽤用类型!!
package main
import (
"fmt"
)
func change (arr []int){
arr[0] = 90
}
func main() {
a:=[3]int{89, 90, 91}
change(a[:])
fmt.Println(a)
}
[90 90 91]
11. make⽤用来分配引⽤用类型的内存,⽐比如 map、slice以及channel ,
new⽤用来分配除引⽤用类型的所有其他类型的内存,⽐比如 int、数组等
值拷⻉和引⽤拷⻉贝
值拷⻉
package main
import (
"fmt"
)
func main() {
// 值拷贝
var a int = 100
b := a
// 引用拷贝8
}
引⽤拷⻉
package main
import (
"fmt"
)
func main() {
var a int = 100
var b *int = &a
var c *int = b
*c = 200
}

Map 类型
1.map类型是⼀一个key-value的数据结构。 注意:map必须初始化才能使⽤用,否则panic
//var a map[key的类型]value类型
var a map[string]int
var b map[int]string
var c map[float32]string
2. map类型的变量量默认初始化为nil,需要使⽤用make分配map内存
package main
import (
"fmt"
)
func main() {
var a map[string]int
if a == nil {
fmt.Println("map is nil. Going to make one.")
A = make(map[string]int)
}
}
3. map插⼊入操作
package main
import (
"fmt"
)
func main() {
a := make(map[string]int)
a["steve"] = 12000
a["jamie"] = 15000
a["mike"] = 9000
fmt.Println("a map contents:", a)
}
map 声明方式二, 通过key访问map中的元素
package main
import (
"fmt"
)
func main() {
a := map[string]int {
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("a map contents:", a)
}
6. 如何判断map指定的key是否存在? value, ok := map[key]
package main
import (
"fmt"
)
func main() {
a := map[string]int {
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
b := "joe"
value, ok := a[b]
if ok == true {
fmt.Println("Salary of", b, "is", value)
} else {
fmt.Println(b,"not found")
}
fmt.Println("a map contents:", a)
}
map遍历操作
package main
import (
"fmt"
)
func main() {
a := map[string]int {
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
for key, value:= range a {
fmt.Printf("personSalary[%s] = %dn", key, value)
}
}
8. map删除元素
package main
import (
"fmt"
)
func main() {
a := map[string]int {
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
delete(a, "steve")
fmt.Println("map after deletion", a)
}
9. map的⻓长度 len(a)
package main
import (
"fmt"
)
func main() {
a := map[string]int {
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
fmt.Println("length is", len(a))
}
10.map是引⽤用类型
package main
import (
"fmt"
)
func main() {
a := map[string]int {
"steve": 12000,
"jamie": 15000,
}
a["mike"] = 9000
b := a
b["mike"] = 18000
fmt.Println("a map changed", a)
}
a map changed map[jamie:15000 mike:18000 steve:12000]
12. 11.默认情况下,map并不不是按照key有序进⾏行行遍历的 ,map按照key进⾏行行排序,遍历
package main
import (
"fmt"
"sort"
)
func main() {
var a map[string]int = make(map[string]int, 10)
for i := 0; i < 10; i++ {
key := fmt.Sprintf("key%d", i)
a[key] = i
}
var keys []string
for key, _ := range a {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
fmt.Printf("key:%s=%dn", key, a[key])
}
}
key:key0=0
key:key1=1
key:key2=2
key:key3=3
key:key4=4
key:key5=5
key:key6=6
key:key7=7
key:key8=8
key:key9=9
13. map类型的切⽚片
package main
import (
"fmt"
)
func main() {
var mapSlice []map[string]int
mapSlice = make([]map[string]int, 5)
fmt.Println("before map init")
for index, value := range mapSlice {
fmt.Printf("index:%d value:%vn", index, value)
}
mapSlice[0] = make(map[string]int, 10)
mapSlice[0]["a"] = 1000
mapSlice[0]["b"] = 2000
mapSlice[0]["c"] = 3000
mapSlice[0]["d"] = 4000
mapSlice[0]["e"] = 5000
fmt.Println("after map init")
for index, value := range mapSlice {
fmt.Printf("index:%d value:%vn", index, value)
}
}
before map init
index:0 value:map[]
index:1 value:map[]
index:2 value:map[]
index:3 value:map[]
index:4 value:map[]
after map init
index:0 value:map[a:1000 b:2000 c:3000 d:4000 e:5000]
index:1 value:map[]
index:2 value:map[]
index:3 value:map[]
index:4 value:map[]
------------------------------------------------------
第四篇
面向对象编程
1 .struct声明和定义
Go中⾯面向对象是通过struct来实现的, struct是⽤用户⾃自定义的类型 , 注意:type是⽤用来定义⼀一种类型 .
type User struct {
Username string
Sex string
Age int
AvatarUrl string
}
初始化方法一:
注意:使⽤用变量量名+ ‘.’ + 字段名访问结构体中的字段
var user User
user.Age = 18
user.Username = “user01”
user.Sex = “男”
user.AvatarUrl = “http://my.com/xxx.jpg"
初始化方法二:
var user User = User {
“Username” : “user01”,
“Age”: 18,
“Sex”: “男”,
“AvatarUrl”: “http://my.com/xxx.jpg”,
}
更简单的写法:
user := User {
“Username” : “user01”,
“Age”: 18,
“Sex”: “男”,
“AvatarUrl”: “http://my.com/xxx.jpg”,
}
2 struct初始化的默认值
var user User
fmt.Printf(“%#vn”, user)
3 结构体类型的指针 注意:&User{}和new(User) 本质上是⼀一样的,都是返回⼀一个 结构体的地址
var user *User = &User{}
fmt.Printf(“%p %#vn”, user)
直接初始化:
var user *User = &User {
Username : “user01”,
Age: 18,
Sex: “男”,
AvatarUrl: “http://my.com/xxx.jpg”,
}
或者使用new
var user User = new(User)
user.Age = 18
user.Username = “user01”
user.Sex = “男”
user.AvatarUrl = “http://my.com/xxx.jpg"
6. 结构体的内存布局: 占⽤用⼀一段连续的内存空间

7. 结构体没有构造函数, 必要时需要⾃自⼰己实现 匿名字段和嵌套
8. 匿名字段: 即没有名字的字段 注意:匿匿名字段默认采⽤用类型名作为 字段名
type User struct {
Username string
Sex string
Age int
AvatarUrl string
}
type User struct {
Username string
Sex string
Age int
AvatarUrl string
int
string
}
9. 结构体嵌套
type Address struct {
City string
Province string
}
type User struct {
Username string
Sex string
Age int
AvatarUrl string
address *Address
}
func main () {
user := &User {
Username: 'user01',
Sex: 'man',
address: &Address {
Province: 'beijing',
City: 'beijing',
},
}
}
10.匿名结构体
type Address struct {
City string
Province string
}
type User struct {
Username string
Sex string
Age int
AvatarUrl string
*Address
}
func main () {
var user User
user.Username = 'user01'
user.Sex = 'man'
// 第一种方式
user.Address = &Address{
Provice: 'bj',
City: 'bj'
}
// 第二种方式
user.Province = 'bj01'
user.City = 'bj01'
}
第二种方式 先在结构体里面找Provice以及City,如果结构体里面没有,则在匿名结构体里面找,
12. 匿名结构体的冲突解决
package main
import (
"fmt"
)
type Address struct {
City string
Province string
CreateTime string
}
type Email struct {
Account string
CreateTime string
}
type User struct {
Username string
Sex string
Age int
AvatarUrl string
*Address
*Email
CreateTime string
}
func main (){
var user User
user.Username = "user01"
user.Sex = "man"
user.City = "bj"
user.Address = new(Address)
user.Email = new(Email)
user.Address.City ="bj01"
user.Address.CreateTime ="001"
user.Email.CreateTime = "002"
fmt.Println( user.Email.CreateTime, user.Address.CreateTime)
}
使用结构体的字段,如果结构体字段有值,则使用节后体,如果没有则使用匿名结构体的字段
如果两个匿名结构体重都有相同的字段,当访问这个相同字段时候,要指定访问那个匿名结构体的字段
13.字段可⻅见性,⼤大写表示可公开访问,⼩小写表示私有

14. tag是结构体的元信息,可以在运⾏行行的时候通过反射的机制读取出来 字段类型后⾯面,以反引号括起来的 key-value结构的字符串串,多个tag 以逗号隔开。
type User struct {
Username string `json:”username”,db:”user_name”`
Sex string `json:”sex”`
Age int `json:”age”`
avatarUrl string
CreateTime string
}

方法的定义:
1. 和其他语⾔言不不⼀一样,Go的⽅方法采⽤用另外⼀一种⽅方式实现。
2. Go的⽅方法是在函数前⾯面加上⼀一个接受者,这样编译器器就知道这个⽅方法属于哪个 类型了了

3. 可以为当前包内定义的任何类型增加⽅方法

4. 函数不不属于任何类型,⽅方法属于特定的类型 5. 指针类型作为接受者

6. 指针类型和值类型作为接受者的区别
值类型:
type People struct {
Name string
Country string
}
func (p People) Print() {
fmt.Println("name=%s country=%sn",p.Name, p.Country)
}
func (p People) Set(name string, country string) {
p.Name = name
p.Country = country
}
func main() {
var p1 People = People {
Name: "people01",
Country: "china",
}
p1.Print() // people01 china
p1.Set("people02","english")
p1.Print() // 不变 p是实例的拷贝 people01 china
}
指针类型:
type People struct {
Name string
Country string
}
func (p People) Print() {
fmt.Println("name=%s country=%sn",p.Name, p.Country)
}
// 值类型
func (p People) Set(name string, country string) {
p.Name = name
p.Country = country
}
// 引用类型
func (p *People) SetV2(name string, country string) {
p.Name = name
p.Country = country
}
func main() {
var p1 People = People {
Name: "people01",
Country: "china",
}
p1.Print() // people01 china
p1.Set("people02","english")
p1.Print() // 不变 p是实例的拷贝 people01 china
(&p1).SetV2("people02","english")或 语法糖: p1.SetV2("people02","english")
p1.Print() // 变了 people02 english
}
7. 什什么时候⽤用值类型/指针类型作为接受者? A. 需要修改接受者中的值的时候 B. 接受者是⼤大对象的时候,副本拷⻉贝代价⽐比较⼤大 C. ⼀一般来说,通常使⽤用指针类型作为接受者 8. 匿匿名结构体与继承
通过组合 和 匿名字段实现的继承,
方法的冲突解决和匿名字段的冲突解决一致,都是通过指定对应的实例的方法解决
type Anaimal struct {
Name string
Sex string
}
func (a *Anaimal) Talk() {
fmt.Println(a.Name)
}
type Dog struct {
Feet string
*Anaimal
}
func (d *Dog) Eat () {
fmt.Println("dog is eat")
}
func main () {
var d *Dog = &Dog{
Feet: "Four Feet",
Anaimal: &Anaimal {
Name: "dog",
Sex: "xiong",
},
}
d.Name = "dog"
d.Sex = "xiong"
d.Eat()
d.Talk()
}
9. 多重继承与冲突解决
type Mother struct {
Name string
}
type Father struct {
Name string
}
type People struct {
Sex string
Age int
*Mother
*Father
}
10. 结构体与json序列列化
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Id string
Name string
Sex string
}
type Class struct {
Name string
Count int
Students []*Student
}
var rawJson = `
{"Name":"101","Count":0,"Students":[{"Id":"0","Name":"stu0","Sex":"man"},{"Id":"1","Name":"stu1","Sex":"man"},{"Id":"2","Name":"stu2","Sex":"man"},{"Id":"3","Name":"stu3","Sex":"man"},{"Id":"4","Name":"stu4","Sex":"man"},{"Id":"5","Name":"stu5","Sex":"man"},{"Id":"6","Name":"stu6","Sex":"man"},{"Id":"7","Name":"stu7","Sex":"man"},{"Id":"8","Name":"stu8","Sex":"man"},{"Id":"9","Name":"stu9","Sex":"man"}]}
`
func main() {
c := &Class{
Name: "101",
Count: 0,
}
for i := 0; i < 10; i++ {
stu := &Student{
Name: fmt.Sprintf("stu%d", i),
Sex: "man",
Id: fmt.Sprintf("%d", i),
}
c.Students = append(c.Students, stu)
}
data, err := json.Marshal(c)
if err != nil {
fmt.Println("json marshal failed")
return
}
fmt.Printf("json:%sn", string(data))
//json反序列化
fmt.Println("unmarshal result is nn")
var c1 *Class = &Class{}
err = json.Unmarshal([]byte(rawJson), c1)
if err != nil {
fmt.Println("unmarhsal failed")
return
}
fmt.Printf("c1:%#vn", c1)
for _, v := range c1.Students {
fmt.Printf("stu:%#vn", v)
}
}
第五篇
接口
接口介绍和定义
1. 接口定义了一个对象的行为规范 A. 只定义规范,不实现 B. 具体的对象需要实现规范的细节
2. Go中接口定义 A. type 接口名字 interface B. 接口里面是一组方法签名的集合
3. Go中接口的实现
A. 一个对象只要包含接口中的方法,那么就实现了这个接口
B. 接口类型的变量可以保存具体类型的实例
type Animal interface {
Talk()
Eat() int
Run()
}
5. 接口类型变量 A. var a Animal B. 那么a能够存储所有实现Animal接口的对象实例
package main
import (
"fmt"
)
type Animal interface {
Talk()
Eat()
Name() string
}
type Dog struct {
}
func (d Dog) Talk() {
fmt.Println("汪汪汪")
}
func (d Dog) Eat() {
fmt.Println("我在吃骨头")
}
func (d Dog) Name() string {
fmt.Println("我的名字叫旺财")
return "旺财"
}
type Pig struct {
}
func (d Pig) Talk() {
fmt.Println("坑坑坑")
}
func (d Pig) Eat() {
fmt.Println("我在吃草")
}
func (d Pig) Name() string {
fmt.Println("我的名字叫猪八戒")
return "猪八戒"
}
func testInterface1() {
var d Dog
var a Animal
a = &d
a.Eat()
a.Talk()
a.Name()
var pig Pig
a = &pig
a.Eat()
a.Talk()
a.Name()
fmt.Printf("%T %vn", a, a)
}
func main() {
testInterface1()
//testInterface2()
}
6. 空接口 A. 空接口没有定义任何方法 B. 所以任何类型都实现了空接口 ,空接口可以存储任何类型,就是一个容器
interface { }
package main
import (
"fmt"
)
func describe(a interface{}) {
fmt.Printf("%T %vn", a, a)
}
func testInterface1() {
var a interface{}
var b int = 100
a = b
fmt.Printf("%T %vn", a, a)
var c string = "hello"
a = c
fmt.Printf("%T %vn", a, a)
var d map[string]int = make(map[string]int, 100)
d["abc"] = 1000
d["eke"] = 30
a = d
fmt.Printf("%T %vn", a, a)
}
type Student struct {
Name string
Sex int
}
func main() {
a := 65
describe(a)
str := "hello"
describe(str)
var stu Student = Student{
Name: "user01",
Sex: 1,
}
describe(stu)
}
8. 类型断言 A. 如何获取接口类型里面存储的具体的值呢?
使用a.(类型的值),ok用来判断是否正确吗,如果没有ok,程序会挂掉
A. 如何解决,引入 ok判断机制! v, ok := i.(T)
package main
import (
"fmt"
)
func test(a interface{}) {
// s := a.(int)
s, ok := a.(int)
if ok {
fmt.Println(s)
return
}
str, ok := a.(string)
if ok {
fmt.Println(str)
return
}
f, ok := a.(float32)
if ok {
fmt.Println(f)
return
}
fmt.Println("can not define the type of a")
}
func testInterface1() {
var a int = 100
test(a)
var b string = "hello"
test(b)
}
B. type switch。 转换为2次,解决办法是 v:=a.(type)
func testSwitch(a interface{}) {
switch a.(type) {
case string:
fmt.Printf("a is string, value:%vn", a.(string))
case int:
fmt.Printf("a is int, value:%vn", a.(int))
case int32:
fmt.Printf("a is int, value:%vn", a.(int))
default:
fmt.Println("not support typen")
}
}
func testSwitch2(a interface{}) {
switch v := a.(type) {
case string:
fmt.Printf("a is string, value:%vn", v)
case int:
fmt.Printf("a is int, value:%vn", v)
case int32:
fmt.Printf("a is int, value:%vn", v)
default:
fmt.Println("not support typen")
}
}
func testInterface2() {
var a int = 100
testSwitch(a)
var b string = "hello"
testSwitch(b)
}
func testInterface3() {
var a int = 100
testSwitch2(a)
var b string = "hello"
testSwitch2(b)
}
func main() {
//testInterface1()
//testInterface2()
13. 指针接收
package main
import (
"fmt"
)
type Animal interface {
Talk()
Eat()
Name() string
}
type Dog struct {
}
func (d *Dog) Talk() {
fmt.Println("汪汪汪")
}
func (d *Dog) Eat() {
fmt.Println("我在吃骨头")
}
func (d *Dog) Name() string {
fmt.Println("我的名字叫旺财")
return "旺财"
}
func main() {
var a Animal
var d Dog
//a存的是一个值类型的Dog,那么调用a.Eat(),&Dog->Eat()
//如果一个变量存储在接口类型的变量中之后,那么不能获取这个变量的地址
a = d
a.Eat()
// 值类型实现了一个接口,指针类型可以存储进去
// 指针类型实现了一个接口,值类型存储存储不进去
fmt.Printf("%T %vn", a, a)
var d1 *Dog = &Dog{}
a = d1
//*(&Dog).Eat() 使用的是指针类型那么定义时候使用
a.Eat()
fmt.Printf("*Dog %T %vn", a, a)
}
同一类型实现多接口,Animal 和PuruDongWu 都可以实现d
package main
import (
"fmt"
)
type Animal interface {
Talk()
Eat()
Name() string
}
type PuruDongWu interface {
TaiSheng()
}
type Dog struct {
}
func (d Dog) Talk() {
fmt.Println("汪汪汪")
}
func (d Dog) Eat() {
fmt.Println("我在吃骨头")
}
func (d Dog) Name() string {
fmt.Println("我的名字叫旺财")
return "旺财"
}
func (d Dog) TaiSheng() {
fmt.Println("狗是胎生的")
}
func main() {
var d Dog
var a Animal
fmt.Println("%v %T %p", a, a, a)
if a == nil {
//a.Eat()
fmt.Println("a is nil")
}
a = d
a.Eat()
var b PuruDongWu
b = d
b.TaiSheng()
}
15. 接口嵌套,和结构体嵌套类似
package main
import "fmt"
type Animal interface {
Eat()
Talk()
Name() string
}
type Describle interface {
Describle() string
}
type AdvanceAnimal interface {
Animal
Describle
}
type Dog struct {
}
func (d Dog) Eat() {
fmt.Println("dog is eating")
}
func (d Dog) Talk() {
fmt.Println("dog is talking")
}
func (d Dog) Name() string {
fmt.Println("my name is dog")
return "dog"
}
func (d Dog) Describle() string {
fmt.Println("dog is a dog")
return "dog is a dog"
}
func main() {
var d Dog
var a AdvanceAnimal
a = d
a.Describle()
a.Eat()
a.Talk()
a.Name()
}
反射详解 变量介绍
1. 变量的内在机制 A. 类型信息,这部分是元信息,是预先定义好的
B. 值类型,这部分是程序运行过程中,动态改变的
var arr [10]int
arr[0] = 10
arr[1] = 20
arr[2] = 30
arr[3] = 40
arr[4] = 50
type Animal struct {
Name string
age int
}
var a Animal
2. 反射与空接口 A. 空接口可以存储任何类型的变量 B. 那么给你一个空接口,怎么里面存储的是什么东西? C. 在运行时动态的获取一个变量的类型信息和值信息,就叫反射
3. 怎么分析? A. 内置包 reflect B. 获取类型信息: reflect.TypeOf
C. 获取值信息: reflect.ValueOf
Type.Kind(),获取变量的类型

Ptr 指针的值
package main
import (
"fmt"
"reflect"
)
func reflect_example(a interface{}) {
t := reflect.TypeOf(a)
fmt.Printf("type of a is:%vn", t)
k := t.Kind()
switch k {
case reflect.Int64:
fmt.Printf("a is int64n")
case reflect.String:
fmt.Printf("a is stringn")
}
}
func reflect_value(a interface{}) {
v := reflect.ValueOf(a)
// t := reflect.TypeOf(a)
k := v.Kind()
//fmt.Printf("a store value is :%dn", v.Int())
switch k {
case reflect.Int64:
fmt.Printf("a is int64, store value is:%dn", v.Int())
case reflect.Float64:
fmt.Printf("a is float64, store value is:%fn", v.Float())
}
}
func reflect_set_value(a interface{}) {
v := reflect.ValueOf(a)
// t := reflect.TypeOf(a)
k := v.Kind()
//fmt.Printf("a store value is :%dn", v.Int())
switch k {
case reflect.Int64:
v.SetInt(100)
fmt.Printf("a is int64, store value is:%dn", v.Int())
case reflect.Float64:
v.SetFloat(6.8)
fmt.Printf("a is float64, store value is:%fn", v.Float())
case reflect.Ptr:
fmt.Printf("set a to 6.8n")
v.Elem().SetFloat(6.8)
default:
fmt.Printf("default switchn")
}
}
func main() {
var x float64 = 3.4
reflect_example(x)
reflect_value(x)
reflect_set_value(&x)
fmt.Printf("x value is %vn", x)
/*
var b *int = new(int)
*b = 100
*/
}
通过反射设置变量的值




11. 获取结构体类型相关信息
package main
import (
"fmt"
"reflect"
)
type S struct {
A int
B string
}
func main() {
s := S{23, "skidoo"}
v := reflect.ValueOf(s)
t := v.Type()
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
fmt.Printf("%d: %s %s = %vn", i,
t.Field(i).Name, f.Type(), f.Interface())
} }
0: A int = 23
1: B string = skidoo
13. 设置结构体相关字段的值
v.Elem() 获取的是结构体 v.Elem().Field(0) 获取的就是第一个值 A,
v.FieldByName("SetA") 也可以获取对应的值
package main import (
"fmt"
"reflect"
)
type S struct {
A int
B string
}
func main() {
s := S{23, "skidoo"}
v := reflect.ValueOf(&s)
t := v.Type()
v.Elem().Field(0).SetInt(100)
// 等价 v.Elem().FieldByName("A").SetInt(100)
for i := 0; i < v.Elem().NumField(); i++ {
f := v.Elem().Field(i)
fmt.Printf("%d: %s %s = %vn", i,t.Elem().Field(i).Name, f.Type(), f.Interface())
} }
14. 获取结构体的方法信息
package main
import (
"fmt"
"reflect"
)
type S struct {
A int
B string
}
func (s *S) Test() {
fmt.Println("this is a test")
}
func main() {
s := S{23, "skidoo"}
v := reflect.ValueOf(&s)
t := v.Type()
v.Elem().Field(0).SetInt(100)
fmt.Println("method num:", v.NumMethod())
for i := 0; i < v.NumMethod(); i++ {
// t获取结构体方法信息,如果调用就是v.method
f := t.Method(i)
fmt.Printf("%d method, name:%v, type:%vn", i, f.Name, f.Type)
}
}
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Sex int
Age int
Score float32
//xxx int
}
func (s *Student) SetName(name string) {
s.Name = name
}
func (s *Student) Print() {
fmt.Printf("通过反射进行调用:%#vn", s)
}
func main() {
var s Student
s.SetName("xxx")
//SetName(&s, "xxx")
v := reflect.ValueOf(&s)
t := v.Type()
//t := reflect.TypeOf(s)
fmt.Printf("struct student have %d methodsn", t.NumMethod())
for i := 0; i < t.NumMethod(); i++ {
method := t.Method(i)
fmt.Printf("struct %d method, name:%s type:%vn", i, method.Name, method.Type)
}
//通过reflect.Value获取对应的方法并调用
m1 := v.MethodByName("Print")
var args []reflect.Value
m1.Call(args)
m2 := v.MethodByName("SetName")
var args2 []reflect.Value
name := "stu01"
nameVal := reflect.ValueOf(name)
args2 = append(args2, nameVal)
m2.Call(args2)
m1.Call(args)
}
16. 获取结构体中tag信息
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string `json:"name" db:"name"`
Sex int
Age int
Score float32
//xxx int
}
func (s *Student) SetName(name string) {
s.Name = name
}
func (s *Student) Print() {
fmt.Printf("通过反射进行调用:%#vn", s)
}
func main() {
var s Student
s.SetName("xxx")
//SetName(&s, "xxx")
v := reflect.ValueOf(&s)
t := v.Type()
//t := reflect.TypeOf(s)
field0 := t.Elem().Field(0)
fmt.Printf("tag json=%sn", field0.Tag.Get("json"))
fmt.Printf("tag db=%sn", field0.Tag.Get("db"))
//json.UnMa
var s string
}
反射总结以及应用场景 17. 反射总结 A.在运行时动态的获取一个变量的类型信息和值信息 18. 应用场景 A. 序列化和反序列化,比如json, protobuf等各种数据协议 B. 各种数据库的ORM, 比如gorm,sqlx等数据库中间件 C. 配置文件解析相关的库, 比如yaml、ini等

第六篇