Go 基础

参考链接

[Golang教程 - 菜鸟教程](Go 基础教程 - Golang教程 - 菜鸟教程l)

Go 系列教程 —— 34. 反射 - Go语言中文网 - Golang中文社区

Hello world

package main                   // 包名称
import "fmt"

func main() {                  // main: 函数名
    fmt.Println("Hello world") // Hello world
    fmt.Println("1+1=",1+1)    // 1+1= 2

    var name = "golong"        // 变量名
    fmt.Println(name)
}

Go 标识符规则

  • 标识符的名称必须以字母或下划线(_)开头。并且名称中可能包含字母“ a-z”或“ A-Z”或数字0-9,以及字符“

  • 标识符的名称不能以数字开头。

  • 标识符的名称区分大小写。

  • 关键字不能用作标识符名称。

  • 标识符名称的长度没有限制,但是建议仅使用4到15个字母的最佳长度。

例如:

Go 内置常量、类型、函数

Go 关键字(25个)

GO 数据类型

  • 基本类型:数字(int),字符串(string)和布尔值(bool)属于此类别。

  • 聚合类型:数组和结构属于此类别。

  • 引用类型:指针,切片,map集合,函数和Channel属于此类别。

  • 接口类型

GO println和printf区别

  • Println :可以打印出字符串,和变量

  • Printf : 只可以打印出格式化的字符串,可以输出字符串类型的变量

Go 变量

变量命名规则: 变量名称必须以字母或下划线(_)开头。并且名称中可能包含字母“ a-z”或“ A-Z”或数字0-9,以及字符" _"

  • 创建变量的方法

    • 使用var关键字

    • 使用短变量声明运算符(:=)---> 只能在局部使用,不能全局

  • 声明变量

    }

    变量类型: int%!(EXTRA int=0, string=, float64=0, int=111, int=222, int=333, int=2, string=abc, float64=11.44, int=2, string=3, float64=11.11, int=800, string=NHOOO, float64=47.56)

Go 语言常量

常量就像变量一样声明,但是使用 const 关键字作为前缀来声明具有特定类型的常量。不能使用 := 语法声明。

  • 典型

Go 运算符

算数运算符

+ - * / %

% 当第一个操作数除以第二个操作数时,'%'运算符返回余数。例如,x%y。

关系运算符

== , != , > , < , >= , <=

逻辑运算符

&&(逻辑AND), ||(逻辑或), !(逻辑非)

按位运算符(没看懂)

  • &(按位与):将两个数字作为操作数,并对两个数字的每一位进行“与”运算。仅当两个位均为1时,AND的结果才为1。

  • | (按位或):将两个数字作为操作数,并对两个数字的每一位进行“或”运算。两个位中的任何一个为1,OR的结果为1。

  • ^(按位XOR):将两个数字作为操作数,并对两个数字的每一位进行XOR。如果两个位不同,则XOR的结果为1。

  • <<(左移):取两个数字,左移第一个操作数的位,第二个操作数决定移位的位数。

  • (右移):取两个数字,右移第一个操作数的位,第二个操作数决定移位的位数。

  • &^(AND NOT):(按位清除)运算符,该运算符的实际操作为&(^)操作。

赋值运算符

赋值运算符用于为变量赋值(运算) 注意: 右侧的值必须与左侧的变量具有相同的数据类型,否则编译器将抛出错误

= , += , -= , *= , /= , %= , &= , ^= , |=

杂项运算符

  • &:此运算符返回变量的地址。

  • *:此运算符提供指向变量的指针。

  • <-:该运算符的名称为接收。它用于从通道接收值。

Go 类型转换

注意:由于Golang具有强大的类型系统,因此不允许在表达式中混合使用数字类型(例如加,减,乘,除等),并且不允许在两个混合类型之间执行赋值类型。

Go 条件判断

Go 循环语句

