导图社区 Go语言
极客时间课程《Go语言从入门到实战》学习笔记:从基础语法(变量、运算符、循环)到复合类型(切片、map、字符串),深入函数式编程(一等公民、可变参数)和面向对象特性(接口多态、封装)重点解析并发编程(协程、对象池)、异常处理(panic/recover)及测试方法(单元测试、BDD)。最后探讨架构模式(微内核、管道过滤)和底层能力(反射、unsafe)带你系统掌握Go的设计哲学与实践精髓"。
编辑于2025-08-11 11:30:55极客时间课程《Go语言从入门到实战》学习笔记:从基础语法(变量、运算符、循环)到复合类型(切片、map、字符串),深入函数式编程(一等公民、可变参数)和面向对象特性(接口多态、封装)重点解析并发编程(协程、对象池)、异常处理(panic/recover)及测试方法(单元测试、BDD)。最后探讨架构模式(微内核、管道过滤)和底层能力(反射、unsafe)带你系统掌握Go的设计哲学与实践精髓"。
《七步读懂财务报表》 读书笔记,涉及了企业财务报表的多个方面,包括资产负债表、损益表、现金流量表、合并报表、内部报表、报表附注以及防范报表失真等内容。该书简明扼要,重点突出,实践性强,既适合企业的各级管理人员阅读,也适合欲学习、掌握企业财务会计知识的各类读者阅读。
社区模板帮助中心,点此进入>>
极客时间课程《Go语言从入门到实战》学习笔记:从基础语法(变量、运算符、循环)到复合类型(切片、map、字符串),深入函数式编程(一等公民、可变参数)和面向对象特性(接口多态、封装)重点解析并发编程(协程、对象池)、异常处理(panic/recover)及测试方法(单元测试、BDD)。最后探讨架构模式(微内核、管道过滤)和底层能力(反射、unsafe)带你系统掌握Go的设计哲学与实践精髓"。
《七步读懂财务报表》 读书笔记,涉及了企业财务报表的多个方面,包括资产负债表、损益表、现金流量表、合并报表、内部报表、报表附注以及防范报表失真等内容。该书简明扼要,重点突出,实践性强,既适合企业的各级管理人员阅读,也适合欲学习、掌握企业财务会计知识的各类读者阅读。
Go
9 - 并发编程
Groutine: 1. Groutine Stack初始化为2K , Java Thread Stack 默认为1M 2. Groutine和Kernel Space Entity的对应关系是M:N,Java Thread是1:1
共享内存并发机制
sync.Mutex + sync.WaitGroup wg.Add() wg.Done() wg.Wait() 控制协程在何时等待
WaitGroup
创建 var wg sync.WaitGroup
添加计数 wg.Add(1) 使用 Add 方法设置要等待的 goroutine 的数量。通常在启动 goroutine 之前调用。
启动goroutine:启动 goroutine,并在完成时调用 Done 方法,表示该 goroutine 完成了任务。 go func( ) { defer wg.Done() // 确保在函数结束时调用 Done // do something }( )
等待完成:在主 goroutine 中,调用 Wait 方法,等待所有添加的 goroutine 完成。 wg.Wait( ) // 阻塞,直到所有 goroutine 完成
CSP(Communicating Sequential Process) & 异步返回 CSP是一种并发计算模型,由Tony Hoare于1978年提出。它是一种基于进程间通信的并发编程范式,用于描述并发系统中的并发进程之间的交互和通信。 CSP的核心思想是将并发系统建模为一组独立运行的顺序进程,这些进程通过消息传递进行通信。每个进程都是顺序执行的,它们通过发送和接收消息来进行通信和同步。进程之间的通信是通过共享的通道(Channel)进行的,进程可以向通道发送消息,也可以从通道接收消息。
channel的声明,放入取出消息
声明:retCh := make(chan string, buffer_size) 放入:retCh <- ret 取出:<-retCh
无buffer,放入后即阻塞,取出消息时继续运行 有buffer,当放入消息时buffer满时才会阻塞
select 多渠道选择消息 与 超时控制
channel关闭与广播
向channel发送完消息可以关闭channel,向关闭的channel发送数据,会导致panic
v, ok <-ch; 从通道取出消息,ok为bool值,true表示正常接收,false表示通道关闭 v<-ch;如果通道中没有消息了,取出的值是对应通道类型的0值
所有的channel接收者都会在channel关闭时,立刻从阻塞等待中返回且上述ok值为false。此广播机制常被利用向多个订阅制同时发送信号,如:信号退出时
context管理树形层级协程任务
根Context: context.Background() 子Context: context.WithCancel(parentContext) 接收取消通知 <-ctx.Done() 当前Context被取消时,基于他的子context都会被取消
ctx, cancel := context.WithCancel(context.Background()) 返回的cancel是个函数,通过调用cancel取消当前的context执行的task 返回的ctx为当前context
10 - 典型并发任务
单例模式(懒汉式,线程安全)
Once.Do可以保证func只执行一次
仅需任意任务完成
return <-ch的时取出消息,先return的协程就是执行第一个执行完指定任务的协程
要声明channel的buffer大小,否则其余的协程会阻塞在放入消息时
所有任务完成
对象池
sync.pool 对象缓存
适用于通过复用,降低复杂对象的创建和GC代价
协程安全,会有锁的开销
生命周期受GC影响,不适合于做连接池等,需自己管理生命周期的资源的池化
11 - 测试
单元测试
t.Error("Error") , Fail: 该测试失败,该测试继续,其他测试继续执⾏
t.Fatal("Fatal") , FailNow 该测试失败,该测试中⽌,其他测试继续执⾏
代码覆盖率 go test -v -cover
assert
go get github.com/stretchr/testify/assert
assert.Equal(t, expected_value, value)
Benchmark
go test -bench=. -benchmem
windows下使用-bench="."
代码被运行的次数,单次的运行时间,B/op:每次操作分配的内存字节数,allocs/op:每次操作分配内存的次数
BDD(Behavior Driven Development)
go get -u github.com/smartystreets/goconvey/convey
. ,表示将 import 进来的 package 的方法是在当前命名空间的,可以直接使用。
12 - 反射和Unsafe
反射编程
reflect.TypeOf 返回类型 reflect.Type reflect.ValueOf 返回值 reflect.Value 通过 t.Kind( ) 来判断类型
e := &Employee{"1", "Mike", 30} 按名字访问结构的成员 reflect.ValueOf(*e).FieldByName("Name") 按名字访问结构的⽅法 reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(28), reflect.ValueOf(27)})
访问StructTag
万能程序
DeepEqual
map和slice不能用 运算符比较,但是可以通过reflect.DeepEqual(e1,e2)进行比较
使用反射的取舍
提高程序灵活性,复用性
降低程序的可读性和性能
不安全编程
危险的unsafe操作会改变 变量的值
f = 5e-323
合理的类型转换是允许的
原子类操作 atomic.StorePointer atomic.LoadPointer
13 - 常见架构模式的实现
Pipe - Filter
Filter + 组合模式
Micro Kernel
特点
易于扩展
错误隔离
保持架构一致性
要点
内核包含公共流程或通用逻辑
将可变或可扩展部分规划为扩展点
抽象扩展点行为,定义接口
利用插件进行扩展
8 - 包与依赖管理
package
基本复用模块单元:以首字母大写来表明可被包外代码访问
代码的package可以和所在的目录不一致
同一目录里的Go代码的package要保持一致
init方法
在main执行钱,所有依赖的package的init方法都会被执行
不同包的init函数按照包导入的依赖关系决定执行顺序
每个包可以有多个init函数
包的每个源文件也可以有多个init函数
remote package
go get -u https://github.com/easierway/concurrent_map
依赖管理
go尚未解决的问题: 1、同一环境下,不同项目可以使用同一包的不同版本 2、无法管理对包的特定版本的依赖,go get 无法指定版本
解决方法:Go 1.5 引入vendor路径 查找依赖包路径的解决方案如下: 1、当前包下vendor目录 2、向上级目录查找,知道找到src下的vendor目录 3、在GOPATH下面查找依赖包 4、在GOROOT目录下查找
常用依赖管理工具:godep、glide、dep
7 - 异常处理
Go 错误机制
Go没有异常机制
error类型实现了error接口
可以通过errors.New来快速创建错误实例
panic和recover
panic
panic用于不可恢复的错误 panic退出前会执行defer函数
os.Exit退出不会调用defer函数 os.Exit退出时不输出当前调用栈信息
recover
不要盲目使用recover Let is Crash 可能是恢复不确定性错误的最好方法
6 - 面向对象编程
Is Go an object-oriented language?
Yes and no. Although Go has types and methods and allows an objectoriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.
封装数据和行为
封装数据
type Employee struct { Id string Name string Age int }
3种实例创建及初始化 e1 := Employee{"0", "Bob", 20} e2 := Employee{Name: "Mike", Age: 30} e3 := new(Employee) e3.name = "Rose" e3处返回的指针,即 e := &Employee{} C++的 ->和dot 用法在 go 中统一用dot
行为 (方法) 定义
第一种 :在实例对应方法被调用时,实例的成员会进行复制 func (e Employee) String() string { ...}
第二种 :通常情况下为了避免内存拷贝使用此种方式 func (e *Employee) String() string { ...
unsafe.Pointer(ptr_name) 用于输出地址
定义交互协议
Duck Type式接口实现: Go 的接口并不需要显式地声明某个类型实现了接口,而是基于类型是否实现了接口中的方法来决定。这种方式也称为 “隐式接口实现”,类似于鸭子类型的概念,即 "如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子"。
隐式实现:Go 中不需要在类型上显式声明实现某个接口。只要一个类型实现了接口的所有方法,这个类型就可以被视为实现了该接口。
动态性:Go 的 Duck Typing 让开发者无需显式声明某个类型的实现,减少了编译时的依赖,并允许更加灵活的代码设计。
接口定义: type Programmer interface{ WriteHelloWorld() string }
接口实现: type GoProgrammer struct { } func (p *GoProgrammer) WriteHelloWorld() string { return "fmt.Println(\"Hello World!\")" }
Go接口与其他编程语言的差异
1、接口为非入侵式,实现不依赖于接口定义
2、所有接口的定义可以包含在接口使用者包内
扩展与复用(多态)
type Pet struct { } func (p *Pet) Speak() { fmt.Print("Hello!") } type Dog struct { Pet } dog := new(Dog) dog.Speak()可以调用Pet的方法,如果Dog实现了Speak()则会调用Dog的 但不能像java那样通过Dog类型实例化一个Pet,不支持LSP原则 var dog *Pet = new(Dog)
匿名类型嵌入
它不是继承,如果我们把“内部 struct ”看作⽗类,把“外部 struct” 看作⼦类,有如下问题: 1、不⽀持⼦类替换 2、⼦类并不是真正继承了⽗类的⽅法,⽗类的定义的⽅法⽆法访问⼦类的数据和⽅法
空接口与断言
空接口可以表示任何类型
通过断言来将空接口转换为指定类型 v, ok := p.(int) // ok=true时转换成功
Go接口最佳实践
5 - 函数
函数是一等公民
可以有多个返回值
所有参数都是值传递:slice、map、channel会有传引用的错觉
传的是指针的值
函数可以作为变量的值
函数可以作为参数和返回值
可变长参数 ...int 代表传入n个int值
defer函数
延迟执行函数,在函数返回前执行
可用于清理资源,释放锁
有panic仍会执行
defer func( ) 匿名defer defer func_name( ) 显示defer
4 - 字符串
go的string特点
string是数据类型,不是引用或指针类型其0值不是nil,而是空字符串""
string是只读的byte类型的slice,不可修改 len函数返回它所包含的byte数,和有多少字符无关系
string的byte数组可以存放任何数据
Unicode和UTF-8
Unicode是一种字符集(code point)
UTF-8是unicode的存储实现()转换为字节序列的规则)
rune是一种数据类型,可以取出字符串的Unicode c := []rune(s)
pkg
strings
strings.Split
strigns.Join
strconv
strconv.Atoi
strconv.Itoa
3 - 常用集合
数组和切片 类似于C++中的array和verctor
数组
数组截取,和python 列表切片语法类似,但不支持负索引 a[开始索引(包含), 结束索引(不包含)]
a := [...]int{1, 2, 3, 4, 5} 使用...可以自动推导数组长度
切片
声明时不能指定长度 切片动态扩容,类似C++ vector
三个函数 len、cap、append
map
初始化m := map[key_type] value_type {} m := map[string] int {} ,value 可以是函数
通过map[type]bool 结构实现set add : mySet[add_value]=true del : delete(mySet, del_value)
在访问的 Key 不存在时,仍会返回零值,不能通过返回 nil 来判断元素是否存在
2 - 基本程序结构
变量与常量
变量
自定类型推导 一个赋值语句中可以对多个变量同时赋值
常量
快速设置连续常量值 const ( Open = 1 << iota Close Pending )
var 与短变量声明 := 的区别
:= 只能用于函数内部,不能声明全局变量,且必须初始化变量
var 可以在函数外部和函数内部使用,而 := 仅限函数内部
数据类型
别名type MyInt int64,同C++
不允许隐式类型转换,包括别名和原有类型也不能进行隐式类型转换
指针类型不支持指针运算
string是值类型,默认值的初始化为空字符串,不是nil
运算符
不支持前置++,--,只支持后置写法
位运算符,独有的 &^ 如果&^右侧为1,左边无论是什么,结果都是0, 如果&^右侧是0,左边是什么结果就是什么
条件和循环
if 及 简短 if if v := x-100; v<0 { return v }
for 基础for循环 简写for循环 无限循环
switch 空分支 默认分支 fallthrough关键字
for-range 遍历数组、切片、字符串、Map等 for key, value := range Map{ ... }
1 - Go语言基础
Go语言简介
起源于2007年 by Rob Pike Ken Thompson Robert Griesemer
25个关键字
基本程序结构
package + import + func
程序入口:必须是main包和main()函数,文件名不要求
退出返回值:main函数不支持任何返回值,通过os.Exit来返回状态
命令行参数获取:main函数不支持传入参数,直接通过os.Args获取命令行参数