go defer & panic & recover & init
go defer&panic&recover&init
1.1 defer
//defer 注册延时调用 后注册的先执行,越靠下的defer先执行
//defer 用于注册一个延迟调用,(在函数返回之前调用)常用于释放资源,比如打开一个文件修改,可以添加defer,在函数最后执行
func deferinit1() {
fmt.Println(1)
}
func deferinit2() {
fmt.Println(2)
}
func deferinit3() {
fmt.Println(3)
defer deferinit2()
defer deferinit1()
}
//defer
//三种defer紧跟打印形参的值,注意紧跟func函数的两个函数的返回值,是调用了不同的c
func deferinit4() (c int){
c = 9
defer func(s string) {
fmt.Printf("%d,%s",c,s) //最后一行return后,返回值形参c的值已经变成了10,所以最后一行打印了10,是从函数的return返回值打印出来的
}("李文超") //因为这个函数没有传参,所以这里的()是空的,里面应该填写形参或者实参
defer func (c int) {
fmt.Printf("c=%d\n",c) //c =9 ,是下面传进来的c =9
}(c)
defer fmt.Println(c) // c = 9 defer后不跟func(),这里的c是在被defer注册之前就计算好了
return 10
}
2.1 panic
//panic
//运行时报错户导致panic,比如数组越界,除0
//程序主动调用panic(error)
//panic 报错时会执行什么
//1.逆序执行当前goroutine的defer链(recover从这里介入)。
//2.打印错误信息和调用堆栈。
//3.调用exit(2)结束整个进程。
func panicInit1() {
arr := []int{1,2,3}
panic("1234566") //对应第二种,主动调用panic后,后面的就不会再执行了
a := 9
b := 0
c := 16
fmt.Println(c/b)
fmt.Println(arr[a]) //对应第一种,数组越界 被动调用panic
}
3.1 recover
//recover会阻断panic的执行
func recoverinit1(a,b int) {
fmt.Println("start defer")
defer func() {
//recover必须在defer中才能生效,可以参考panic报错后执行的三部操作
if err := recover(); err != nil {
fmt.Printf("recoverinit1 函数中发生了panic:%s\n",err)
}
}() //这里注册defer
// panic报错时会执行三步,先执行函数中的defer,而recover会检查当前有没有发生panic,检查发现panic,上面的err就不会为 nil
//panic(errors.New("my error"))
defer fmt.Println("AAAAAAAAAAAAAA")
defer func() {
a := 1
b := 0
fmt.Println(a/b) //不会执行,
//defer func()函数内部如果发生panic,会暂时搁置,不执行第三步的调用exit(2)结束整个进程,先去执行其他defer。并不会注册,这时候的传参还是参数里的
//defer后面不跟func,而是执行一条语句,则相关变量在注册时被拷贝和计算,这时候执行打印的参数是最终结果的参数
defer fmt.Println("CCCCCCC") //这里的defer没有注册
}()
}
4.1 init
init和defer是有区别的,init是针对main的主程序,而defer是针对函数的
- main函数是go程序的唯一入口,所以main函数只能存在一个
- main函数必须位于main包中
- 在main函数执行之前会先执行init函数
- 在一个目录,甚至一个go文件里,init()可以重复定义
- 在引入其他包时,相应包里的init函数也会在main函数之前被调用
func main() {
fmt.Println("我是main") #最后打印
}
func init() {
fmt.Println("我是init") #第一个打印
}
func init() {
fmt.Println("我是init2") #第二个打印
}
#会依次打印