如果数组,字符串,切片或map集合为空,则for循环不会抛出错误并继续其流程。换句话说,如果数组,字符串,切片或map为nil,则for循环的迭代次数为零。

  • 语法:

  • initialization: 可选,循环开始之前执行(如变量声明,递增,赋值,函数)

  • condition: 布尔表达式,该表达式在循环的每次迭代开始时计算。如果条件语句的值为true,则执行循环。

  • post: for循环体之后执行。在post语句之后,条件语句再次计算条件语句的值是否为false,然后循环结束

for简单循环

for无线循环

for循环用作while循环

for循环数组

for循环字符串

for循环map的键和值对

for通道

for控制语句

  • break ---> 终止其所在的循环或语句

  • continue ---> 在循环中,表示结束本次循环,进行下次循环。

  • goto ---> 表示无条件跳转到标号处执行。

Go Switch几种用法

  • 表达式 switch

  • 类型 switch

语法:

示例一: 可选语句结合表达式

示例二: 无可选,无表达式

switch语句不带可选语句和表达式

示例三: case多个值

示例四: 类型switch

Go 避免死锁

  • 语法

设置默认值

Go 函数

简单函数

语法

  • func: Go关键字,用于创建函数

  • function_name: 函数名称

  • Parmeter_list: 包含函数参数名称和类型

  • Return_type: 可选,包含函数返回值类型,如果在函数中使用return_type,则必须在函数中使用return语句。

可变参函数

  • 当您要在函数中传递切片时,使用可变参数函数。

  • 当我们不知道参数的数量时,使用可变参数函数。

  • 在程序中使用可变参数函数时,它可以增加程序的可读性。

匿名函数

  • 定义格式

  • 在定义时调用匿名函数

  • 将匿名函数赋值给变量

  • 匿名函数用作回调函数

实现对切片的遍历操作,遍历中访问每个元素的操作使用匿名函数来实现,用户传入不同的匿名函数体可以实现对元素不同的遍历操作

  • 简单实用

  • 匿名函数作为参数传递给其他函数。

  • 从另一个函数返回匿名函数。(常用于回调函数)

Go 函数调用

按值调用

函数内部进行的任何更改都不会反映在调用者的实际参数中

引用调用

  • 指针概念: 在函数调用中,我们传递变量的地址,并使用解引用运算符*修改值

函数返回值

  • 首先先看下函数语法

  • function_name:它是函数的名称。

  • parameter-list:它包含函数参数的名称和类型。

  • return_type_list:这是可选的,它包含函数返回的值的类型。如果在函数中使用return_type,则必须在函数中使用return语句。

返回多个值

返回值命名

允许为返回值提供名称。你也可以在代码中使用这些变量名。

  • 语法

Go 空白标识符

只需使用_(下划线)。它允许编译器忽略该特定变量的错误(declared and not used)。

Go 上下文函数

init()函数的主要目的是初始化无法在全局上下文中初始化的全局变量。

Go Defer关键字

  • 同一程序中允许多个defer语句,并且它们按LIFO(后进先出)顺序执行

  • 在defer语句中,将在执行defer语句时(而不是在调用它们时)评估参数。

  • defer语句通常用于确保在完成文件处理后关闭文件,关闭通道或捕获程序中的紧急情况。

  • 语法

  • 后进先出延迟执行

Go 方法(看不懂)

Go 结构体(看不懂)

Go 数组

  • 语法

var创建数组

简声明创建数组

多维数组

数组是一维的,但是允许创建多维数组。多维数组是相同类型数组的数组

  • 语法

省略号

... 在长度位置处可见,则数组的长度由初始化的元素确定

数组比较

Go Defer关键字

  • 同一程序中允许多个defer语句,并且它们按LIFO(后进先出)顺序执行

  • 在defer语句中,将在执行defer语句时(而不是在调用它们时)评估参数。

  • defer语句通常用于确保在完成文件处理后关闭文件,关闭通道或捕获程序中的紧急情况。

  • 语法

  • 后进先出延迟执行

