您好,登錄后才能下訂單哦!
一 error接口
GO語(yǔ)言中的error類型實(shí)際上是抽象Error()方法的error接口
type error interface{
Error() string
}
GO語(yǔ)言使用該接口進(jìn)行標(biāo)準(zhǔn)的錯(cuò)誤處理。
對(duì)于大多數(shù)函數(shù),如果要返回錯(cuò)誤,大致上都可以定義為如下模式,將error作為多種返回值中的最后一個(gè),但這并非是強(qiáng)制要求:
func Foo (param int) (n int,err error){
// .....
}
調(diào)用時(shí)的代碼建議按照如下方式處理錯(cuò)誤情況
n,err := Foo(0)
if err != nil {
//錯(cuò)誤處理
}else{
//使用返回值n
}
看下面的例子綜合了一下error接口的用法:
package main import( "fmt" ) //自定義錯(cuò)誤類型 type ArithmeticError struct { error //實(shí)現(xiàn)error接口 } //重寫Error()方法 func (this *ArithmeticError) Error() string { return "自定義的error,error名稱為算數(shù)不合法" } //定義除法運(yùn)算函數(shù) func Devide(num1, num2 int) (rs int, err error) { if num2 == 0 { return 0, &ArithmeticError{} } else { return num1 / num2, nil } } func main() { var a, b int fmt.Scanf("%d %d", &a, &b) rs, err := Devide(a, b) if err != nil { fmt.Println(err) } else { fmt.Println("結(jié)果是:", rs) } }
運(yùn)行,輸入?yún)?shù)5 2(正確的情況):
5 2結(jié)果是: 2
若輸入5 0(產(chǎn)生錯(cuò)誤的情況):
5 0自定義的error,error名稱為算數(shù)不合法
通過(guò)上面的例子可以看出error類型類似于Java中的Exception類型,不同的是Exception必須搭配throw和catch使用。
二、defer--延遲語(yǔ)句
在GO語(yǔ)言中,可以使用關(guān)鍵字defer向函數(shù)注冊(cè)退出調(diào)用,即主函數(shù)退出時(shí),defer后的函數(shù)才會(huì)被調(diào)用。
defer語(yǔ)句的作用是不管程序是否出現(xiàn)異常,均在函數(shù)退出時(shí)自動(dòng)執(zhí)行相關(guān)代碼。(相當(dāng)于Java中的finally )
當(dāng)函數(shù)執(zhí)行到最后時(shí),這些defer語(yǔ)句會(huì)按照逆序執(zhí)行,最后該函數(shù)返回。
例如:
package main import ( "fmt") func main() { for i := 0; i < 5; i++ { defer fmt.Println(i) } }
其執(zhí)行結(jié)果為:
43210
defer語(yǔ)句在聲明時(shí)被加載到內(nèi)存(多個(gè)defer語(yǔ)句按照FIFO原則) ,加載時(shí)記錄變量的值,而在函數(shù)返回之后執(zhí)行,看下面的例子:
例子1:defer語(yǔ)句加載時(shí)記錄值
func f1() { i := 0 defer fmt.Println(i) //實(shí)際上是將fmt.Println(0)加載到內(nèi)存 i++ return} func main() { f1() }
其結(jié)果顯然是0
例子2:在函數(shù)返回后執(zhí)行
func f2() (i int) { var a int = 1 defer func() { a++ fmt.Println("defer內(nèi)部", a) }() return a } func main() { fmt.Println("main中", f2()) }
其結(jié)果是
defer內(nèi)部 2main中 1
例子3:defer語(yǔ)句會(huì)讀取主調(diào)函數(shù)的返回值,并對(duì)返回值賦值.(注意和例子2的區(qū)別)
func f3() (i int) { defer func() { i++ }() return 1} func main() { fmt.Println(f3()) }
其結(jié)果竟然是2.
通過(guò)上面的幾個(gè)例子,自然而然會(huì)想到用defer語(yǔ)句做清理工作,釋放內(nèi)存資源(這樣你再也不會(huì)為Java中的try-catch-finally層層嵌套而苦惱了)
例如關(guān)閉文件句柄:
srcFile,err := os.Open("myFile") defer srcFile.Close()
關(guān)閉互斥鎖:
mutex.Lock() defer mutex.Unlock()
上面例子中defer語(yǔ)句的用法有兩個(gè)優(yōu)點(diǎn):
1.讓設(shè)計(jì)者永遠(yuǎn)也不會(huì)忘記關(guān)閉文件,有時(shí)當(dāng)函數(shù)返回時(shí)常常忘記釋放打開的資源變量。
2.將關(guān)閉和打開靠在一起,程序的意圖變得清晰很多。
下面看一個(gè)文件復(fù)制的例子:
package main import ( "fmt" "io" "os" ) func main() { copylen, err := copyFile("dst.txt", "src.txt") if err != nil { return } else { fmt.Println(copylen) } } //函數(shù)copyFile的功能是將源文件sec的數(shù)據(jù)復(fù)制給dst func copyFile(dstName, srcName string) (copylen int64, err error) { src, err := os.Open(srcName) if err != nil { return } //當(dāng)return時(shí)就會(huì)調(diào)用src.Close()把源文件關(guān)閉 defer src.Close() dst, err := os.Create(dstName) if err != nil { return } //當(dāng)return是就會(huì)調(diào)用src.Close()把目標(biāo)文件關(guān)閉 defer dst.Close() return io.Copy(dst, src) }
可以看到確實(shí)比Java簡(jiǎn)潔許多。
三panic-recover運(yùn)行時(shí)異常處理機(jī)制
Go語(yǔ)言中沒(méi)有Java中那種try-catch-finally結(jié)構(gòu)化異常處理機(jī)制,而使用panic()函數(shù)答題throw/raise引發(fā)錯(cuò)誤,
然后在defer語(yǔ)句中調(diào)用recover()函數(shù)捕獲錯(cuò)誤,這就是Go語(yǔ)言的異常恢復(fù)機(jī)制——panic-recover機(jī)制
兩個(gè)函數(shù)的原型為:
func panic(interface{}) //接受任意類型參數(shù) 無(wú)返回值 func recover() interface{} //可以返回任意類型 無(wú)參數(shù)
一定要記住,你應(yīng)當(dāng)把它作為最后的手段來(lái)使用,也就是說(shuō),你的代碼中應(yīng)當(dāng)沒(méi)有,或者很少有panic的東西。這是個(gè)強(qiáng)大的工具,請(qǐng)明智地使用
它。那么,我們應(yīng)該如何使用它呢?
panic()
是一個(gè)內(nèi)建函數(shù),可以中斷原有的控制流程,進(jìn)入一個(gè)令人panic(恐慌即Java中的異常)的流程中。當(dāng)函數(shù)F調(diào)用panic,函數(shù)F的執(zhí)行被中
斷,但是F中的延遲函數(shù)(必須是在panic之前的已加載的defer)會(huì)正常執(zhí)行,然后F返回到調(diào)用它的地方。在調(diào)用的地方,F(xiàn)的行為就像調(diào)用了panic。這一
過(guò)程繼續(xù)向上,直到發(fā)生panic的goroutine中所有調(diào)用的函數(shù)返回,此時(shí)程序退出。異??梢灾苯诱{(diào)用panic產(chǎn)
生。也可以由運(yùn)行時(shí)錯(cuò)誤產(chǎn)生,例如訪問(wèn)越界的數(shù)組。
recover()
是一個(gè)內(nèi)建的函數(shù),可以讓進(jìn)入令人恐慌的流程中的goroutine恢復(fù)過(guò)來(lái)。recover僅在延遲函數(shù)中有效。在正常
的執(zhí)行過(guò)程中,調(diào)用recover會(huì)返回nil,并且沒(méi)有其它任何效果。如果當(dāng)前的goroutine陷入panic,調(diào)用
recover可以捕獲到panic的輸入值,并且恢復(fù)正常的執(zhí)行。
一般情況下,recover()應(yīng)該在一個(gè)使用defer關(guān)鍵字的函數(shù)中執(zhí)行以有效截取錯(cuò)誤處理流程。如果沒(méi)有在發(fā)生異常的goroutine中明確調(diào)用恢復(fù)
過(guò)程(使用recover關(guān)鍵字),會(huì)導(dǎo)致該goroutine所屬的進(jìn)程打印異常信息后直接退出。
這里結(jié)合自定義的error類型給出一個(gè)使用panic和recover的完整例子:
package main import ( "fmt" ) //自定義錯(cuò)誤類型 type ArithmeticError struct { error } //重寫Error()方法 func (this *ArithmeticError) Error() string { return "自定義的error,error名稱為算數(shù)不合法" } //定義除法運(yùn)算函數(shù)***這里與本文中第一幕①error接口的例子不同 func Devide(num1, num2 int) int { if num2 == 0 { panic(&ArithmeticError{}) //當(dāng)然也可以使用ArithmeticError{}同時(shí)recover等到ArithmeticError類型 } else { return num1 / num2 } } func main() { var a, b int fmt.Scanf("%d %d", &a, &b) defer func() { if r := recover(); r != nil { fmt.Printf("panic的內(nèi)容%v\n", r) if _, ok := r.(error); ok { fmt.Println("panic--recover()得到的是error類型") } if _, ok := r.(*ArithmeticError); ok { fmt.Println("panic--recover()得到的是ArithmeticError類型") } if _, ok := r.(string); ok { fmt.Println("panic--recover()得到的是string類型") } } }() rs := Devide(a, b) fmt.Println("結(jié)果是:", rs) }
其執(zhí)行的結(jié)果為:
使用與上面相同的測(cè)試數(shù)據(jù),輸入5 2得:
5 2結(jié)果是: 2
輸入5 0得:
5 0panic的內(nèi)容自定義的error,error名稱為算數(shù)不合法 panic--recover()得到的是error類型 panic--recover()得到的是ArithmeticError類型
可見(jiàn)已將error示例程序轉(zhuǎn)換為了Java中的用法,但是在大多數(shù)程序中使用error處理的方法較多。
需要注意的是:defer語(yǔ)句定義的位置 如果defer放在了
rs := Devide(a, b)語(yǔ)句之后,defer將沒(méi)有機(jī)會(huì)執(zhí)行即:
rs := Devide(a, b) defer func() { if r := recover(); r != nil { fmt.Printf("panic的內(nèi)容%v\n", r) if _, ok := r.(error); ok { fmt.Println("panic--recover()得到的是error類型") } if _, ok := r.(*ArithmeticError); ok { fmt.Println("panic--recover()得到的是ArithmeticError類型") } if _, ok := r.(string); ok { fmt.Println("panic--recover()得到的是string類型") } } }()
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。