常用小知识点总结:
1、GO函数执行顺序,如果有init方法,实质先执行的init,后执行main.main;如果同一个文件中有多个init函数,则按照init出现的顺序执行
2、defer关键字相当于执行函数的善后工作,等函数执行完了再执行defer中的内容;defer执行过程相当于执行闭包函数,调用外层变量相当于引用传递,如果有需要,可以将defer后的body体更改为函数值传递
3、GO语言不支持继承,通过在结构体内置匿名成员来实现继承
4、如果需要虚函数的多态特性,则需要借助GO语言的接口来实现
5、unit属于无符号类型,int属于有符号(正负)类型
6、goroutine执行数据需要同步处理的话使用管道channel,发送和接受操作通常都在不同的goroutine中发生,如果在同一个goroutine中操作,很容易导致死锁,无缓存的发送操作总在接受操作完成前发生;如果需要异步处理的话,使用sync.WaitGroup,sync.WaitGroup内部有个计数器,最初从0开始,它有三个方法:Add(),Done(),Wait()用来控制计数器的数量;
如果没有设置容量,或者容量设置为0, 说明Channel没有缓存,只有sender和receiver都准备好了后它们的通讯(communication)才会发生(Blocking)。如果设置了缓存,就有可能不发生阻塞, 只有buffer满了后 send才会阻塞, 而只有缓存空了后receive才会阻塞;一个nil channel不会通信。
done := make(chan int) // 不带缓存的管道,是阻塞的,goroutine只能顺序阻塞执行
done := make(chan int, 1) // 带缓存的管道,goroutine执行时不会阻塞,可以实现真正意义的并发
7、sync/atomic 对于并发操作而言,原子操作是个非常现实的问题。典型的就是 i++ 的问题。 当两个 CPU 同时对内存中的 i 进行读取,然后把加一之后的值放入内存中,可能两次 i++ 的结果,这个 i 只增加了一次。 如何保证多 CPU 对同一块内存的操作是原子的;golang 中 sync/atomic 就是做这个使用的;使用原子操作对数值进行运算时,不用加解锁。
8、make函数创建一个指定元素类型、长度和容量的slice。
9、sync.Once:如果有的数据初始化不需要在程序启动的时候进行,则可以优化在程序运行过程中初始化,但是多goroutine情况下,可能存在调用时数值还没被初始化的情况;解决此问题的策略一个是加锁,但是性能比较低下,另外一个是使用sync.Once.do(data),如果没有被初始化的话,只初始化一次,是比较优的方案。
sync.Once:内部实现采用了双层校验机制,初始化时,先判断初始化校验判断,如果值不为1,则加锁,初始化,校验位值存储为1,,否则直接返回不用执行初始化操作
10、GO语言数组当做函数参数传递时相当于值传递,切片当做函数参数传递时,属于引用传递;切片本身不存储值,只是对底层数组的引用,对切片的拷贝相当于对同一个底层数组的多个引用;向函数传递一个数组指针参数,并在函数内修改数组。尽管它是有效的,但却不是 Go 语言惯用的实现方式。我们最好使用切片来处理,如a[:]。
以下是常用基础知识汇总:
变量格式化
%d 十进制整数
%x, %o, %b 十六进制,八进制,二进制整数。
%f, %g, %e 浮点数: 3.141593 3.141592653589793 3.141593e+00
%t 布尔:true或false
%c 字符(rune) (Unicode码点)
%s 字符串
%q 带双引号的字符串"abc"或带单引号的字符'c'
%v 变量的自然形式(natural format)
%T 变量的类型
%% 字面上的百分号标志(无操作数)
内建的常量、类型和函数
内建常量: true false iota nil # iota用来定义自增长的常量
内建类型: int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64
bool byte rune string error # rune使用方式 []rune(str),转化成Unicode码点,一般用来中文拆分方案
内建函数: make len cap new append copy close delete
complex real imag panic recover
make、new操作
make: 用于内建类型(map、slice 和channel)的内存分配,并且返回一个有初始值(非零)的T类型,而不是*T
new: new用于各种类型的内存分配,new(T)分配了零值填充的T类型的内存空间,并且返回其地址,即一个*T类型的值
map使用
ages := make(map[string]int) 创建map
delete(ages, "alice") 即使key不存在也不会报错
for name, age := range ages map元素遍历
注意:1、map不可以取地址,因为随着元素数据量的增大,从而分配更大的内存空间,导致原有的map地址失效
2、map中的元素使用range循环遍历结果顺序会不断发生变化
struct匿名成员
结构体匿名成员的嵌入,使得被嵌入的成员属性直接可以在嵌入结构体直接使用,也就是GO的组合特性,用来代替继承
结构体中无法嵌入结构体本身,但是可以嵌入结构体指针,此种方案可以实现树结构以及链表
encoding/json
type Movie struct {
Title string
Year int `json:"released"` 后面的标记是结构体成员tag,在转化为json后,成员名字可以由后面的替换
Color bool `json:"color,omitempty"`
Actors []string
}
data, err := json.Marshal(movies) 将movies结构体转化为json格式,相当于一个list里面包含字典的长的字符串,里面没有空白隔离符号
data, err := json.MarshalIndent(movies, "", " ") 以友好的形式格式化转码json
movies := []Movie{}
err := json.Unmarshal(data, &movies) 将json解码为go结构体
err := json.NewDecoder(resp.Body).Decode(&result) 输入流解码
err := json.NewEncoder().Encode() 输出流编码