Go 方法(看不懂)

Go 结构体(看不懂)

Go 数组

  • 语法

使用var关键字创建数组

使用简声明

多维数组

数组是一维的,但是允许创建多维数组。多维数组是相同类型数组的数组

  • 语法

省略号

... 在长度位置处可见,则数组的长度由初始化的元素确定

数组比较

数组复制

  • 语法

数组作为函数参数

将1或多维数组传递给该函数

  • 语法

切片的使用

  • 数组是固定大小

  • 切片大小可伸缩

  • 在内部,切片和数组相连,切片是对基础数组的引用

  • 切片中的第一个索引位置始终为0,而最后一个索引位置将为(切片的长度– 1)。

  • 切片声明: []T 或 []T{} 或 []T{value1, value2, value3, ...value n}

make()函数

make()函数用于创建一个空切片

  • 语法:

func make([]T, len, cap) []T ;

[]T 类型, len 长度, cap 容量(可选,默认和长度一样))

遍历切片方法

多维切片使用

切片比较判断

  • 判断两个[]byte是否相等

  • 使用reflect判断slice是否相等

深度比较两个对象包括它们内部包含的元素是否都相等: 语法: func DeepEqual(x, y interface{}) bool

  • 牺牲了性能

  • 手写切片是否相等判断

性能比 reflect 快

切片排序

Go语言的标准库提供了sort包,其中包含用于对int,float64和字符串切片进行排序的不同类型的排序方法。 这些函数始终按升序对可用元素进行切片排序

切片分割

  • 语法

切片传递给函数

字符串操作

字符串比较

是否存在某个字符或子串

  • 语法

子串出现次数 ( 字符串匹配 )

当 sep 为空时,Count 的返回值是:utf8.RuneCountInString(s) + 1 Count 是计算子串在字符串中出现的无重叠的次数

  • 语法

Fields 和 FieldsFunc

该包提供了六个三组分割函数:Fields 和 FieldsFunc、Split 和 SplitAfter、SplitN 和 SplitAfterN。

  • 语法

  • Fields

用一个或多个连续的空格分隔字符串 s,返回子字符串的数组(slice)如果字符串 s 只包含空格,则返回空列表 ([]string 的长度为 0)

  • FieldsFunc

可以通过回调函数来指定分隔字符串 s 的字符, 也可自定义回调

  • Split 和 SplitAfter、 SplitN 和 SplitAfterN

这四个函数都是通过 sep 进行分割,返回[]string。如果 sep 为空,相当于分成一个个的 UTF-8 字符 Split(s, sep) 和 SplitN(s, sep, -1) 等价;SplitAfter(s, sep) 和 SplitAfterN(s, sep, -1) 等价。

  • 语法

字符串是否有某个前缀或后缀

字符或子串在字符串中出现的位置

字符串 JOIN 操作

将字符串数组(或 slice)连接起来可以通过 Join 实现,函数签名如下:

字符串重复几次

将 s 重复 count 次,如果 count 为负数或返回值长度 len(s)*count 超出 string 上限会导致 panic

  • 语法

字符替换

字符串子串替换

  • 语法

大小写转换

  • 语法

修剪

Replacer 类型

进行多个替换

  • 替换结果写入到文件

Go 语言结构体(struct)

不支持继承但支持组合的轻量级类。

  • 声明结构、 访问结构、修改结构、指针地址

Go 结构体比较

如果结构彼此相等(就其字段值而言),则运算符和方法均返回true;否则,返回false。并且,如果比较的变量属于不同的结构,则编译器将给出错误

Go 嵌套结构体

  • 语法

Go 匿名结构和字段

  • 匿名结构

  • 匿名字段

  • 不允许

结构体内嵌类型

函数用作结构体字段

Go指针

指针的引用

指针解引用

传递指针给函数

函数返回指针地址

结构体指针

