最近开始学习Go,记录一下Go语言的学习笔记。
基础篇
- 1- 如果import了没有使用的包,那么会出现红线提醒"、import for side-effects"以及"unused import",我所使用的GoLand编辑器会在go build之后自动消除这些错误,这挺好的
- 2- 只能引用其中已导出的名字(顾名思义),并且已经导出的方法均为大写开头,这和Python当中的是不一样的。例如导入了math,之后所有的方法都是大写开头,例如math.Acos()函数
- 3- 变量的类型都声明在变量之后,函数也是如此,例如:
func add(a float64, b float64) float64{
return math.Abs(a)+math.Acos(b)
}
//对于a,b以及函数add的返回类型都是在之后进行说明,并且可以采用省略方式进行书写,例如add(a,b float64)
- 4- var和:=的区别在于,var为关键词,可以在函数外使用,但是:=不可以,只能在函数内进行赋值使用,但是它们都是在需要创建新的变量的时候才进行使用,如果赋值,则使用普通‘=’即可
- 5- 不同类型之间的转换需要显示转换,例如
x:=3.1415
y:=float32(x)
z:=uint(y)
//这样是错误的 :z uint=y
- 6- 变量的声明直接采用 := 就行,如v := 2,如果需要自己明确是什么类型的变量,则使用var和=进行声明,如var t int8=1,产量的声明只能是采用=,如const i=9
- 7- for循环的几种形式:
//方式1
for initial_condition ; ending_condition ; operation{
//some operations
}
//方式2
for ; ending_condition ; {
//just ending condition
}
//方式3
for ending_condition{
//same as while
}
- 8- if当中的赋值语句的变量作用域仅仅在if和对应的else当中
- 9- defer 语句会将函数推迟到外层函数返回之前执行(执行return之前执行defer语句)。推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。
- 10- 不声明长度的切片:variable := [] Type{data1,data2,...,datan},slice和array的区别在于array需要制定长度,而slice不需要指明,例如:
q:=[]struct{
x bool
y uint
}{{true,1},
{true,2},
{true,3}}
//等同于:
type mys struct {
a bool
b uint
}
q:= [] mys{{true,1},
{true,2},
{true,3}}
- 11- 切片当中的len和cap属性是不完全一致的,后者属于固定属性,初始化的时候已经决定了,后者则是可变的,例如某一切片什么都没有,此时len(a)=0。并且切片属于 套娃 任何类型都可以放进去,但是所有套进去的(通过append函数添加的)必须是同一类型,这和Python中的list很不一样
- 12- 需要通过变量指定切片大小时,需要再用make来初始化切片,如a:=make([]int,len,cap) 创建一个长度为len容量大小为cap,基本类型为int的切片
- 13- 采用map进行映射,达到Python字典的效果,例如:
type Vertex struct {
Lat, Long float64
}
m := make(map[string]Vertex,2)//声明了长度为2依旧可以不断的添加键值
m["Bell Labs"] = Vertex{
40.68433, -74.39967,
}
m["12"]=Vertex{0.12,0.23}
m["15"]=Vertex{0.265,0.123}
m["16"]=Vertex{0.265,0.123}
fmt.Println(m["16"])
- 14- 可以直接从array当中获取slice,如下所示,aslice直接从array当中获取,对于aslice的改变会直接影响array。同时aslice的三个属性为开始指针,长度,容量。下例的len=4(7-3),cap=7(从d一直到j)
array := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
aslice:=array[3:7]
aslice[0]='z'
fmt.Print(aslice)
fmt.Print(array)
- 15-
- map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取
- map的长度是不固定的,也就是和slice一样,也是一种引用类型(改变其中的一个,被应用的随之也改变)
- 内置的len函数同样适用于map,返回map拥有的key的数量
- map的值可以很方便的修改,通过mapName["one"]=11可以很容易的把key为one的字典值改为11
- 16-
- new 的作用是 初始化 一个指向类型的指针 (*T), make 的作用是为 slice, map 或者 channel 初始化,并且返回引用 T
- make(T, args)函数的目的与new(T)不同。它仅仅用于创建 Slice, Map 和 Channel,并且返回类型是 T(不是T*)的一个初始化的(不是零值)的实例。 这中差别的出现是由于这三种类型实质上是对在使用前必须进行初始化的数据结构的引用。 例如, Slice是一个 具有三项内容的描述符,包括 指向数据(在一个数组内部)的指针,长度以及容量。在这三项内容被初始化之前,Slice的值为nil。对于Slice,Map和Channel, make()函数初始化了其内部的数据结构,并且准备了将要使用的值。
- 17- 我们把很多值聚合在了一个case里面,同时,Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。
- 18- panic以及recover的使用方法:
panic意思是抛出一个异常,和python的raise用法类似
recover是捕获异常,和python的except用法类似。defer会延迟函数到其他函数之后完之后再执行,后面跟的是函数。golang 的错误处理流程:当一个函数在执行过程中出现了异常或遇到panic(),正常语句就会立即终止,此时在返回之前如果没有相应的recover操作那么系统自动抛出错误,并停止运行。如果与recover操作,那么recover之后再返回,回退执行defer语句,再报告异常信息,最后退出goroutine。如果在 defer 中使用了 recover()函数,则会捕获错误信息,使该错误信息终止报告。
package main
import (
"fmt"
)
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
fmt.Println("Recovered in f", r)
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g()
fmt.Println("Returned normally from g.")
}
func g() {
panic("Error in g")
}
- 19- go的结构体当中的匿名字段与外部的属性关键词重复时,默认先访问外部属性,或者通过先访问匿名字段的再访问匿名字段当中的属性。
- 20- Receiver通常是以值传递,而非引用传递。Receiver还可以是指针, 两者的差别在于, 指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。
- 21- 数据竞争会在两个以上的goroutine并发访问相同的变量且至少其中一个为写操作时发生。没有所谓的无害的竞争,对于数据的存取而言,数据竞争是致命的。
- 22- 解决数据竞争的几种办法:①只读不写,彻底断绝紊乱的可能性。②读写只在一个goroutine当中进行,避免了竞争的产生。③多个goroutine之间采用channel进行数据传递