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")  #第二个打印
}

#会依次打印
posted @ 2022-05-29 16:20  liwenchao1995  阅读(44)  评论(0编辑  收藏  举报