指针比较

  • == 运算符:如果两个指针都指向同一个变量,则此运算符返回true。或如果两个指针都指向不同的变量,则返回false。

  • != 运算符:如果两个指针都指向同一个变量,则此运算符返回false。或如果两个指针都指向不同的变量,则返回true。

指针容量和长度

Go 方法接收器

方法简单示例

等同于

在结构上定义接收器方法

指针接收器与值接收器

  • 指针接收器: 考虑下一个结构体有很多的字段。在方法内使用这个结构体做为值接收器需要拷贝整个结构体,这是很昂贵的。在这种情况下使用指针接收器,结构体不会被拷贝,只会传递一个指针到方法内部使用。

  • 值接收器: 除了指针接收器外,在其他的所有情况,都可以被使用值接收器

匿名字段的方法

在方法中使用值接收器 与 在函数中使用值参数

  • 当一个函数有一个值参数,它只能接受一个值参数

  • 当一个方法有一个值接收器,它可以接受值接收器和指针接收器。

在方法中使用指针接收器 与 在函数中使用指针参数

和值参数相类似,函数使用指针参数只接受指针,而使用指针接收器的方法可以使用值接收器和指针接收器。

在非结构体上的方法

Go 接口

  • 静态接口

  • 动态接口

  • 语法:

接口的声明与实现

接口的实际用途

根据公司员工的个人薪资,计算公司的总支出。为了简单起见,我们假定支出的单位都是美元。 公司员工有着不同的薪资结构,以永久工和合同工薪酬为例

接口的内部表示

把接口看作内部的一个元组 (type, value)。 type 是接口底层的具体类型(Concrete Type),而 value 是具体类型的值。

Test 接口只有一个方法 Tester(),而 MyFloat 类型实现了该接口。 我们把变量 f(MyFloat 类型)赋值给了 t(Test 类型)。现在 t 的具体类型为 MyFloat,而 t 的值为 89.7。

空接口

没有包含方法的接口称为空接口。空接口表示为interface{}。由于空接口没有方法,因此所有类型都实现了空接口。

类型断言

类型断言的语法是 i.(T)

类型选择(Type Switch)

i.(T) , 而对于类型选择,类型 T 由关键字 type 代替

类型与接口比较

还可以将一个类型和接口相比较。如果一个类型实现了接口,那么该类型与其实现的接口就可以互相比较。

指针接受者与值接受者

实现多个接口

接口的零值

接口的零值是 nil。对于值为 nil 的接口,其底层值(Underlying Value)和具体类型(Concrete Type)都为 nil。

结合值和接口使用

指针类型实现接口:

值类型实现接口:

通过函数封装接口实现:

Go 并发

Go 编程语言原生支持并发。Go 使用 Go 协程(Goroutine) 和信道(Channel)来处理并发

Go 协程

  • 启动一个新的协程时,协程的调用会立即返回。与函数不同,程序控制不会去等待 Go 协程执行完毕。在调用 Go 协程之后,程序控制会立即返回到代码的下一行,忽略该协程的任何返回值。

  • 如果希望运行其他 Go 协程,Go 主协程必须继续运行着。如果 Go 主协程终止,则程序终止,于是其他 Go 协程也不会继续运行。

启动多个 Go 协程(并发)

启动了两个 Go 协程。现在,这两个协程并发地运行。numbers 协程首先休眠 250 微秒,接着打印 1,然后再次休眠,打印 2,依此类推,一直到打印 5 结束。alphabete 协程同样打印从 a 到 e 的字母,并且每次有 400 微秒的休眠时间。 Go 主协程启动了 numbers 和 alphabete 两个 Go 协程,休眠了 3000 微秒后终止程序。

信道(channel)

无缓冲信道

Go 协程之间的管道,通过使用信道,数据也可以从一端发送,在另一端接收。实现 Go 协程间的通信。

  • 信道的声明

所有信道都关联了一个类型。信道只能运输这种类型的数据,而运输其他类型的数据都是非法的。

  • 信道进行发送和接收

