这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
简介
主要介绍一些go语言的关键字和内置函数使用
1.make
make(Type, len, cap)
make主要负责内存分配,有三个主要参数Type、len和cap
Type:数据类型,必要参数,Type的值只能是slice、map、channel这三种数据类型。len:数据类型实际占用的内存空间长度,map、channel是可选参数,slice是必要参数。cap:为数据类型提前预留的内存空间长度,可选参数。所谓的提前预留是当前为数据类型申请内存空间的时候,提前申请好额外的内存空间,这样可以避免二次分配内存带来的开销,大大提高程序的性能。
package main
import "fmt"
func main() {
arr := make([]int, 0)
n := 20
for i := 0; i < n; i++ {
arr = append(arr, 1)
fmt.Printf("len=%d,cap=%d\n", len(arr), cap(arr))
}
}
make() 使用的是一种动态数组算法,一开始先向操作系统申请一小块内存,这个就是 cap,等 cap 被 len 占用满以后就需要扩容,扩容就是动态数组再去向操作系统申请当前长度的两倍的内存,然后将旧数据复制到新内存空间中。
package main
import (
"fmt"
)
func main() {
slice2 := make([]float32, 3, 5) // [0 0 0] 长度为3容量为5的切片
fmt.Println(len(slice2), cap(slice2)) // 3 5
slice2 = append(slice2, 1, 2, 3, 4) // [0, 0, 0, 1, 2, 3, 4]
fmt.Println(len(slice2), cap(slice2)) // 7 12
}
对于上述案例,应该slice2扩容之后的内存容量应为10,实际结果却为12。
2.defer
Java 等语言中有 try...catch 机制,在 try 中捕获各种类型的异常,在 catch 中定义异常处理的行为。Go 语言也提供了类似的机制 defer 和 recover。
defer语句被用于预定对一个函数的调用。可以把这类被defer语句调用的函数称为延迟函数。
defer作用:
- 释放占用的资源
defer coon.Close() //延迟连接关闭,资源回收,防止出现panic无法执行 - 捕捉处理异常
- 输出日志
recover使当前的程序从运行时panic的状态中恢复并重新获得流程控制权,只会在defer修饰的函数中使用,且只可以在panic时触发
package main
import (
"fmt"
)
func get1(index int) (ret int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Some error happened!", r) //r为错误信息
ret = -1 //如果没有指明,会直接返回0
}
}()
arr := [3]int{2, 3, 4}
return arr[index]
}
func get2(index int) (ret int) {
func() {
if r := recover(); r != nil {
fmt.Println("Some error happened!", r)
ret = -1
}
}()
arr := [3]int{2, 3, 4}
return arr[index]
}
func main() {
fmt.Println(get1(5)) //Some error happened! runtime error: index out of range [5] with length 3
//-1
fmt.Println(get2(5)) //panic: runtime error: index out of range [5] with length 3
fmt.Println("finished") //不会执行
}
- 在 get 函数中,使用 defer 定义了异常处理的函数,
在协程退出前,会执行完 defer 挂载的任务。因此如果触发了panic,控制权就交给了 defer。 - 在
defer的处理逻辑中,使用recover,使程序恢复正常,并且将返回值设置为 -1,在这里也可以不处理返回值,如果不处理返回值,返回值将被置为默认值0。
3.struct
结构体类似于其他语言中的 class,可以在结构体中定义多个字段,为结构体实现方法,实例化等
package main
import (
"fmt"
)
type Student struct {
name string
age int
}
func (stu *Student) hello(person string) string { //此时hello为stu实例的方法
return fmt.Sprintf("hello %s, I am %s", person, stu.name)
}
func main() {
stu := &Student{
name: "Tom",
}
msg := stu.hello("Jack")
fmt.Println(msg) // hello Jack, I am Tom
}
4.interface
接口定义了一组方法的集合,接口不能被实例化,一个类型可以实现多个接口
package main
import (
"fmt"
)
type Person interface {
getName() string
}
type Student struct {
name string
age int
}
func (stu *Student) getName() string { //Student的成员方法
return stu.name
}
func main() {
var p Person = &Student{ //实例化 Student后,强制类型转换为接口类型 Person
name: "Tom",
age: 18,
}
fmt.Println(p.getName()) // Tom
}