1、Go
语言更高效的利用计算机CPU
上的所有核心,更高效的复用代码。
2、Go
语言自带垃圾回收器,不需要用户自己管理内存。
3、使用 goroutine
并发,使用 通道
避免其他语言中的共享内存访问
的问题,减少了锁
或者 同步机制
。
4、Go
语言的每个代码文件都属于一个包,一个包定义一组编译过的代码,包的名字类似于命名空间。处在同一个文件夹里的代码文件,必须使用同一个包名,按照惯例,包和文件夹同名。
5、Go
编译器为了让程序的可读性更强,不允许声名导入某个包却不使用。解决方法: 在导入的路径前加一个 下划线
,这样它会调用对应包内的所有代码文件里定义的 init
函数,进行初始化操作。
6、每个代码文件里的 init
函数都会在 main
函数执行前调用。
7、Go
中,标识符(变量 or 函数等)要么从包里公开,要么不从包里公开。当代码导入了一个包时,程序可以直接访问这个包中任意一个公开的标识符。公开的标识符用 大写字母 开头,以 小写字母 开头的标识符是不公开的。
8、 在 Go
中,所有变量都会被初始化为其零值,如 指针
类型的零值为 nil
,引用类型的话,所引用的底层数据结构会被初始化为对应的零值,整个引用类型的变量会返回 nil
作为其值。
9、:=
运算符,用于声明一个变量并给这个变量赋予初始值。
10、如果需要声明初始值为零值的变量,应该使用 var
关键字声明变量;如果提供确切的非零值初始化变量或者使用函数返回值创建变量,应该使用简化变量声明运算符 :=
。
11、Go
中声明常量的时候不需要指定类型。
12、关键字 defer
会将其后面的函数调用在函数返回时才执行。关键字 defer
可以保证这个函数一定会被调用,哪怕函数意外崩溃终止,还可以缩短打开文件和关闭文件之间间隔的代码行数,提高代码可读性。
13、如果声明函数的时候带有接收者,则意味着声明了一个方法,这个方法会和指定的接收者类型绑定在一起。接口实现多态
14、使用 指针 可以在 函数 间或者 goroutine
间共享数据。
15、Go
程序编译时,会使用声明 main
包的代码所在的目录的目录名作为二进制可执行文件的文件名。
16、创建并初始化 切片
、映射
等,可以使用内置的 make
函数 或者 字面量
。[]
中指定长度的是 数组
,不指定长度的是 切片
。
17、在Go
语言中,数组、切片、映射
在作为参数传入函数时,只有 数组
是将全部的值复制一份拷贝,切片、映射
仅拷贝元数据,其底层的存储用的是同一份。
18、切片、函数以及包含切片的结构类型这些类型由于具有引用语义,不能作为映射的键。
19、Go
语言是静态类型
的编程语言,也就是说,编译器需要在编译的时候就知道程序里每个变量的类型。
20、Go
编译器不会对不同类型的值做隐式转换
。
21、关键字 func
和函数名之间的参数被称作 接收者
, 将函数与接收者的类型绑在一起。如果一个函数有接收者,这个函数就被称为 方法
。Go
语言里有两种类型的接收者:值接收者
和 指针接收者
。如果使用值接收者声明方法,调用时会使用这个值的副本来执行,而指针接收者使用实际值来调用方法。
Go
语言既允许使用值,也允许使用指针来调用方法,不必严格符合接收者的类型。
22、方法,接口与方法集
在将数据类型与方法绑定时,值可以调用绑定值或者值指针的方法,值指针也可以调用绑定值或者值指针的方法,没什么影响,只取决于是否要更改变量的某些东西。
在数据类型实现接口中声明的函数(方法)时,如果将值与方法绑定,那么只能通过值调用该方法(将值传递给接口参数,
接口.函数()
);如果将值指针与方法绑定,可以通过值或者值指针来调用该方法。原因是:不是总能获得一个值的地址,所以值的方法集只能包括使用值接收者实现的方法。
23、嵌入类型
嵌入类型是将已有的类型直接声明在新的结构类型里,被嵌入的类型被称为新的外部类型的内部类型。
要嵌入一个类型,只需要声明这个类型的名字就可以了。
通过嵌入类型,与内部类型相关的标识符会提升到外部类型上。这样被提升的标识符就像直接声明在外部类型里的标识符一样,也是外部类型的一部分。
外部类型可以通过声明与内部类型标识符同名的标识符来覆盖内部标识符的字段或者方法。
注意 自动提升, 如果外部类型实现了 某方法 , 内部类型的实现就不会被提升。不过内部类型的值一直存在,因此还可以通过直接访问内部类型的值,来调用没有被提升的内部类型实现的方法。
24、当一个标识符的名字以小写字母开头时,这个标识符就是未公开的,即包外的代码不可见。结构类型是公开的,里面的某个字段是未公开的,那么也不能在包外访问这个未公开的字段。
25、 Go
语言调度器在任何给定的时间,都会全面控制哪个 goroutine
要在哪个逻辑处理器上运行。用于在 goroutine
之间同步和传递数据的关键数据类型叫做 通道
。
26、Go
语言在运行时,默认会为每个可用的物理处理器分配一个逻辑处理器。
27、goroutine
终止时无法获取函数的返回值。
28、Go
语言中一种消除竞争状态的办法是:使用Go
语言提供的 锁机制
, 来锁住 共享资源
,从而保证 goroutine
的同步状态。
- 原子函数 。 在
sync/atomic
库,atomic.AddInt()、atomic.LoadInt()、atomic.StoreInt()
- 互斥锁。 在
sync.Mutex
中,mutex.Lock()、mutex.Unlock()
29、有缓冲通道和无缓冲通道
- 无缓冲通道保证进行发送和接收的
goroutine
会在同一时间进行数据交换。 - 有缓冲的通道, 只有在缓冲区满的时候,发送动作才会堵塞,只有在缓冲区空的时候,接收动作才会阻塞。
30、当通道关闭后,goroutine
依旧可以从通道接收数据,但是不能再向通道里发送数据。这允许通道关闭后依旧能取出其中缓冲的全部值,而不会有数据丢失。从一个已经关闭且没有数据的通道里获取数据,总会立刻返回,并返回一个通道类型的零值。如果在获取通道时还加入了可选的标志,就能得到通道的状态信息。
31、select
语句只能处理通道类型的数据,当有 default
语句时,前面的条件都不满足的时候执行 default
语句,当没有 default
语句的时候,程序进入阻塞状态。当有多个语句满足条件时,select
随机挑选一条执行,其他的忽略。 switch
语句可以处理各种类型的数据。
32、Go
语言中的通道类型分为 双向通道 和 单向通道。 双向:chan
、只发送数据:<-chan
、只接收数据:chan<-
。
33、Go
语言中的断言
类型断言的本质,跟类型转换类似,都是类型之间进行转换 。 不同之处在于,类型断言是在接口之间进行 。
1 | <目标类型的值>,<布尔参数> := <表达式>.( 目标类型 ) // 安全类型断言 |
34、定制 日志记录器
,可以将不同层次的日志记录到不同的路径中去。 方法:创建 log.Logger
指针,并使用 log.New()
初始化。
35、反序列化
: 将字符串转换成数据变量值,
序列化
: 将数据变量值转换成字符串。
36、任何类型都实现了一个空接口 interface{}
,声明一个 map
类型,其键是 string
类型,其值是 interface{}
类型,这意味着这个 map
类型可以使用任意类型的值作为给定键的值。
37、Go
语言的测试工具只会认为以 _test.go
结尾的文件是测试文件。一但测试工具找到了测试文件,就会查找里面的测试函数并执行。包的名字以 _test
结尾,那么测试代码只能访问包里的公开的标识符,即便测试代码文件和被测试代码文件的代码放在同一个文件夹中,也只能访问公开的标识符。
38、一个测试函数必须是公开的函数,并且以 Test
开头,并且函数的参数要接收一个指向 testing.T
类型的指针,并且不返回任何值。
39、Println
、Fatalln
、Panicln
这三个函数都有格式化的版本,值需要用 f
替换结尾的 ln
。
Fatal
系列函数用来写日志消息,然后使用 os.Exit(1)
终止程序。
Panic
系列函数用来写日志消息,然后出发一个 panic
,除非程序执行 recover
函数,否则会导致程序打印调用栈后终止。
Print
系列函数是写日志消息的标准方法。
40、如果测试函数执行时没有调用过 t.Fatal
或者 t.Error
方法,就认为测试通过了。
41、基础单元测试
、 表组测试
: 测试可以接受一组不同的输入并产生不同的输出的代码。
42、结构类型 (struct)
在初始化时,如果是在同一行赋值的话,行尾不需要加逗号。如果每一行初始化一个字段,那么每一行末尾都需要加一个逗号。
43、基准测试
和单元测试文件一样,基准测试的文件名也必须以 _test.go
结尾,同时也必须导入 testing
包。基准测试函数必须以 Benchmark
开头,接受一个指向 testing.B
类型的指针作为唯一参数。
44、包中的示例代码,既能用于测试,也能用于文档。函数名以 Example
开头。