默认是阻塞

在一个单独的 Go 协程计算平方和,而在另一个协程计算立方和,最后在 Go 主协程把平方和与立方和相加。

死锁

当 Go 协程给一个信道发送数据时,照理说会有其他 Go 协程来接收数据。如果没有的话,程序就会在运行时触发 panic,形成死锁。

单向信道

信道转换,把一个双向信道转换成唯送信道或者唯收(Receive Only)信道都是行得通的

我们创建了一个双向信道 cha1。在第 11 行 cha1 作为参数传递给了 sendData 协程。在第 5 行,函数 sendData 里的参数 sendch chan<- int 把 cha1 转换为一个唯送信道。于是该信道在 sendData 协程里是一个唯送信道,而在 Go 主协程里是一个双向信道。该程序最终打印输出 10。

关闭信道和使用 for range 遍历信道

  • 成功接收信道所发送的数据,那么 ok 等于 true。而如果 ok 等于 false

v, ok := <- ch

producer 协程会从 0 到 9 写入信道 chn1,然后关闭该信道。 主函数有一个无限的 for 循环,使用变量 ok 检查信道是否已经关闭。如果 ok 等于 false,说明信道已经关闭,于是退出 for 循环。如果 ok 等于 true,会打印出接收到的值和 ok 的值。

  • 优化

for range 循环用于在一个信道关闭之前,从信道接收数据。

缓冲信道

创建一个有缓冲(Buffer)的信道。只在缓冲已满的情况,才会阻塞向缓冲信道(Buffered Channel)发送数据。同样,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据。

  • 语法

死锁

在上面程序里,我们向容量为 2 的缓冲信道写入 3 个字符串。当在程序控制到达第 3 次写入时,由于它超出了信道的容量,因此这次写入发生了阻塞。现在想要这次写操作能够进行下去,必须要有其它协程来读取这个信道的数据。但在本例中,并没有并发协程来读取这个信道,因此这里会发生死锁(deadlock)。程序会在运行时触发 panic

长度 vs 容量

  • 缓冲信道的容量是指信道可以存储的值的数量。我们在使用 make 函数创建缓冲信道的时候会指定容量大小。

  • 缓冲信道的长度是指信道中当前排队的元素个数。

WaitGroup 工作池

  • WaitGroup 用于等待一批 Go 协程执行结束。程序控制会一直阻塞,直到这些协程全部执行完毕

假设我们有 3 个并发执行的 Go 协程(由 Go 主协程生成)。Go 主协程需要等待这 3 个协程执行结束后,才会终止。这就可以用 WaitGroup 来实现。

工作池缓冲信道实现

  • 1、创建一个 Go 携程池,监听一个等待作业分配的输入型缓冲信道

  • 2、将作业添加到该输入型缓冲信道中

  • 3、作业完成后,再将结果写入一个输出型缓冲信道

  • 4、从输出型缓冲信道读取并打印结果

随着工作协程数量增加,完成作业的总时间会减少。你们可以练习一下:在 main 函数里修改 noOfJobs 和 noOfWorkers 的值,并试着去分析一下结果。

select(信道操作)

select 语句用于在多个发送/接收信道操作中进行选择。select 语句会一直阻塞,直到发送/接收操作准备就绪。如果有多个信道操作准备完毕,select 会随机地选取其中之一执行。该语法与 switch 类似,所不同的是,这里的每个 case 语句都是信道操作

默认情况

在没有 case 准备就绪时,可以执行 select 语句中的默认情况(Default Case)。这通常用于防止 select 语句一直阻塞。

死锁与默认情况

如果存在默认情况,就不会发生死锁,因为在没有其他 case 准备就绪时,会执行默认情况。我们用默认情况重写后

如果 select 只含有值为 nil 的信道,也同样会执行默认情况。

随机选取

当 select 由多个 case 准备就绪时,将会随机地选取其中之一去执行。

