书籍详情
深入浅出Go语言核心编程
作者:张朝明、李奕锋、甘海彬
出版社:清华大学出版社
出版时间:2024-01-01
ISBN:9787302649106
定价:¥149.00
购买这本书可以去
内容简介
《深入浅出Go语言核心编程》是一本全面而深入的Go语言学习手册,涵盖了Go语言的诸多关键特性,包括语法结构、内存原理、并发、上下文机制与框架应用等。本书共20章。第1章引导读者快速搭建开发环境,详细介绍Go语言的环境配置及编译运行的具体细节。第2~5章详细讨论Go语言独特的变量、常量、常用数据类型和流程控制,并重点解析复杂类型的底层实现机制。第6~8章讲解Go语言的函数及如何实现面向对象编程,打通Go语言面向过程和面向对象编程之间的桥梁。第9~12章探讨Go语言的一些高级话题,包括并发、上下文、反射、泛型等。第13~15章探讨Go语言的I/O、网络编程及RPC通信等编程场景。第16~18章是Go语言的扩展话题,涵盖了内存管理、正则表达式和Go语言的汇编。第19章和第20章重点探讨了Go语言在日常开发中的典型应用,主要介绍HTTP框架Gin的使用,以及如何利用Go语言开发一个综合项目。 《深入浅出Go语言核心编程》内容丰富,由浅入深,力求带领读者探究Go语言的本质,既适合初次接触Go语言的新手,也适合有一定经验的软件开发人员阅读。
作者简介
张朝明 前金山软件技术总监,部就职于多家互联网企业,拥有近20年的软件开发和设计经验,尤其擅长Golang、Java等编程语言,著有《XML开发典型应用:数据标记、处理、共享与分析》 《21天学通Oracle》《Oracle入门很简单》等书。李奕锋某头部互联网公司Go研发工程师,曾任金山游戏高级软件工程师。拥有丰富的分布式、高并发、大规模微服务集群的开发设计经验,在企业应用、游戏社区等领域参与过大型Go项目设计开发工作。甘海彬曾先后在魅族、西山居任职高级研发工程师,现于金山办公担任研发经理,对开发企业级应用有着丰富经验。擅长语言Go、Java语言以及全栈开发,对分布式架构、高并发微服务设计有深刻理解。
目录
目 录
第1章 第一个Go程序 1
1.1 搭建开发环境 1
1.2 一个简单的Go程序 3
1.2.1 编写第一个Go程序 3
1.2.2 运行第一个Go程序 5
1.3 环境变量说明 6
1.4 在IDE中运行Go语言程序 7
1.4.1 创建项目 7
1.4.2 创建Go程序文件 8
1.4.3 运行.go文件 9
1.5 Go语言如何实现跨平台 9
1.5.1 跨平台的准备工作 9
1.5.2 执行跨平台编译 10
1.6 探寻Go语言程序的编译执行过程 11
1.6.1 go build命令的选项 11
1.6.2 查看编译的详细过程 11
1.6.3 链接环节 13
1.7 编程范例——启动参数的使用 14
1.7.1 程序启动的入口函数 14
1.7.2 获取启动参数 15
1.8 本章小结 19
第2章 变量与常量 20
2.1 变量 20
2.1.1 变量声明 20
2.1.2 变量赋值 21
2.1.3 同时进行变量声明和赋值 23
2.1.4 多重赋值与“:=”操作符 24
2.1.5 没有多余的局部变量 25
2.1.6 全局变量 25
2.1.7 全局变量与链接 26
2.2 常量 26
2.2.1 常量的声明 26
2.2.2 常量块的使用 27
2.2.3 常量可以声明而不使用 28
2.3 iota与枚举 28
2.3.1 iota实现自增 28
2.3.2 iota计数不会中断 30
2.3.3 iota的使用场景 31
2.4 编程范例——iota的使用技巧 32
2.5 本章小结 34
第3章 简单数据类型 35
3.1 整型 35
3.1.1 声明整型变量 35
3.1.2 int和uint的设计初衷 36
3.2 浮点型 37
3.2.1 声明浮点型变量 37
3.2.2 浮点型会产生精度损失 37
3.2.3 Go语言中没有float关键字的原因 38
3.2.4 浮点型与类型推导 38
3.2.5 浮点型的比较 39
3.3 布尔类型 40
3.4 字符型 40
3.4.1 声明字符型变量 41
3.4.2 转义字符 42
3.5 字符串类型 43
3.5.1 声明字符串变量 43
3.5.2 字符串在磁盘中的存储 43
3.5.3 字符串在内存中的存储 44
3.5.4 利用rune类型处理文本 45
3.5.5 rune类型与字符集的关系 46
3.6 数组类型 46
3.6.1 声明数组变量 47
3.6.2 利用索引来访问数组元素 47
3.6.3 数组大小不可变更 48
3.6.4 数组作为函数参数 48
3.7 编程范例——原义字符的使用 49
3.8 本章小结 50
第4章 复杂数据类型 51
4.1 值类型和指针类型 51
4.1.1 值类型和指针类型的存储结构 51
4.1.2 为什么要区分值类型和指针类型 53
4.1.3 关于引用类型 54
4.2 slice(切片)的使用及实现原理 54
4.2.1 切片如何实现大小可变 54
4.2.2 切片的声明和定义 55
4.2.3 切片长度的扩展 56
4.2.4 切片容量的扩展 57
4.2.5 切片参数的复制 58
4.2.6 利用数组创建切片 60
4.2.7 利用切片创建切片 62
4.2.8 切片元素的修改 62
4.2.9 切片的循环处理 63
4.2.10 切片索引越界 63
4.2.11 总结切片操作的底层原理 64
4.3 map(映射)的使用及实现原理 65
4.3.1 声明和创建map 65
4.3.2 遍历map中的元素 65
4.3.3 元素查找与避免二义性 66
4.3.4 删除元素 67
4.3.5 map的存储结构解析 68
4.3.6 map元素的定位原理解析 70
4.3.7 map的容量扩展原理解析 72
4.4 channel(通道)的使用及实现原理 72
4.4.1 channel的使用 72
4.4.2 channel的实现原理 74
4.4.3 channel与消息队列、协程通信的对比 76
4.5 自定义结构体 76
4.5.1 自定义数据类型和自定义结构体 76
4.5.2 自定义结构体的使用 77
4.5.3 利用new创建实例 78
4.5.4 从自定义结构体看访问权限控制 79
4.5.5 自描述的访问权限 80
4.6 编程范例——结构体使用实例 80
4.6.1 利用自定义结构体实现bitmap 80
4.6.2 利用timer.Ticker实现定时任务 84
4.7 本章小结 87
第5章 流程控制 88
5.1 分支控制 88
5.1.1 if语句实现分支控制 88
5.1.2 switch语句实现分支控制 89
5.1.3 分支控制的本质是向下跳转 90
5.1.4 避免多层if嵌套的技巧 91
5.2 循环控制 94
5.2.1 for循环 94
5.2.2 for-range循环 95
5.2.3 循环控制的本质是向上跳转 97
5.2.4 循环和递归的区别 98
5.3 跳转控制 99
5.3.1 goto关键字的使用 99
5.3.2 goto的本质是任意跳转 101
5.4 编程范例——流程控制的灵活使用 101
5.4.1 for循环的误区 101
5.4.2 switch-case的灵活使用 104
5.5 本章小结 106
第6章 函数 107
6.1 函数在Go语言中的地位 107
6.1.1 Go语言中函数和方法的区别 108
6.1.2 重新理解变量声明中数据类型出现的位置 109
6.2 函数的定义 110
6.2.1 函数的参数 110
6.2.2 函数的返回值 111
6.2.3 函数多返回值的实现原理 113
6.3 函数的管理——模块和包 115
6.3.1 函数管理形式 115
6.3.2 模块与文件夹 116
6.3.3 本地包管理 119
6.3.4 模块名与文件夹名称 121
6.3.5 代码规范的意义 123
6.4 函数的调用和执行 123
6.4.1 包的别名与函数调用 123
6.4.2 init()函数与隐式执行顺序 125
6.4.3 利用init()函数执行初始化 126
6.4.4 利用匿名包实现函数导入 127
6.5 将函数作为变量使用 128
6.5.1 将函数赋值给变量 128
6.5.2 函数赋值给变量的应用场景 129
6.6 匿名函数和闭包 132
6.6.1 为什么需要匿名函数 132
6.6.2 闭包 134
6.7 函数的强制转换 137
6.7.1 从数据类型的定义到函数类型的定义 137
6.7.2 从数据类型的强制转换到函数类型的强制转换 138
6.7.3 函数类型及强制转换的意义 138
6.7.4 利用强制转换为函数绑定方法 140
6.8 编程范例——闭包的使用 142
6.8.1 闭包封装变量的真正含义 142
6.8.2 利用指针修改闭包外部的变量 145
6.9 本章小结 146
第7章 异常处理 147
7.1 异常机制的意义 147
7.2 Go语言中的异常 150
7.2.1 创建异常 150
7.2.2 抛出异常 151
7.2.3 自定义异常 152
7.3 异常捕获 154
7.3.1 利用延迟执行机制来捕获异常 155
7.3.2 在上层调用者中捕获异常 157
7.3.3 异常捕获的限制条件 158
7.4 异常捕获后的资源清理 159
7.4.1 未正常释放锁对象带来的副作用 160
7.4.2 确保锁对象释放的正确方式 162
7.5 编程范例——异常的使用及误区 163
7.5.1 利用结构体自定义异常 163
7.5.2 未成功捕获异常,导致程序崩溃 164
7.6 本章小结 166
第8章 Go语言的面向对象编程 167
8.1 面向对象编程的本质 167
8.2 Go语言实现封装 168
8.2.1 Go语言中字段和方法的封装 168
8.2.2 为值类型和指针类型绑定方法的区别 169
8.3 Go语言实现继承 171
8.3.1 利用组合实现继承 171
8.3.2 匿名字段的支持 173
8.3.3 多继承 174
8.4 Go语言实现多态 176
8.5 面向接口编程 178
8.5.1 Go语言中的接口 179
8.5.2 Go语言中的接口实现 179
8.5.3 利用面向接口编程实现方法多态 180
8.6 编程范例——接口的典型应用 181
8.6.1 接口嵌套实例 181
8.6.2 伪继承与接口实现 183
8.7 本章小结 184
第9章 并发 185
9.1 线程的概念 185
9.2 线程模型 187
9.3 协程的工作原理 187
9.3.1 协程的使用 188
9.3.2 GPM模型 189
9.3.3 从3种线程模型看GOMAXPROCS参数 191
9.4 Go语言中的协程同步 192
9.4.1 独占锁——Mutex 192
9.4.2 读写锁——RWMutex 195
9.4.3 等待组——WaitGroup 198
9.5 利用channel实现协程同步 199
9.5.1 利用channel实现锁定 200
9.5.2 利用channel实现等待组 202
9.5.3 总结使用channel实现并发控制 204
9.6 让出时间片 204
9.6.1 time.Sleep()和runtime.Gosched()的本质区别 204
9.6.2 runtime.Gosched()与多核CPU 205
9.7 Go语言中的单例 206
9.7.1 利用sync.Once实现单例 206
9.7.2 sync.Once的实现原理 208
9.8 编程范例——协程池及协程中断 209
9.8.1 协程池的实现 209
9.8.2 协程的中断执行 213
9.9 本章小结 217
第10章 上下文 218
10.1 上下文和普通参数的区别 218
10.2 上下文树 219
10.2.1 上下文接口——Context 219
10.2.2 利用context.emptyCtx创建树的根节点 219
10.2.3 上下文树的构建 220
10.3 利用valueCtx实现信息透传 222
10.3.1 valueCtx用于参数传递 222
10.3.2 从父节点获得透传值 223
10.4 利用cancelCtx通知协程终止执行 224
10.4.1 通知子协程终止执行 225
10.4.2 通知子协程的实现过程 226
10.4.3 为什么需要取消函数 230
10.5 利用timerCtx实现定时取消 230
10.5.1 调用context.WithDeadline()创建定时器上下文 231
10.5.2 调用context.WithTimeout()创建定时器上下文 233
10.6 编程范例——上下文的典型应用场景 234
10.6.1 利用结构体传递参数 234
10.6.2 valueContext为什么需要key 236
10.6.3 利用cancelCtx同时取消多个子协程 237
10.7 本章小结 239
第11章 反射 240
11.1 反射的意义 240
11.2 反射的API 241
11.2.1 利用reflect.TypeOf()来获得类型信息 241
11.2.2 利用reflect.Type.Kind()方法来获取类型的具体分类 242
11.2.3 利用reflect.Type.Element()方法来获取元素类型 243
11.2.4 类型断言的用法与局限性 245
11.3 值信息 246
11.3.1 利用reflect.ValueOf()来获得值信息 246
11.3.2 利用reflect.Value.Kind()来获得值的分类信息 247
11.3.3 利用reflect.Value.Elem()来获得值的元素信息 248
11.3.4 利用反射访问和修改值信息 249
11.3.5 利用反射机制动态调用方法 252
11.4 编程范例——动态方法调用 255
11.5 本章小结 258
第12章 泛型 259
12.1 泛型的意义 259
12.2 泛型应用到函数 261
12.2.1 泛型函数的使用 261
12.2.2 泛型中的隐含信息 262
12.2.3 避免类型强制转换 263
12.2.4 泛型类型的单独定义 264
12.3 泛型导致接口定义的变化 265
12.3.1 接口定义的变化 265
12.3.2 空接口的二义性 266
12.3.3 接口类型的限制 266
12.4 泛型类型应用到receiver 268
12.4.1 泛型类型不能直接用于定义receiver 268
12.4.2 间接实现泛型定义receiver 269
12.5 编程范例——自定义队列的实现 270
12.6 本章小结 272
第13章 I/O 273
13.1 Reader和Writer 273
13.1.1 理解Reader和Writer 273
13.1.2 Reader和Writer接口 274
13.1.3 Go语言的I/O API要解决的问题 275
13.1.4 文件读取 275
13.1.5 文件写入 278
13.1.6 文件权限与umask 281
13.1.7 一次性读写 283
13.2 缓冲区读写 284
13.2.1 bufio中的Reader和Writer 285
13.2.2 利用bufio实现按行读取 285
13.3 字符串数据源 287
13.3.1 strings.Reader解析 287
13.3.2 字节扫描器ByteScanner 288
13.3.3 按Rune读取UTF-8字符 289
13.4 bufio.Scanner的使用 292
13.4.1 扫描过程及源码解析 292
13.4.2 扫描时的最大支持 298
13.4.3 扫描时的最小容忍 301
13.5 编程范例——文件系统相关操作 303
13.5.1 查看文件系统 303
13.5.2 临时文件 305
13.6 本章小结 307
第14章 网络编程 308
14.1 网络连接的本质 308
14.2 利用TCP实现网络通信 310
14.2.1 创建TCP连接 310
14.2.2 利用TCP连接进行消息传递 312
14.3 利用UDP实现网络通信 315
14.3.1 监听模式 316
14.3.2 拨号模式 319
14.3.3 总结监听模式和拨号模式 322
14.4 HTTP的相关操作 322
14.4.1 客户端发送HTTP请求 322
14.4.2 服务端处理HTTP请求 326
14.4.3 HTTP请求源码解析 328
14.4.4 提炼思考 333
14.5 数据传输过程 334
14.5.1 本地处理阶段 334
14.5.2 路由器处理阶段 335
14.5.3 目标主机处理阶段 335
14.5.4 网络地址转换(NAT)所扮演的角色 335
14.5.5 总结数据传输 336
14.6 编程范例——常见网络错误的产生及解决方案 336
14.6.1 模拟CLOSE_WAIT 336
14.6.2 模拟I/O timeout 341
14.6.3 模拟read: connection reset by peer异常 344
14.6.4 模拟TIME_WAIT 347
14.7 本章小结 351
第15章 RPC通信 352
15.1 如何理解RPC通信 352
15.2 Gob格式——利用HTTP和TCP实现RPC通信 354
15.2.1 利用HTTP实现RPC通信 354
15.2.2 HTTP实现RPC通信的原理 358
15.2.3 利用TCP实现RPC通信 370
15.2.4 利用HTTP和TCP实现RPC的区别 373
15.3 JSON格式——利用jsonrpc实现RPC通信 374
15.4 gRPC格式——利用gRPC实现RPC通信 376
15.4.1 生成RPC支持文件 377
15.4.2 gRPC调用过程 381
15.5 编程范例——基于Wireshark理解RPC通信 385
15.6 本章小结 389
第 16 章 内存管理 390
16.1 内存对齐 390
16.1.1 内存空隙 390
16.1.2 内存对齐和对齐边界 391
16.1.3 结构体的内存对齐 393
16.2 内存分级管理 395
16.2.1 分级管理的本质 395
16.2.2 Go语言内存管理的基本单位——Span 396
16.2.3 线程级别维护Span——mcache 398
16.2.4 进程级别维护Span——mcentral 398
16.2.5 堆级别维护Span——mheap 399
16.3 Go语言的垃圾回收 400
16.3.1 内存标记——双色标记法 400
16.3.2 内存标记——三色标记法 403
16.3.3 三色标记法与写屏障 405
16.3.4 垃圾回收 406
16.3.5 垃圾回收的时机 407
16.4 编程范例——unsafe包的使用 408
16.4.1 利用unsafe修改结构体字段 409
16.4.2 内存地址强制转换为结构体 411
16.4.3 并非所有内存均可修改 412
16.5 本章小结 415
第 17 章 Go语言中的正则表达式 416
17.1 正则表达式基础 416
17.1.1 正则表达式与通配符 416
17.1.2 元字符和普通字符 417
17.1.3 字符转义与字符类 417
17.1.4 字符组的使用 418
17.2 Go语言中的正则表达式 418
17.2.1 ASCII字符类 418
17.2.2 语言文字字符类 419
17.2.3 Unicode编码方式 420
17.3 Go语言中的正则表达式函数 421
17.3.1 正则表达式函数 421
17.3.2 正则表达式结构体RegExp 423
17.4 编程范例——判断行为序列 429
17.5 本章小结 430
第 18 章 深入理解Go——Plan 9汇编 431
18.1 Go汇编简介 432
18.1.1 为什么需要Go汇编 432
18.1.2 汇编文件——.s文件 432
18.1.3 .s文件的命名 432
18.1.4 .go文件和.s文件的编译 433
18.2 从内存角度看函数的调用过程 434
18.2.1 内存布局 434
18.2.2 函数执行过程 435
18.2.3 栈顶和栈底 437
18.2.4 栈内存分配与内存变量读取 437
18.3 寄存器与内存布局 439
18.3.1 通用寄存器 439
18.3.2 伪寄存器 439
18.3.3 自动分配的内存 444
18.3.4 区分通用寄存器和伪寄存器 444
18.3.5 栈帧的大小由什么决定 444
18.4 第一个Go汇编程序 445
18.4.1 利用汇编文件修改变量的值 445
18.4.2 跨包引用变量 448
18.5 利用Go汇编定义变量 449
18.5.1 全局变量和局部变量 449
18.5.2 字面量和表达式 449
18.5.3 定义字符串型变量 450
18.5.4 定义布尔型变量 453
18.5.5 定义整型变量 454
18.5.6 定义切片变量 455
18.5.7 总结变量定义 457
18.6 利用Go汇编定义函数 457
18.6.1 Go中调用汇编函数 457
18.6.2 汇编中调用Go函数 459
18.7 Go汇编中的流程控制 462
18.7.1 Go汇编中的if条件控制 462
18.7.2 Go汇编中的for循环 464
18.8 重新理解多返回值 467
18.9 编程范例——理解常用寄存器 467
18.9.1 真、伪寄存器的对比使用 467
18.9.2 验证伪寄存器SP和FP值的差异 469
18.10 本章小结 471
第19章 Gin处理HTTP请求及响应 472
19.1 Gin框架简介 472
19.2 Gin框架与HTTP请求 473
19.2.1 安装Gin框架 473
19.2.2 利用Gin框架开发第一个HTTP接口程序 473
19.3 Gin框架处理参数 475
19.3.1 获得URL查询参数 475
19.3.2 获得表单参数 476
19.3.3 获得URL路径参数 477
19.3.4 将JSON格式的参数解析为结构体 478
19.3.5 将表单参数解析为结构体 479
19.3.6 接收和处理上传文件 480
19.4 Gin框架处理响应 481
19.4.1 返回JSON格式的响应 481
19.4.2 返回XML格式的响应 483
19.4.3 返回HTML格式的响应 484
19.4.4 文件下载 486
19.4.5 自定义响应 487
19.5 Gin框架的路由处理 489
19.5.1 单个路由 489
19.5.2 路由组 489
19.5.3 Any方法 491
19.5.4 NoRoute和NoMethod方法 491
19.6 Gin框架的中间件 492
19.6.1 内置中间件 492
19.6.2 自定义中间件 494
19.7 编程范例——实现登录认证 496
19.8 本章小结 499
第20章 Go语言实现MVC项目 500
20.1 项目背景 500
20.1.1 业务背景概述 500
20.1.2 技术背景概述 501
20.1.3 项目代码结构 502
20.2 利用gorm生成MySQL数据表 502
20.2.1 定义结构体及表结构 502
20.2.2 从结构体到数据表 503
20.3 实现用户注册 506
20.4 实现用户登录 510
20.5 实现用户查询 512
20.6 实现用户删除 514
20.7 本章小结 516
第1章 第一个Go程序 1
1.1 搭建开发环境 1
1.2 一个简单的Go程序 3
1.2.1 编写第一个Go程序 3
1.2.2 运行第一个Go程序 5
1.3 环境变量说明 6
1.4 在IDE中运行Go语言程序 7
1.4.1 创建项目 7
1.4.2 创建Go程序文件 8
1.4.3 运行.go文件 9
1.5 Go语言如何实现跨平台 9
1.5.1 跨平台的准备工作 9
1.5.2 执行跨平台编译 10
1.6 探寻Go语言程序的编译执行过程 11
1.6.1 go build命令的选项 11
1.6.2 查看编译的详细过程 11
1.6.3 链接环节 13
1.7 编程范例——启动参数的使用 14
1.7.1 程序启动的入口函数 14
1.7.2 获取启动参数 15
1.8 本章小结 19
第2章 变量与常量 20
2.1 变量 20
2.1.1 变量声明 20
2.1.2 变量赋值 21
2.1.3 同时进行变量声明和赋值 23
2.1.4 多重赋值与“:=”操作符 24
2.1.5 没有多余的局部变量 25
2.1.6 全局变量 25
2.1.7 全局变量与链接 26
2.2 常量 26
2.2.1 常量的声明 26
2.2.2 常量块的使用 27
2.2.3 常量可以声明而不使用 28
2.3 iota与枚举 28
2.3.1 iota实现自增 28
2.3.2 iota计数不会中断 30
2.3.3 iota的使用场景 31
2.4 编程范例——iota的使用技巧 32
2.5 本章小结 34
第3章 简单数据类型 35
3.1 整型 35
3.1.1 声明整型变量 35
3.1.2 int和uint的设计初衷 36
3.2 浮点型 37
3.2.1 声明浮点型变量 37
3.2.2 浮点型会产生精度损失 37
3.2.3 Go语言中没有float关键字的原因 38
3.2.4 浮点型与类型推导 38
3.2.5 浮点型的比较 39
3.3 布尔类型 40
3.4 字符型 40
3.4.1 声明字符型变量 41
3.4.2 转义字符 42
3.5 字符串类型 43
3.5.1 声明字符串变量 43
3.5.2 字符串在磁盘中的存储 43
3.5.3 字符串在内存中的存储 44
3.5.4 利用rune类型处理文本 45
3.5.5 rune类型与字符集的关系 46
3.6 数组类型 46
3.6.1 声明数组变量 47
3.6.2 利用索引来访问数组元素 47
3.6.3 数组大小不可变更 48
3.6.4 数组作为函数参数 48
3.7 编程范例——原义字符的使用 49
3.8 本章小结 50
第4章 复杂数据类型 51
4.1 值类型和指针类型 51
4.1.1 值类型和指针类型的存储结构 51
4.1.2 为什么要区分值类型和指针类型 53
4.1.3 关于引用类型 54
4.2 slice(切片)的使用及实现原理 54
4.2.1 切片如何实现大小可变 54
4.2.2 切片的声明和定义 55
4.2.3 切片长度的扩展 56
4.2.4 切片容量的扩展 57
4.2.5 切片参数的复制 58
4.2.6 利用数组创建切片 60
4.2.7 利用切片创建切片 62
4.2.8 切片元素的修改 62
4.2.9 切片的循环处理 63
4.2.10 切片索引越界 63
4.2.11 总结切片操作的底层原理 64
4.3 map(映射)的使用及实现原理 65
4.3.1 声明和创建map 65
4.3.2 遍历map中的元素 65
4.3.3 元素查找与避免二义性 66
4.3.4 删除元素 67
4.3.5 map的存储结构解析 68
4.3.6 map元素的定位原理解析 70
4.3.7 map的容量扩展原理解析 72
4.4 channel(通道)的使用及实现原理 72
4.4.1 channel的使用 72
4.4.2 channel的实现原理 74
4.4.3 channel与消息队列、协程通信的对比 76
4.5 自定义结构体 76
4.5.1 自定义数据类型和自定义结构体 76
4.5.2 自定义结构体的使用 77
4.5.3 利用new创建实例 78
4.5.4 从自定义结构体看访问权限控制 79
4.5.5 自描述的访问权限 80
4.6 编程范例——结构体使用实例 80
4.6.1 利用自定义结构体实现bitmap 80
4.6.2 利用timer.Ticker实现定时任务 84
4.7 本章小结 87
第5章 流程控制 88
5.1 分支控制 88
5.1.1 if语句实现分支控制 88
5.1.2 switch语句实现分支控制 89
5.1.3 分支控制的本质是向下跳转 90
5.1.4 避免多层if嵌套的技巧 91
5.2 循环控制 94
5.2.1 for循环 94
5.2.2 for-range循环 95
5.2.3 循环控制的本质是向上跳转 97
5.2.4 循环和递归的区别 98
5.3 跳转控制 99
5.3.1 goto关键字的使用 99
5.3.2 goto的本质是任意跳转 101
5.4 编程范例——流程控制的灵活使用 101
5.4.1 for循环的误区 101
5.4.2 switch-case的灵活使用 104
5.5 本章小结 106
第6章 函数 107
6.1 函数在Go语言中的地位 107
6.1.1 Go语言中函数和方法的区别 108
6.1.2 重新理解变量声明中数据类型出现的位置 109
6.2 函数的定义 110
6.2.1 函数的参数 110
6.2.2 函数的返回值 111
6.2.3 函数多返回值的实现原理 113
6.3 函数的管理——模块和包 115
6.3.1 函数管理形式 115
6.3.2 模块与文件夹 116
6.3.3 本地包管理 119
6.3.4 模块名与文件夹名称 121
6.3.5 代码规范的意义 123
6.4 函数的调用和执行 123
6.4.1 包的别名与函数调用 123
6.4.2 init()函数与隐式执行顺序 125
6.4.3 利用init()函数执行初始化 126
6.4.4 利用匿名包实现函数导入 127
6.5 将函数作为变量使用 128
6.5.1 将函数赋值给变量 128
6.5.2 函数赋值给变量的应用场景 129
6.6 匿名函数和闭包 132
6.6.1 为什么需要匿名函数 132
6.6.2 闭包 134
6.7 函数的强制转换 137
6.7.1 从数据类型的定义到函数类型的定义 137
6.7.2 从数据类型的强制转换到函数类型的强制转换 138
6.7.3 函数类型及强制转换的意义 138
6.7.4 利用强制转换为函数绑定方法 140
6.8 编程范例——闭包的使用 142
6.8.1 闭包封装变量的真正含义 142
6.8.2 利用指针修改闭包外部的变量 145
6.9 本章小结 146
第7章 异常处理 147
7.1 异常机制的意义 147
7.2 Go语言中的异常 150
7.2.1 创建异常 150
7.2.2 抛出异常 151
7.2.3 自定义异常 152
7.3 异常捕获 154
7.3.1 利用延迟执行机制来捕获异常 155
7.3.2 在上层调用者中捕获异常 157
7.3.3 异常捕获的限制条件 158
7.4 异常捕获后的资源清理 159
7.4.1 未正常释放锁对象带来的副作用 160
7.4.2 确保锁对象释放的正确方式 162
7.5 编程范例——异常的使用及误区 163
7.5.1 利用结构体自定义异常 163
7.5.2 未成功捕获异常,导致程序崩溃 164
7.6 本章小结 166
第8章 Go语言的面向对象编程 167
8.1 面向对象编程的本质 167
8.2 Go语言实现封装 168
8.2.1 Go语言中字段和方法的封装 168
8.2.2 为值类型和指针类型绑定方法的区别 169
8.3 Go语言实现继承 171
8.3.1 利用组合实现继承 171
8.3.2 匿名字段的支持 173
8.3.3 多继承 174
8.4 Go语言实现多态 176
8.5 面向接口编程 178
8.5.1 Go语言中的接口 179
8.5.2 Go语言中的接口实现 179
8.5.3 利用面向接口编程实现方法多态 180
8.6 编程范例——接口的典型应用 181
8.6.1 接口嵌套实例 181
8.6.2 伪继承与接口实现 183
8.7 本章小结 184
第9章 并发 185
9.1 线程的概念 185
9.2 线程模型 187
9.3 协程的工作原理 187
9.3.1 协程的使用 188
9.3.2 GPM模型 189
9.3.3 从3种线程模型看GOMAXPROCS参数 191
9.4 Go语言中的协程同步 192
9.4.1 独占锁——Mutex 192
9.4.2 读写锁——RWMutex 195
9.4.3 等待组——WaitGroup 198
9.5 利用channel实现协程同步 199
9.5.1 利用channel实现锁定 200
9.5.2 利用channel实现等待组 202
9.5.3 总结使用channel实现并发控制 204
9.6 让出时间片 204
9.6.1 time.Sleep()和runtime.Gosched()的本质区别 204
9.6.2 runtime.Gosched()与多核CPU 205
9.7 Go语言中的单例 206
9.7.1 利用sync.Once实现单例 206
9.7.2 sync.Once的实现原理 208
9.8 编程范例——协程池及协程中断 209
9.8.1 协程池的实现 209
9.8.2 协程的中断执行 213
9.9 本章小结 217
第10章 上下文 218
10.1 上下文和普通参数的区别 218
10.2 上下文树 219
10.2.1 上下文接口——Context 219
10.2.2 利用context.emptyCtx创建树的根节点 219
10.2.3 上下文树的构建 220
10.3 利用valueCtx实现信息透传 222
10.3.1 valueCtx用于参数传递 222
10.3.2 从父节点获得透传值 223
10.4 利用cancelCtx通知协程终止执行 224
10.4.1 通知子协程终止执行 225
10.4.2 通知子协程的实现过程 226
10.4.3 为什么需要取消函数 230
10.5 利用timerCtx实现定时取消 230
10.5.1 调用context.WithDeadline()创建定时器上下文 231
10.5.2 调用context.WithTimeout()创建定时器上下文 233
10.6 编程范例——上下文的典型应用场景 234
10.6.1 利用结构体传递参数 234
10.6.2 valueContext为什么需要key 236
10.6.3 利用cancelCtx同时取消多个子协程 237
10.7 本章小结 239
第11章 反射 240
11.1 反射的意义 240
11.2 反射的API 241
11.2.1 利用reflect.TypeOf()来获得类型信息 241
11.2.2 利用reflect.Type.Kind()方法来获取类型的具体分类 242
11.2.3 利用reflect.Type.Element()方法来获取元素类型 243
11.2.4 类型断言的用法与局限性 245
11.3 值信息 246
11.3.1 利用reflect.ValueOf()来获得值信息 246
11.3.2 利用reflect.Value.Kind()来获得值的分类信息 247
11.3.3 利用reflect.Value.Elem()来获得值的元素信息 248
11.3.4 利用反射访问和修改值信息 249
11.3.5 利用反射机制动态调用方法 252
11.4 编程范例——动态方法调用 255
11.5 本章小结 258
第12章 泛型 259
12.1 泛型的意义 259
12.2 泛型应用到函数 261
12.2.1 泛型函数的使用 261
12.2.2 泛型中的隐含信息 262
12.2.3 避免类型强制转换 263
12.2.4 泛型类型的单独定义 264
12.3 泛型导致接口定义的变化 265
12.3.1 接口定义的变化 265
12.3.2 空接口的二义性 266
12.3.3 接口类型的限制 266
12.4 泛型类型应用到receiver 268
12.4.1 泛型类型不能直接用于定义receiver 268
12.4.2 间接实现泛型定义receiver 269
12.5 编程范例——自定义队列的实现 270
12.6 本章小结 272
第13章 I/O 273
13.1 Reader和Writer 273
13.1.1 理解Reader和Writer 273
13.1.2 Reader和Writer接口 274
13.1.3 Go语言的I/O API要解决的问题 275
13.1.4 文件读取 275
13.1.5 文件写入 278
13.1.6 文件权限与umask 281
13.1.7 一次性读写 283
13.2 缓冲区读写 284
13.2.1 bufio中的Reader和Writer 285
13.2.2 利用bufio实现按行读取 285
13.3 字符串数据源 287
13.3.1 strings.Reader解析 287
13.3.2 字节扫描器ByteScanner 288
13.3.3 按Rune读取UTF-8字符 289
13.4 bufio.Scanner的使用 292
13.4.1 扫描过程及源码解析 292
13.4.2 扫描时的最大支持 298
13.4.3 扫描时的最小容忍 301
13.5 编程范例——文件系统相关操作 303
13.5.1 查看文件系统 303
13.5.2 临时文件 305
13.6 本章小结 307
第14章 网络编程 308
14.1 网络连接的本质 308
14.2 利用TCP实现网络通信 310
14.2.1 创建TCP连接 310
14.2.2 利用TCP连接进行消息传递 312
14.3 利用UDP实现网络通信 315
14.3.1 监听模式 316
14.3.2 拨号模式 319
14.3.3 总结监听模式和拨号模式 322
14.4 HTTP的相关操作 322
14.4.1 客户端发送HTTP请求 322
14.4.2 服务端处理HTTP请求 326
14.4.3 HTTP请求源码解析 328
14.4.4 提炼思考 333
14.5 数据传输过程 334
14.5.1 本地处理阶段 334
14.5.2 路由器处理阶段 335
14.5.3 目标主机处理阶段 335
14.5.4 网络地址转换(NAT)所扮演的角色 335
14.5.5 总结数据传输 336
14.6 编程范例——常见网络错误的产生及解决方案 336
14.6.1 模拟CLOSE_WAIT 336
14.6.2 模拟I/O timeout 341
14.6.3 模拟read: connection reset by peer异常 344
14.6.4 模拟TIME_WAIT 347
14.7 本章小结 351
第15章 RPC通信 352
15.1 如何理解RPC通信 352
15.2 Gob格式——利用HTTP和TCP实现RPC通信 354
15.2.1 利用HTTP实现RPC通信 354
15.2.2 HTTP实现RPC通信的原理 358
15.2.3 利用TCP实现RPC通信 370
15.2.4 利用HTTP和TCP实现RPC的区别 373
15.3 JSON格式——利用jsonrpc实现RPC通信 374
15.4 gRPC格式——利用gRPC实现RPC通信 376
15.4.1 生成RPC支持文件 377
15.4.2 gRPC调用过程 381
15.5 编程范例——基于Wireshark理解RPC通信 385
15.6 本章小结 389
第 16 章 内存管理 390
16.1 内存对齐 390
16.1.1 内存空隙 390
16.1.2 内存对齐和对齐边界 391
16.1.3 结构体的内存对齐 393
16.2 内存分级管理 395
16.2.1 分级管理的本质 395
16.2.2 Go语言内存管理的基本单位——Span 396
16.2.3 线程级别维护Span——mcache 398
16.2.4 进程级别维护Span——mcentral 398
16.2.5 堆级别维护Span——mheap 399
16.3 Go语言的垃圾回收 400
16.3.1 内存标记——双色标记法 400
16.3.2 内存标记——三色标记法 403
16.3.3 三色标记法与写屏障 405
16.3.4 垃圾回收 406
16.3.5 垃圾回收的时机 407
16.4 编程范例——unsafe包的使用 408
16.4.1 利用unsafe修改结构体字段 409
16.4.2 内存地址强制转换为结构体 411
16.4.3 并非所有内存均可修改 412
16.5 本章小结 415
第 17 章 Go语言中的正则表达式 416
17.1 正则表达式基础 416
17.1.1 正则表达式与通配符 416
17.1.2 元字符和普通字符 417
17.1.3 字符转义与字符类 417
17.1.4 字符组的使用 418
17.2 Go语言中的正则表达式 418
17.2.1 ASCII字符类 418
17.2.2 语言文字字符类 419
17.2.3 Unicode编码方式 420
17.3 Go语言中的正则表达式函数 421
17.3.1 正则表达式函数 421
17.3.2 正则表达式结构体RegExp 423
17.4 编程范例——判断行为序列 429
17.5 本章小结 430
第 18 章 深入理解Go——Plan 9汇编 431
18.1 Go汇编简介 432
18.1.1 为什么需要Go汇编 432
18.1.2 汇编文件——.s文件 432
18.1.3 .s文件的命名 432
18.1.4 .go文件和.s文件的编译 433
18.2 从内存角度看函数的调用过程 434
18.2.1 内存布局 434
18.2.2 函数执行过程 435
18.2.3 栈顶和栈底 437
18.2.4 栈内存分配与内存变量读取 437
18.3 寄存器与内存布局 439
18.3.1 通用寄存器 439
18.3.2 伪寄存器 439
18.3.3 自动分配的内存 444
18.3.4 区分通用寄存器和伪寄存器 444
18.3.5 栈帧的大小由什么决定 444
18.4 第一个Go汇编程序 445
18.4.1 利用汇编文件修改变量的值 445
18.4.2 跨包引用变量 448
18.5 利用Go汇编定义变量 449
18.5.1 全局变量和局部变量 449
18.5.2 字面量和表达式 449
18.5.3 定义字符串型变量 450
18.5.4 定义布尔型变量 453
18.5.5 定义整型变量 454
18.5.6 定义切片变量 455
18.5.7 总结变量定义 457
18.6 利用Go汇编定义函数 457
18.6.1 Go中调用汇编函数 457
18.6.2 汇编中调用Go函数 459
18.7 Go汇编中的流程控制 462
18.7.1 Go汇编中的if条件控制 462
18.7.2 Go汇编中的for循环 464
18.8 重新理解多返回值 467
18.9 编程范例——理解常用寄存器 467
18.9.1 真、伪寄存器的对比使用 467
18.9.2 验证伪寄存器SP和FP值的差异 469
18.10 本章小结 471
第19章 Gin处理HTTP请求及响应 472
19.1 Gin框架简介 472
19.2 Gin框架与HTTP请求 473
19.2.1 安装Gin框架 473
19.2.2 利用Gin框架开发第一个HTTP接口程序 473
19.3 Gin框架处理参数 475
19.3.1 获得URL查询参数 475
19.3.2 获得表单参数 476
19.3.3 获得URL路径参数 477
19.3.4 将JSON格式的参数解析为结构体 478
19.3.5 将表单参数解析为结构体 479
19.3.6 接收和处理上传文件 480
19.4 Gin框架处理响应 481
19.4.1 返回JSON格式的响应 481
19.4.2 返回XML格式的响应 483
19.4.3 返回HTML格式的响应 484
19.4.4 文件下载 486
19.4.5 自定义响应 487
19.5 Gin框架的路由处理 489
19.5.1 单个路由 489
19.5.2 路由组 489
19.5.3 Any方法 491
19.5.4 NoRoute和NoMethod方法 491
19.6 Gin框架的中间件 492
19.6.1 内置中间件 492
19.6.2 自定义中间件 494
19.7 编程范例——实现登录认证 496
19.8 本章小结 499
第20章 Go语言实现MVC项目 500
20.1 项目背景 500
20.1.1 业务背景概述 500
20.1.2 技术背景概述 501
20.1.3 项目代码结构 502
20.2 利用gorm生成MySQL数据表 502
20.2.1 定义结构体及表结构 502
20.2.2 从结构体到数据表 503
20.3 实现用户注册 506
20.4 实现用户登录 510
20.5 实现用户查询 512
20.6 实现用户删除 514
20.7 本章小结 516
猜您喜欢