本书采用“理论+实例”的形式编写,通过大量的实例,结合作者多年的一线开发实战经验,全面介绍了Go语言的语法及使用方法。本书作者特意为本书精心录制了详细的配套教学视频,这将极大地提升读者的学习效率。本书分为13章,主要介绍了Go语言的特性与环境搭建、基本语法与使用、容器(存储和组织数据的方式)、流程控制、函数、结构体、接口(interface)、包(package)、并发、反射、编译与工具、开发技巧,最后的实战演练部分剖析了作者的开源网络库cellnet的架构及设计思想,并且实现了Socket聊天功能。本书对于Go语言的特色功能并发有全面、深入的讲解,需要读者重点学习。本书特别适合Go语言初学者入门和进阶阅读。另外,本书也适合社会培训学校作为教材使用,还适合大中专院校的相关专业作为教学参考书。
封面图
目录
- 配套学习资源
- 前言
- 第1章 初识Go语言1
- 1.1 Go语言特性1
- 1.2 使用Go语言的项目9
- 1.3 怎样安装Go语言开发包10
- 1.3.1 Windows版安装11
- 1.3.2 Linux版安装13
- 1.4 搭建开发环境14
- 1.4.1 集成开发环境——Jetbrains GoLand14
- 1.4.2 方便定义功能的编辑器——Visual Studio Code15
- 第2章 Go语言基本语法与使用19
- 2.1 变量19
- 2.1.1 声明变量19
- 2.1.2 初始化变量20
- 2.1.3 多个变量同时赋值23
- 2.1.4 匿名变量——没有名字的变量24
- 2.2 数据类型24
- 2.2.1 整型25
- 2.2.2 浮点型25
- 2.2.3 示例:输出正弦函数(Sin)图像26
- 2.2.4 布尔型28
- 2.2.5 字符串29
- 2.2.6 字符31
- 2.2.7 切片——能动态分配的空间32
- 2.3 转换不同的数据类型33
- 2.4 指针34
- 2.4.1 认识指针地址和指针类型35
- 2.4.2 从指针获取指针指向的值36
- 2.4.3 使用指针修改值37
- 2.4.4 示例:使用指针变量获取命令行的输入信息39
- 2.4.5 创建指针的另一种方法——new()函数40
- 2.5 变量生命期——变量能够使用的代码范围40
- 2.5.1 什么是栈41
- 2.5.2 什么是堆42
- 2.5.3 变量逃逸(Escape Analysis)——自动决定变量分配方式,提高运行效率43
- 2.6 字符串应用46
- 2.6.1 计算字符串长度46
- 2.6.2 遍历字符串——获取每一个字符串元素47
- 2.6.3 获取字符串的某一段字符48
- 2.6.4 修改字符串49
- 2.6.5 连接字符串49
- 2.6.6 格式化50
- 2.6.7 示例:Base64编码——电子邮件的基础编码格式51
- 2.6.8 示例:从INI配置文件中查询需要的值52
- 2.7 常量——恒定不变的值57
- 2.7.1 枚举——一组常量值58
- 2.7.2 将枚举值转换为字符串59
- 2.8 类型别名(Type Alias)60
- 2.8.1 区分类型别名与类型定义61
- 2.8.2 非本地类型不能定义方法62
- 2.8.3 在结构体成员嵌入时使用别名63
- 第3章 容器:存储和组织数据的方式65
- 3.1 数组——固定大小的连续空间65
- 3.1.1 声明数组66
- 3.1.2 初始化数组66
- 3.1.3 遍历数组——访问每一个数组元素67
- 3.2 切片(slice)——动态分配大小的连续空间67
- 3.2.1 从数组或切片生成新的切片68
- 3.2.2 声明切片70
- 3.2.3 使用make()函数构造切片71
- 3.2.4 使用append()函数为切片添加元素71
- 3.2.5 复制切片元素到另一个切片73
- 3.2.6 从切片中删除元素74
- 3.3 映射(map)——建立事物关联的容器76
- 3.3.1 添加关联到map并访问关联和数据76
- 3.3.2 遍历map的“键值对”——?访问每一个map中的关联关系77
- 3.3.3 使用delete()函数从map中删除键值对79
- 3.3.4 清空map中的所有元素79
- 3.3.5 能够在并发环境中使用的map——?sync.Map79
- 3.4 列表(list)——可以快速增删的非连续空间的容器81
- 3.4.1 初始化列表83
- 3.4.2 在列表中插入元素83
- 3.4.3 从列表中删除元素84
- 3.4.4 遍历列表——访问列表的每一个元素85
- 第4章 流程控制87
- 4.1 条件判断(if)87
- 4.2 构建循环(for)88
- 4.2.1 for中的初始语句——开始循环时执行的语句89
- 4.2.2 for中的条件表达式——控制是否循环的开关89
- 4.2.3 for中的结束语句——每次循环结束时执行的语句90
- 4.3 示例:九九乘法表90
- 4.4 键值循环(for range)——直接获得对象的索引和数据91
- 4.4.1 遍历数组、切片——获得索引和元素92
- 4.4.2 遍历字符串——获得字符92
- 4.4.3 遍历map——获得map的键和值92
- 4.4.4 遍历通道(channel)——接收通道数据93
- 4.4.5 在遍历中选择希望获得的变量93
- 4.5 分支选择(switch)——拥有多个条件分支的判断94
- 4.5.1 基本写法95
- 4.5.2 跨越case的fallthrough——兼容C语言的case设计96
- 4.6 跳转到指定代码标签(goto)96
- 4.6.1 使用goto退出多层循环96
- 4.6.2 使用goto集中处理错误97
- 4.6.3 统一错误处理98
- 4.7 跳出指定循环(break)——可以跳出多层循环99
- 4.8 继续下一次循环(continue)100
- 第5章 函数(function)101
- 5.1 声明函数101
- 5.1.1 普通函数的声明形式101
- 5.1.2 参数类型的简写102
- 5.1.3 函数的返回值102
- 5.1.4 调用函数104
- 5.1.5 示例:将“秒”解析为时间单位104
- 5.1.6 示例:函数中的参数传递效果测试105
- 5.2 函数变量——把函数作为值保存到变量中108
- 5.3 示例:字符串的链式处理——操作与数据分离的设计技巧109
- 5.4 匿名函数——没有函数名字的函数112
- 5.4.1 定义一个匿名函数112
- 5.4.2 匿名函数用作回调函数113
- 5.4.3 使用匿名函数实现操作封装113
- 5.5 函数类型实现接口——把函数作为接口来调用115
- 5.5.1 结构体实现接口115
- 5.5.2 函数体实现接口116
- 5.5.3 HTTP包中的例子117
- 5.6 闭包(Closure)——引用了外部变量的匿名函数118
- 5.6.1 在闭包内部修改引用的变量119
- 5.6.2 示例:闭包的记忆效应119
- 5.6.3 示例:闭包实现生成器121
- 5.7 可变参数——参数数量不固定的函数形式122
- 5.7.1 fmt包中的例子122
- 5.7.2 遍历可变参数列表——获取每一个参数的值123
- 5.7.3 获得可变参数类型——获得每一个参数的类型124
- 5.7.4 在多个可变参数函数中传递参数125
- 5.8 延迟执行语句(defer)127
- 5.8.1 多个延迟执行语句的处理顺序127
- 5.8.2 使用延迟执行语句在函数退出时释放资源127
- 5.9 处理运行时发生的错误131
- 5.9.1 net包中的例子131
- 5.9.2 错误接口的定义格式132
- 5.9.3 自定义一个错误132
- 5.9.4 示例:在解析中使用自定义错误133
- 5.10 宕机(panic)——程序终止运行135
- 5.10.1 手动触发宕机135
- 5.10.2 在运行依赖的必备资源缺失时主动触发宕机136
- 5.10.3 在宕机时触发延迟执行语句136
- 5.11 宕机恢复(recover)——防止程序崩溃137
- 5.11.1 让程序在崩溃时继续执行137
- 5.11.2 panic和recover的关系139
- 第6章 结构体(struct)141
- 6.1 定义结构体141
- 6.2 实例化结构体——为结构体分配内存并初始化142
- 6.2.1 基本的实例化形式142
- 6.2.2 创建指针类型的结构体143
- 6.2.3 取结构体的地址实例化143
- 6.3 初始化结构体的成员变量144
- 6.3.1 使用“键值对”初始化结构体145
- 6.3.2 使用多个值的列表初始化结构体146
- 6.3.3 初始化匿名结构体147
- 6.4 构造函数——结构体和类型的一系列初始化操作的函数封装148
- 6.4.1 多种方式创建和初始化结构体——模拟构造函数重载149
- 6.4.2 带有父子关系的结构体的构造和初始化——模拟父级构造调用149
- 6.5 方法150
- 6.5.1 为结构体添加方法151
- 6.5.2 接收器——方法作用的目标152
- 6.5.3 示例:二维矢量模拟玩家移动155
- 6.5.4 为类型添加方法160
- 6.5.5 示例:使用事件系统实现事件的响应和处理165
- 6.6 类型内嵌和结构体内嵌170
- 6.6.1 声明结构体内嵌170
- 6.6.2 结构内嵌特性172
- 6.6.3 使用组合思想描述对象特性173
- 6.6.4 初始化结构体内嵌174
- 6.6.5 初始化内嵌匿名结构体175
- 6.6.6 成员名字冲突177
- 6.7 示例:使用匿名结构体分离JSON数据178
- 第7章 接口(interface)181
- 7.1 声明接口181
- 7.1.1 接口声明的格式181
- 7.1.2 开发中常见的接口及写法182
- 7.2 实现接口的条件182
- 7.2.1 接口被实现的条件一:接口的方法与实现接口的类型方法格式一致182
- 7.2.2 条件二:接口中所有方法均被实现185
- 7.3 理解类型与接口的关系186
- 7.3.1 一个类型可以实现多个接口186
- 7.3.2 多个类型可以实现相同的接口187
- 7.4 示例:便于扩展输出方式的日志系统189
- 7.5 示例:使用接口进行数据的排序195
- 7.5.1 使用sort.Interface接口进行排序195
- 7.5.2 常见类型的便捷排序197
- 7.5.3 对结构体数据进行排序199
- 7.6 接口的嵌套组合——将多个接口放在一个接口内202
- 7.7 在接口和类型间转换205
- 7.7.1 类型断言的格式205
- 7.7.2 将接口转换为其他接口205
- 7.7.3 将接口转换为其他类型208
- 7.8 空接口类型(interface{})——能保存所有值的类型208
- 7.8.1 将值保存到空接口209
- 7.8.2 从空接口获取值209
- 7.8.3 空接口的值比较210
- 7.9 示例:使用空接口实现可以保存任意值的字典211
- 7.10 类型分支——批量判断空接口中变量的类型214
- 7.10.1 类型断言的书写格式214
- 7.10.2 使用类型分支判断基本类型215
- 7.10.3 使用类型分支判断接口类型215
- 7.11 示例:实现有限状态机(FSM)217
- 第8章 包(package)227
- 8.1 工作目录(GOPATH)227
- 8.1.1 使用命令行查看GOPATH信息227
- 8.1.2 使用GOPATH的工程结构228
- 8.1.3 设置和使用GOPATH229
- 8.1.4 在多项目工程中使用GOPATH230
- 8.2 创建包package——编写自己的代码扩展231
- 8.3 导出标识符——让外部访问包的类型和值231
- 8.3.1 导出包内标识符231
- 8.3.2 导出结构体及接口成员232
- 8.4 导入包(import)——在代码中使用其他的代码232
- 8.4.1 默认导入的写法233
- 8.4.2 导入包后自定义引用的包名234
- 8.4.3 匿名导入包——只导入包但不使用包内类型和数值235
- 8.4.4 包在程序启动前的初始化入口:init235
- 8.4.5 理解包导入后的init()函数初始化顺序235
- 8.5 示例:工厂模式自动注册——管理多个包的结构体237
- 第9章 并发241
- 9.1 轻量级线程(goroutine)——根据需要随时创建的“线程”241
- 9.1.1 使用普通函数创建goroutine241
- 9.1.2 使用匿名函数创建goroutine244
- 9.1.3 调整并发的运行性能(GOMAXPROCS)245
- 9.1.4 理解并发和并行245
- 9.1.5 Go语言的协作程序(goroutine)和普通的协作程序(coroutine)246
- 9.2 通道(channel)——在多个goroutine间通信的管道246
- 9.2.1 通道的特性247
- 9.2.2 声明通道类型247
- 9.2.3 创建通道248
- 9.2.4 使用通道发送数据248
- 9.2.5 使用通道接收数据249
- 9.2.6 示例:并发打印252
- 9.2.7 单向通道——通道中的单行道254
- 9.2.8 带缓冲的通道255
- 9.2.9 通道的多路复用——同时处理接收和发送多个通道的数据257
- 9.2.10 示例:模拟远程过程调用(RPC)258
- 9.2.11 示例:使用通道响应计时器的事件261
- 9.2.12 关闭通道后继续使用通道264
- 9.3 示例:Telnet回音服务器——TCP服务器的基本结构266
- 9.4 同步——保证并发环境下数据访问的正确性273
- 9.4.1 竞态检测——检测代码在并发环境下可能出现的问题273
- 9.4.2 互斥锁(sync.Mutex)——保证同时只有一个goroutine可以访问共享资源275
- 9.4.3 读写互斥锁(sync.RWMutex)——在读比写多的环境下比互斥锁更高效277
- 9.4.4 等待组(sync.WaitGroup)——保证在并发环境中完成指定数量的任务277
- 第10章 反射280
- 10.1 反射的类型对象(reflect.Type)280
- 10.1.1 理解反射的类型(Type)与种类(Kind)281
- 10.1.2 指针与指针指向的元素283
- 10.1.3 使用反射获取结构体的成员类型284
- 10.1.4 结构体标签(Struct Tag)——对结构体字段的额外信息标签287
- 10.2 反射的值对象(reflect.Value)288
- 10.2.1 使用反射值对象包装任意值288
- 10.2.2 从反射值对象获取被包装的值289
- 10.2.3 使用反射访问结构体的成员字段的值290
- 10.2.4 反射对象的空和有效性判断292
- 10.2.5 使用反射值对象修改变量的值293
- 10.2.6 通过类型创建类型的实例297
- 10.2.7 使用反射调用函数298
- 10.3 示例:将结构体的数据保存为JSON格式的文本数据299
- 第11章 编译与工具306
- 11.1 编译(go build)306
- 11.1.1 go build 无参数编译306
- 11.1.2 go build+文件列表307
- 11.1.3 go build +包308
- 11.1. 4 go build编译时的附加参数310
- 11.2 编译后运行(go run)310
- 11.3 编译并安装(go install)311
- 11.4 一键获取代码、编译并安装(go get)312
- 11.4.1 远程包的路径格式312
- 11.4.2 go get + 远程包312
- 11.4.3 go get使用时的附加参数313
- 11.5 测试(go test)313
- 11.5.1 单元测试——测试和验证代码的框架313
- 11.5.2 基准测试——获得代码内存占用和运行效率的性能数据316
- 11.6 性能分析(go pprof)——发现代码性能问题的调用位置319
- 11.6.1 安装第三方图形化显式分析数据工具(Graphviz)319
- 11.6.2 安装第三方性能分析来分析代码包319
- 11.6.3 性能分析代码319
- 第12章 “避坑”与技巧323
- 12.1 合理地使用并发特性323
- 12.1.1 了解goroutine的生命期时再创建goroutine323
- 12.1.2 避免在不必要的地方使用通道326
- 12.2 反射:性能和灵活性的双刃剑330
- 12.3 接口的nil判断335
- 12.4 map的多键索引——多个数值条件可以同时查询336
- 12.4.1 基于哈希值的多键索引及查询337
- 12.4.2 利用map特性的多键索引及查询341
- 12.4.3 总结342
- 12.5 优雅地处理TCP粘包342
- 第13章 实战演练——剖析cellnet网络库设计并实现Socket聊天功能354
- 13.1 了解cellet网络库特性、流程及架构354
- 13.1.1 cellnet网络库的特性354
- 13.1.2 cellnet网络库的流程及架构356
- 13.2 管理TCP Socket连接356
- 13.2.1 理解Socket的事件类型357
- 13.2.2 管理事件回调359
- 13.2.3 连接器(Connector)361
- 13.2.4 会话管理(SessionManager)363
- 13.2.5 接受器(Acceptor)366
- 13.3 组织接收和发送数据流程的Socket会话(Session)367
- 13.3.1 在会话开始时启动goroutine和派发事件368
- 13.3.2 会话中的接收数据循环369
- 13.3.3 会话中的发送数据循环370
- 13.4 排队处理事件的事件队列(EventQueue)372
- 13.4.1 实现事件队列372
- 13.4.2 使用不同的事件队列模式处理数据374
- 13.5 消息编码(codec)——让cellnet支持消息的多种编码格式377
- 13.6 消息元信息(MessageMeta)——消息ID、消息名称和消息类型的关联关系379
- 13.6.1 理解消息元信息380
- 13.6.2 注册消息元信息380
- 13.6.3 示例:使用消息元信息381
- 13.6.4 实现消息的编码(EncodeMessage())和解码(DecodeMessage())函数382
- 13.7 接收和发送封包(packet)383
- 13.7.1 接收可变长度封包384
- 13.7.2 了解封包数据读取器(PacketReader)385
- 13.7.3 了解封包数据写入器(PacketWriter)387
- 13.7.4 读取自定义封包及数据387
- 13.7.5 写入自定义封包及数据389
- 13.7.6 响应消息处理事件390
- 13.8 使用cellnet网络库实现聊天功能392
- 13.8.1 定义聊天协议393
- 13.8.2 实现客户端功能394
- 13.8.3 实现服务器功能396
- 13.8.4 运行聊天服务器和客户端398