空 select

除非有 case 执行,select 语句就会一直阻塞着。在这里,select 语句没有任何 case,因此它会一直阻塞,导致死锁。

Mutex(加锁)

  • 临界区: 当程序并发地运行时,多个 Go 协程不应该同时访问那些修改共享资源的代码。这些修改共享资源的代码称为临界区

  • Mutex 用于提供一种加锁机制(Locking Mechanism),可确保在某时刻只有一个协程在临界区运行,以防止出现竞态条件。

  • 方法

    • mutex.Lock()

    • mutex.Unlock()

使用 Mutex

我们创建了 1000 个 Go 协程。如果每个协程对 x 加 1,最终 x 期望的值应该是 1000, 使用 Mutex,修复竞态条件的问题。

使用信道处理竞态条件

  • 信道来处理竞态条件

Mutex vs 信道

  • 当 Go 协程需要与其他协程通信时,可以使用信道。

  • 而当只允许一个协程访问临界区时,可以使用 Mutex。

63 + 49 + 39.60

Go实现面向对象

  • 使用结构体,而非类

Go 不支持类,而是提供了结构体。结构体中可以添加方法。这样可以将数据和操作数据的方法绑定在一起,实现与类相似的效果。

employee.go

main.go

Go 组合取代继承

Go 不支持继承,但它支持组合(Composition)。组合一般定义为“合并在一起”。汽车就是一个关于组合的例子:一辆汽车由车轮、引擎和其他各种部件组合在一起。

通过嵌套结构体进行组合

  • 例如: 组合的典型例子就是博客帖子。每一个博客的帖子都有标题、内容和作者信息

结构体切片的嵌套

进一步处理这个示例,使用博客帖子的切片来创建一个网站

63 + 49 + 39.60

多态

所有实现了接口的类型,都可以把它的值保存在一个接口类型的变量中。在 Go 中,我们使用接口的这种特性来实现多态。

使用接口实现多态

假设某个虚构的组织所获得的收入来源于两个项目:fixed billing 和 time and material。该组织的净收益等于这两个项目的收入总和

新增收益流(动态添加了新项目)

尽管我们新增了收益流,但却完全没有修改 calculateNetIncome 函数。这就是多态带来的好处。由于新的 Advertisement 同样实现了 Income 接口,所以我们能够向 incomeStreams 切片添加 Advertisement。calculateNetIncome 无需修改,因为它能够调用 Advertisement 类型的 calculate() 和 source() 方法。

错误处理

示例

错误类型的表示

获取错误详细信息的各种方法

  • 1、断言底层结构体类型,使用结构体字段获取更多信息

  • 2、断言底层结构体类型,调用方法获取更多信息

编写一个程序,断言 *DNSError 类型,并调用这些方法来确定该错误是临时性错误,还是由超时导致的。

  • 3、直接比较

