看完《Go语言实战》和《Go核心编程》这两本教程,自己总结了一些使用Go编程的要领和注意事项
使用 := 赋值操作符,这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明;
全局变量允许声明但不使用,但是局部变量声明后不使用,则编译会报错;
如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a,但是两个变量的类型必须相同;
Go 语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑;
字符串是一种值类型,且值不可变,即创建某个文本后你无法再次修改这个文本的内容;更深入地讲,字符串是字节的定长数组;
获取字符串中某个字节的地址的行为是非法的,例如:
1
2str := "hello world"
&str[i]switch语句case中不需要break来结束;
未初始化的map的值是nil。map默认是无序的,既不是按照key的顺序排列,也不是按照value排序的;
无论是结构体还是结构体指针类型,都使用点号来获取结构体字段(不同于C/C++,指针使用->来获取成员变量);
如果File是一个结构体类型,那么表达式
1
2new(File)
&File{ }是等价的
在一个结构体中,对于每一种数据类型只能有一个匿名字段;
内嵌将一个已存在类型的字段和方法注入到了另一个类型里:匿名字段上的方法“晋升”成为了外层类型的方法,比较类似于面向对象里面的继承;
空接口: type Any interface{ },不实现任何方法,所以任何类型都实现了空接口. 可以给一个空接口类型的变量赋任何类型的值, 每个空接口类型变量占用2个字节:一个用来存储它包含的类型,另一个用来存储它包含的数据或者指向数据的指针,空接口类似于C语言的*void指针;
复制数据切片至空接口切片必须逐一赋值,因为内存布局不一样;
结构struct中只有被导出字段(首字母大写)才是可设置的;
实际上,反射是通过检查一个接口的值,变量首先被转换成空接口;
类型可以通过内嵌多个接口来提供像多重继承一样的特性;
函数重载通过空接口实现;
封装:
1)包范围内的:通过标识符首字母小写,对象只在它所在的包内可见;
2)可导出的:通过标识符首字母大写,对象对所在包以外也可见;继承:
用组合实现:内嵌一个(或多个)包含想要的行为(字段和方法)的类型;多重
继承可以通过内嵌多个类型实现;多态:
用接口实现:某个类型的实例可以赋给它所实现的任意接口类型的变量。类型和
接口是松耦合的,并且多重继承可以通过实现多个接口实现;字节切片初始化方式:
1)圆括号会进行类型转换;
2)花括号则直接赋值;当调用panic时,所有的defer语句都会保证执行,并把控制权交还给panic的函数调用者;
panic会导致栈被展开直到defer修饰的recover()被调用或者程序中止,recover只能在defer修饰的函数;
执行go test用于测试单个文件时,一定要后跟被测试的源代码文件,如果原文件有其他的引用,有需要一并跟上,否则会提示引用找不到的错误;
协程工作在相同的地址空间,所以共享内存的方式一定是同步的;协程是轻量的,比线程更轻;
经验法则,对于n个核心的情况设置GOMAXPROCS 为n-1以获得最佳性能,也同样需要遵守这条规则:协程的数量 > 1+GOMAXPROCS > 1;
如果在某一时间只有一个协程在执行,不要设置GOMAXPROCS!
GOMAXPROCS等同于(并发的)线程数量,在一台核心数多于1个的机器上,会尽可能有等同于核心数的线程在并行运行;
协程会随着程序的结束而自动消亡;
协程是独立的处理单元,一旦陆续启动一些协程,你无法确定他们是什么时候真正开始执行的。你的代码逻辑必须独立于协程调用的顺序;
通道只能传输一种类型的数据, 所有的类型都可以用于通道,空接口interface{}也可以。甚至可以(有时非常有用)创建通道的通道;
一个无缓冲通道只能包含1个元素, 带缓冲的通道包含多个元素;
关闭通道:
只有发送者才需要关闭通道,接收者不需要关闭通道;
所有处于同一个文件夹里的代码文件,必须使用同一个包名。按照惯例,包和文件夹同名;
import导入包时,如果包名前有下划线_,表示初始化该包,但是不是用包里的变量;
程序中每个代码文件里的 init 函数都会在 main 函数执行前调用;
包中任意一个公开的标识符。这些标识符以大写字母开头。以小写字母开头的标识符是不公开的,不能被其其包中的代码直接访问;
包名应该使用全小写命名
如果使用 … 替代数组的长度,Go 语言会根据初始化时数组元素的数量来确定该数组的长度;
如果在[ ]运算符里指定了一个值,那么创建的就是数组而不是切片。只有为空的时候,才会创建切片;
nil切片不同于空切片, 成员指针变量的值不一样,前者值为nil,后者值为0;
切片赋值,共享底层数组;
如果在创建切片时设置切片的容量和长度一样,就可以强制让新切片的第一个 append 操作创建新的底层数组,与原有的底层数组分离。新切片与原有的底层数组分离后,可以安全地进行后续修改;
在函数间传递切片,就是要在函数间以值的方式传递切片,这里值传递是指的拷贝地址,实际的切片元素值是不用拷贝的;
在64位架构的机器上,一个切片需要 24 字节的内存:指针字段需要 8 字节,长度和容量字段分别需要 8 字节;
由于与切片关联的数据包含在底层数组里,不属于切片本身,所以将切片复制到任意函数的时候,对底层数组大小都不会有影响。复制时只会复制切片本身,不会涉及底层数组,值传递;
用type声明的新类型与原类型是两个完全不同的类型,不能互相赋值;
go语言里面有两种类型的接受者(1-值接收者;2-指针接收者)
两种类型的方法,可以彼此互相调用;
值接收者使用值的副本来调用方法,而指针接受者使用实际值来调用方法(方法对值的修改会影响实际值)。
在实际编写代码时,还要看值类型,是否方便复制(如果值中包含未公开类型,则不能复制)
接口:
用来定义行为的类型。这些被定义的行为不由接口直接实现,而是通过方法由用户定义的类型实现;
1
2
3type Maker interface {
Make()
}当一个标识符的名字以小写字母开头时,这个标识符就是未公开的,即包外的代码不可见。
如果一个标识符以大写字母开头,这个标识符就是公开的,即被包外的代码可见带缓冲的通道,当主动close通道后,goroutine依然可以从通道接收数据,但是不能再往通道发送数据了
iota的使用,默认为0,逐步递增;
字符串类型在 go里是个结构, 包含指向底层数组的指针和长度,这两部分每部分都是 8 个字节,所以字符串类型大小为 16 个字节
在定义常量组时,如果不提供初始值,则表示将使用上一行的表达式;
1
2
3
4
5
6
7
8
9const (
Sunday = 0
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
)Sunday为0,其他常量均为0
可以通过将非可变参数置于可变参数前面的方式来混合使用它们,注意:非可变参数只能放在可变参数前面;
使用os.Exit(0)函数退出程序,之前声明的defer函数不会执行;
go语言有指针,但是没有指针运算,不能对指针进行偏移;
初始化顺序:
全局变量 -> init() -> main();
package 基本的管理单元:同一个package下面,可以有非常多的不同文件,只要每个文件的头部 都有 如 “package xxx” 的相同name, 表示这些文件属于同一个包;
import导入包滥用时,可能会存在循环依赖的问题,需要主动避免;
空切片和nil切片是两个完全不同的概念;
nil切片:只声明,不做初始化
空切片:声明后初始化为空