天天看點

6.Go-錯誤,defer,panic和recover

6.1.錯誤

   Go語言中使用builtin包下error接口作為錯誤類型

   Go語言中錯誤都作為方法/函數的傳回值

   自定義錯誤類型

//Learn_Go/main.go
package main

import (
	"errors"
	"fmt"
)

func demo(i,k int) (r int, e error)  {
	if k == 0 {
		e = errors.New("除數不能為0")
		return
	}
	r = i/k
	return
}

func main()  {
	//result,error := demo(6,3)
	result,e := demo(6,0)
	if e != nil{
		fmt.Println("執行錯誤,錯誤資訊為:",e)   //執行錯誤,錯誤資訊為: 除數不能為0
		return
	}
	fmt.Println("執行成功,結果:",result)      //執行成功,結果: 2
}           

複制

6.2.defer

  Go語言中defer可以完成延遲功能,目前函數執行完成後執行defer功能

  defer最常用的就是關閉連接配接(資料庫,檔案等),可以打開連接配接後緊跟defer進行關閉

(1)Go語言中defer無論寫到哪裡都是最後執行,不用非要把關閉代碼寫在最後

//Learn_Go/main.go
package main

import "fmt"

func main()  {
	fmt.Println("打開連接配接")
	//defer fmt.Println("關閉連接配接") 
	defer func() {
		fmt.Println("關閉連接配接")       //defer執行
	}()
	fmt.Println("進行操作")
}

//結果
打開連接配接
進行操作
關閉連接配接           

複制

(2)多個defer

  多重defer采用棧結構執行,先産生後執行

  在很多代碼結構中都可能出現産生多個對象,而程式希望這些對象倒叙關閉,多個defer正好可以解決這個問題

//Learn_Go/main.go
package main

import "fmt"

func main()  {
	fmt.Println("打開連接配接A")
	defer fmt.Println("關閉連接配接A")
	fmt.Println("打開連接配接B")
	defer fmt.Println("關閉連接配接B")
	fmt.Println("打開連接配接C")
	defer fmt.Println("關閉連接配接C")
	fmt.Println("進行操作")
}

//結果
打開連接配接A
打開連接配接B
打開連接配接C
進行操作
關閉連接配接C
關閉連接配接B
關閉連接配接A           

複制

(3)defer和return結合

   defer與return同時存在時,要把return了解成兩條執行結合,一個指令是給傳回值

   指派,另一個指令傳回跳出函數

   defer和return時整體執行順序

  • 先給傳回值指派
  • 執行defer
  • 傳回跳出函數

(4)沒有定義傳回值接收變量,執行defer時傳回值已經指派

//Learn_Go/main.go
package main

import "fmt"

func demo() int {
	i := 1
	defer func() {
		i = i + 2
	}()
	return i
}

func main() {
	fmt.Println(demo())       //1
}           

複制

(5)聲明接收傳回值變量,執行defer時修改了傳回值内容

//Learn_Go/main.go
package main

import "fmt"

func demo() (z int) {
	i := 1
	defer func() {
		z = i + 2
	}()
	return
}

func main() {
	fmt.Println(demo())       //3
}           

複制

6.3.panic

panic是build中函數,當執行到panic後,終止剩餘代碼執行,并列印錯誤棧資訊。

//Learn_Go/main.go
package main

import "fmt"

func main() {
	fmt.Println("111")
	panic("錯誤資訊")
	fmt.Println("222")
}

//結果
111
panic: 錯誤資訊

goroutine 1 [running]:
main.main()
C:/Users/86158/Desktop/Learn_Go/main.go:8 +0x82           

複制

panic不是立即停止程式,defer還是執行的

//Learn_Go/main.go
package main

import "fmt"

func main() {
	defer fmt.Println("執行defer的内容")
	fmt.Println("111")
	panic("錯誤資訊")
	fmt.Println("222")
}

//結果
111
執行defer的内容
panic: 錯誤資訊

goroutine 1 [running]:
main.main()
C:/Users/86158/Desktop/Learn_Go/main.go:9 +0xdc           

複制

6.4.recover

 recover()表示回複程式的panic(),讓程式正常執行

    rcover()是和panic一樣都是builtin中函數,可以接受panic的資訊,恢複程式的正常執行

    recover()一般在defer内部,如果沒有panic資訊,傳回nil;如果有panic,recover會把panic狀态取消

//Learn_Go/main.go
package main

import "fmt"

func main() {
	defer func() {
		if error := recover();error != nil{
			fmt.Println("panic為:", error)
		}
	}()
	fmt.Println("111")
	panic("出現了錯誤資訊")
	fmt.Println("222")
}

//結果
111
panic為: 出現了錯誤資訊           

複制

函數調用過程中panic和recover()

  • recover()隻能恢複目前函數級或目前函數調用函數中的panic(),恢複後調用目前級别函數結束,但是調用此函數的函數可以繼續執行
  • panic會一直向上傳遞,如果沒有recover()則表示程式終止,但是碰見了recover(),recover()所在級别函數表示沒有panic,panic就不會向上傳遞
//Learn_Go/main.go
package main

import "fmt"

func demo1()  {
	fmt.Println("demo1上半部分")
	demo2()
	fmt.Println("demo1下半部分")
}

func demo2()  {
	fmt.Println("demo2上半部分")
	demo3()
	fmt.Println("demo2下半部分")
}

func demo3()  {
	fmt.Println("demo3上半部分")
	panic("demo3中出現panic")
	fmt.Println("demo3下半部分")
}

func main() {
	fmt.Println("程式開始")
	demo1()
	fmt.Println("程式結束")
}

//結果
程式開始
demo1上半部分
demo2上半部分
demo3上半部分
panic: demo3中出現panic           

複制

 demo3添加recover() 

//Learn_Go/main.go
package main

import "fmt"

func demo1()  {
	fmt.Println("demo1上半部分")
	demo2()
	fmt.Println("demo1下半部分")
}

func demo2()  {
	fmt.Println("demo2上半部分")
	demo3()
	fmt.Println("demo2下半部分")
}

func demo3()  {
	defer func() {
		recover()
	}()
	fmt.Println("demo3上半部分")
	panic("demo3中出現panic")
	fmt.Println("demo3下半部分")
}

func main() {
	fmt.Println("程式開始")
	demo1()
	fmt.Println("程式結束")
}

//結果
程式開始
demo1上半部分
demo2上半部分
demo3上半部分
demo2下半部分
demo1下半部分
程式結束           

複制

 demo2添加recover()

//Learn_Go/main.go
package main

import "fmt"

func demo1()  {
	fmt.Println("demo1上半部分")
	demo2()
	fmt.Println("demo1下半部分")
}

func demo2()  {
	defer func() {
		recover()
	}()
	fmt.Println("demo2上半部分")
	demo3()
	fmt.Println("demo2下半部分")
}

func demo3()  {
	fmt.Println("demo3上半部分")
	panic("demo3中出現panic")
	fmt.Println("demo3下半部分")
}

func main() {
	fmt.Println("程式開始")
	demo1()
	fmt.Println("程式結束")
}

//結果
程式開始
demo1上半部分
demo2上半部分
demo3上半部分
demo1下半部分
程式結束           

複制