数据类型
int/uint类型位数不是固定的,根据运行平台可能是32位或64位 获取Int类型位数:strconv.IntSize
Go不允许隐式类型转换,只能强制转换 强制转换:int32(a)
float32/float64,分别精确到小数点后7/15位
byte别名uint8
string是指类型,默认值是空字符串,而不是nil
指针
Go不支持指针运算,指针保存了值的内存地址。
& 操作符会生成一个指向其操作数的指针,* 操作符表示指针指向的底层值
go语言只有值传递,没有引用传递(方法参数为指针时,同样是值传递,因为传递的是指针地址) 函数调用时,传变量,是copy了一份变量,传到函数使用,函数内修改不影响原变量值;传指针时,是copy了一份指针,而不是引用传递。
当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数值的时候,调用函数中相应实参不会发生任何变化,因为数值变化只作用在copy上。 传指针比较较量级(8bytes),只是传内存地址,我们可以用指针传递体积较大的结构体。如果用参数值传递的话,在每次copy上就会花费相对较多的系统开销(内存和时间)。所以当要传递大的结构体时,用指针是一个明智的选择。
指针变量指向一个值的内存地址,*用于指定变量是作为一个指针;数组不用加*来操作数组,go可以自动识别
Go语言中string,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递(注:若函数需改变slice长度,仍需要取地址传递指针)
常量
Go中常量一般不用大写,因为大写表示public
常量定义但不使用,不会报错
iota是常量的计数器,从0开始,组中每定义1个常量自动递增1;每遇到一个const关键字,iota就会重置为0
运算符
++和–,在go语言中是语句而不是表达式,所以不能放在等号右边,只能做为单独一行
位移
<<,左移n位,即等以2的n次方 >>,右移n位,即除以2的n次方
PS:1 « 10 = 1024,即1K;1 « 20 = 1048576,即1M
条件语句
switch语句,case后面不用加break 默认case匹配成功后,不地执行其他case,如果需要执行后面case,可以使用fallthrough,此时会进行下一项case,且不做条件判断!!!
函数
在一个结构体内,最好统一接收者为指针实例,因为:
- 指针实例可以调用接受者是指针的函数和接受者为实例的函数;普通实例(非指针实例)只能调用接受者实例的函数,而不能调用接受者是指针的函数
- 以interface做为类型判定的时候,结构体内实现interface的函数同时存在指针实例和普通实例的情况下,不能被正确识别类型
数组和切片
切片是数组的连续片断引用
初始化[]表示切片类型,不需要指定大小 也可以用make初始化,make([]T, length, capacity),len为切片的实际长度,capacity为切片第1个元素在底层数组的位置,到底层数组结尾位置的长度
切片自动扩容规则:容量小于1024,则每次*2;大于等于1024,每次增加1/4 此时数组会还原,切片指向分配的新数组,与原数组无关联
数组初始化,可以用:[3]{0:1, 1:3, 2:4}的方式,相当于{1,3,4}
slice是引用类型,底层是数组。一般用make创建(new返回的是指针,则实际是指向指针的指针,基本没意义) 数组是值类型,不是引用类型。被当成方法参数时,传递的是值的copy,不是地址
参数为…形式的是值传递,区别于传slice参数的地址传递
fmt
fmt占位符:https://golang.org/pkg/fmt/#hdr-Printing
类型断言
语法:value, ok := em.(T)
- em代表要判断的变量,必须是interface类型
- T代表被判断的类型,如果是em指针类型,则T需要改为*T
- value代表返回的值
- ok代表是否为改类型
或者不传入具体类型,用type替代,让系统判断,如:value, ok := em.(type)
经常与switch一起组合使用
defer
defer在方法调用结束后,return调用前执行 多个defer同时存在时,是以栈的形式,先进后出,FILO 多用于释放资源(个人理解应该是确保释放资源,因为资源不用应该及时释放,而不是依赖于defer,defer的存在只是为了确保方法完成后资源会释放)
defer定义的操作,在发生error和panic时仍然会被执行,调用panic之后定义的defer不会运行,因为未入栈
os.Exit调用后,defer不会运行
错误处理
结构体实现了Error()时,对于返回error的,可以直接返回结构体。因为ducker typing
结构体
go语言中的方法是作用在特定类型的变量上,因此自定义的类型都可以有方法,不仅仅是在结构体中
WaitGroup
不用初始化就能使用,但是!如果要当参数传递,必须是指针!!!这里采过坑!因为Go只有值传递,所以必须传指针,指向同一个实例;否则会视为两个不同实例
map
删除map键值对,使用delete函数 map可用sort排序
包变量和结构体类型的区别
包变量类型于静态变量,不属于任何实例;结构体成员相当于类的成员变量
new/make/struct{}的区别
make 和 struct{} 一样,返回的都是值; new 返回的是指针,make返回初始化后的(非零)值 make 只能用于 map、slice、channel 的创建值引用, struct 只能用 struct{} 来创建值引用
关于文件夹名和包名
require -> mod name
import -> [mod name / ] package
关于交叉编译
|
|
init方法的坑
只要有某个程序文件(*.go)显式引用了某个带init()方法的包,那么即使该程序文件并没有被整个程序任何地方使用过(比如结构体初始化),那么也会触发被引用包的init()方法。 举例来说:公共包common在database/redis的package下有一个redis.go文件中有init()方法,引用common包的程序a,有一个程序文件b.go,在b.go中import 了common包的database/redis,那么即使该程序文件b.go在程序a中并没有被使用过,common包的database/redis的init方法仍然会执行。很奇怪!