查询了模式为 [ 的文件,然而这个模式写的不正确。我们检查了该错误是否为 nil。为了获取该错误的更多信息,将 error 直接与 filepath.ErrBadPattern 相比较。如果该条件满足,那么该错误就是由模式错误导致的

  • 4、不可忽略错误

使用 New 函数创建自定义错误

使用 Errorf 给错误添加更多信息

上面错误是固定的,如果我们想给错误信息添加额外的信息,比如把半径值打印出来

  • Errorf 函数会根据格式说明符,规定错误的格式,并返回一个符合该错误的字符串。

使用结构体类型和字段提供错误的更多信息

使用结构体类型的方法来提供错误的更多信息

编写一个计算矩形面积的程序。如果长或宽小于零,程序就会打印出错误。

panic|recover

  • 有些情况,当程序发生异常时,无法继续运行。在这种情况下,我们会使用 panic 来终止程序

可以认为 panic 和 recover 与其他语言中的 try-catch-finally 语句类似

什么时候应该使用 panic

发生了一个不能恢复的错误,此时程序不能继续运行。 一个例子就是 web 服务器无法绑定所要求的端口。在这种情况下,就应该使用 panic,因为如果不能绑定端口,啥也做不了。

panic 有两个合理的用例。

  • 1、发生了一个不能恢复的错误,此时程序不能继续运行。 一个例子就是 web 服务器无法绑定所要求的端口。在这种情况下,就应该使用 panic,因为如果不能绑定端口,啥也做不了。

  • 2、发生了一个编程上的错误。 假如我们有一个接收指针参数的方法,而其他人使用 nil 作为参数调用了它。在这种情况下,我们可以使用 panic,因为这是一个编程错误:用 nil 参数调用了一个只能接收合法指针的方法。

panic 示例

发生 panic 时的 defer

当函数发生 panic 时,它会终止运行,在执行完所有的延迟函数后,程序控制返回到该函数的调用方。这样的过程会一直持续下去,直到当前协程的所有函数都返回退出,然后程序会打印出 panic 信息,接着打印出堆栈跟踪,最后程序终止。

  • 发生 panic 时,首先执行了延迟函数,接着控制返回到函数调用方,调用方的延迟函数继续运行,直到到达顶层调用函数。

recover

recover 是一个内建函数,用于重新获得 panic 协程的控制。

  • 只有在延迟函数的内部,调用 recover 才有用

  • 在延迟函数内调用 recover,可以取到 panic 的错误信息,并且停止 panic 续发事件(Panicking Sequence),程序运行恢复正常

  • 如果在延迟函数的外部调用 recover,就不能停止 panic 续发事件。

panic,recover 和 Go 协程

只有在相同的 Go 协程中调用 recover 才管用。recover 不能恢复一个不同协程的 panic

由 go b() 修改为 b(),就可以恢复 panic 了, 因为 panic 发生在与 recover 相同的协程里。

恢复后获得堆栈跟踪

当我们恢复 panic 时,我们就释放了它的堆栈跟踪。实际上,在上述程序里,恢复 panic 之后,我们就失去了堆栈跟踪。

有办法可以打印出堆栈跟踪,就是使用 Debug 包中的 PrintStack 函数。

头等函数

可以把函数赋值给变量,也可以把函数作为其他函数的参数或者返回值

匿名函数

  • 1、 匿名函数赋值变量

  • 2、匿名函数立即调用

  • 3、向匿名函数传递参数

自定义的函数类型

高阶函数

高阶函数(Hiher-order Function)定义为:满足下列条件之一的函数:

  • 接收一个或多个函数作为参数

  • 返回值是一个函数

  • 把函数作为参数,传递给其它函数

  • 在其它函数中返回函数

闭包

当一个函数所访问的变量定义在函数体的外部时,称这样的匿名函数为闭包

  • 每一个闭包都会绑定一个它自己的外围变量

头等函数的实际用途

示例二: 对切片的每个元素执行相同的操作,并返回结果

反射

反射就是程序能够在运行时检查变量和值,求出它们的类型

reflect 包

  • reflect.Type: 表示 interface{} 具体类型

  • reflect.Value: 表示它的具体值

  • reflect.TypeOf() 返回 reflect.Type

  • reflect.ValueOf() 返回 reflect.Value

  • reflect.Kind: 表示该类型的特定类别

  • NumField(): 返回结构体中字段的数量

  • Field(): 返回字段 i 的 reflect.Value。

reflect 实现了运行时反射。reflect 包会帮助识别 interface{} 变量的底层具体类型和具体值

reflect.Type 和 reflect.Value

reflect.Kind

Type 表示 interface{} 的实际类型(在这里是 main.Order),而 Kind 表示该类型的特定类别

NumField() 和 Field() 方法

Int() 和 String() 方法

Int 和 String 可以帮助我们分别取出 reflect.Value 作为 int64 和 string。

完整实例(通用查询器)

Map(集合)

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

语法

map 示例

delete 函数

delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key

Last updated