GO语言学习笔记
【基础知识】
1.不能在一个目录里声明多个包
2.在同一行用,间隔声明多个变量或用var()在多行声明变量
3.用const声明常量,采用iota常量计数器
4.一般用int表示整型宽度,在32位系统下就是32位,64位系统下就是64位
【容器类型】
5.直接使用...让编译器计算数组长度声明: arr := [...]int{1,2,3,4,5}
6.切片是引用类型,相当于没有长度的数组,除了短声明还可以可以用make声明:make([]Type, size, cap)
7.从数组中切片slice := arr[1:3]
8.追加切片数据slice = append(slice, []slice{}...),获得切片容量:cap()
9.创建Map短声明还可以可以用make函数: m := make(map[keyType]ValueType),Map是引用类型
10.删除Map中的元素: delete(m, key)
11.判断Map键值是否存在: value, ok := m[key]
12.获得数组、切片、Map长度: len()
【指针、结构体】
13.用new为所有类型创建指针(分配内存)并初始化: p := new(string)
14.结构体的定义: type name struct{}
15.匿名结构体: s := struct{}{}
16.结构体的匿名字段可以用字段类型访问
17.结构体的比较可以直接用 == 或用反射: if reflect.DeepEqual(s1, s2) {}
18.结构体方法的定义: func (接收者 结构体类型) 方法名(参数列表) 返回值列表{}
【函数、包、流程控制、方法、接口】
19.可变参数的函数: func f(args ...string)int{} 如果有其他参数,可变参数一定在最后
20.不同类型可变参数的函数: func f(args ...interface)int{}
21.匿名函数常用于协程,首字母大写可见性为Public
22.使用package命名包,使用import导入包
23.包被导入时初始化函数优先执行: init(){} 可以有多个,但初始化一次
24.包的匿名导入: import _ 包名 仅执行包的init()
25.分支判断: else if
26.选择语句: switch ... case ... default ; 并列case用,间隔; 使用fallthrough执行下一case;
27.循环语句for不接表达式可以通过break跳出; 用continue跳过本次循环
28.用defer进行延迟调用函数和方法,并不影响变量赋值; defer栈是后进先出的
29.跳转执行: goto label ... label: ; goto语句与标签之间不能有变量声明
30.不同结构体可以定义同名方法,但函数不可以同名
31.指针接收器对调用者可见,值接收器对调用者不可见
32.非结构体也可以定义方法,即类型上定义方法,方法的接收器类型定义和方法的定义应该在同一个包中
33.定义接口 type name interface{}
34.利用接口实现不同功能的方法就是多态
35.接口可以看成是type和value的组合,type是接口底层的具体类型,value是具体类型的值
36.空接口interface{}可以承载各种类型
37.接口类型断言可以使用.(type)
38.类型可以实现多个接口;接口也可以嵌套
【协程、通道、select】
39.启用协程: go f()
40.休眠: time.sleep(1 * time.Second)
41.定义通道: var name chan type 或 name:=make(chan type, num) ;
42.发送数据: name <- data ; 接收数据: v := <- name ;发送和接收数据是阻塞的
43.关闭通道: close(name) ; 判断通道是否关闭: v, ok := <-name
44.获取通道的长度和容量分别是len()、 cap()
45.只读通道: <- chan ; 只写通道 chan <- ;先定义双向通道,然后再使用只读或只写通道
46.可以使用for range遍历通道
47.无缓冲通道必须准备好接收通道数据才不会造成死锁
48.等待一组任务结束再执行其他业务可以使用WaitGroup: var wg sync.WaitGroup
49.传入子协程数量: wg.Add(3) ; 阻塞: wg.Wait() ; 协程计数器-1操作 : defer wg.Done()
50.在多个发送/接收通道操作中进行选择: select ... case ... default 其中case表达式只能对通道进行写入或读取操作
51.利用select可以处理超时执行
【线程同步】
52.互斥锁在结构体中定义字段: Mutex sync.Mutex 方法中在临界区使用 Mutex.Lock() 和 defer Mutex.Unlock() 进行加锁和解锁
53.读写锁: rwMutex sync.RWMutex 写锁仍是Lock()和Unlock() 读锁使用RLock()和RUnlock()
54.创建条件变量: var m sync.Mutex cond := sync.NewCond(&m)
55.条件变量使用: cond.L.Lock() cond.Wait() cond.Broadcast() cond.L.Unlock()
【错误、异常】
56.实现Error() string方法的类型都可以当作一个错误类型; 利用errors.New()返回一个自定义错误
57.使用fmt.Errorf()返回格式化错误
58.错误指的是可能出现问题的地方出现了问题;而异常指的是不应该出现问题的地方出现了问题
59.程序执行没有意义的时候,可以触发异常: panic()
60.当函数发生 panic 时,它会终止运行,在执行完所有的延迟函数后,程序返回到该函数的调用方。这样的过程会一直持续下去,直到当前协程的所有函数都返回退出,然后程序会打印出 panic 信息,接着打印出堆栈跟踪,最后程序终止。
61.可以捕获到 panic 信息: err := recover() recover用于重新获得 panic 协程的控制,必须在 defer 函数中才能生效
62.只有在相同的协程中调用 recover 才管用, recover 不能恢复一个不同协程的 panic
【函数类型、闭包】
63.可以给变量赋值函数 f := func(){} 该变量就是函数类型
64.函数参数和返回值都可以是函数类型,即函数可以被当作参数传递给其他函数,也可以作为另一个函数的返回值
65.当一个匿名函数所访问的变量定义在函数体外部时,就称这样的匿名函数为闭包
【静态类型、动态类型、反射、结构体中的Tag标签】
66.静态类型是变量声明时候的类型;动态类型声明: var in interface{}
67.每个接口变量实际上是由一pair对(type 和 data)组成,其中记录了实际变量的值和类型
68.使用reflect.TypeOf()获得interface{}具体的类型
69.使用reflect.ValueOf()获得interface{}具体的值
70.使用reflect.TypeOf().Kind()获得interface{}的种类,例如,判断是否是结构体: if reflect.ValueOf(x).Kind() == reflect.Struct {
71.使用reflect.TypeOf(struct).NumField()获得结构体中字段的数量
72.使用reflect.TypeOf(struct).Field(i)获得结构体中字段的reflect.Value
73.反射可以将“接口类型变量”转换为“反射类型变量”: 使用reflect.TypeOf 或 使用reflect.ValueOf
74.反射可以将“反射类型变量”转换为“接口类型对象”: v.Interface() , 或进一步: v.Interface().(float64)
75.修改“反射类型对象”: reflect.TypeOf().SetFloat()
76.如果要修改“反射类型对象”其值必须是可写的。检查是否可写: reflect.TypeOf().CanSet()
77.设置“反射类型对象”可写: reflect.TypeOf(&c).Elem()
78.在结构体字段最后增加 `json:"name"` ,可以使用omitempty忽略处理空值,即: `json:"name, omitempty"`
79.获得json数据: json.Marshal()
80.利用反射获得field: reflect.TypeOf(obj).FieldByName("Name")
81.利用反射获得field: reflect.ValueOf(obj).Type().Field(i)
82.利用反射获得field: reflect.ValueOf(&obj).Elem.Type().Field(i)
83.获得Tag: tag := field.Tag
84.获得键值对: labelValue := tag.Get("label")
85.获得键值对: labelValue, ok := tag.Lookup("label")