go语言之抛出异常
<p>一: panic和recover </p><p>作用:panic 用来主动抛出错误; recover 用来捕获 panic 抛出的错误。</p>
<div class="cnblogs_code">
<p><span style="color: rgba(0, 0, 0, 1)">概述: </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">,引发panic有两种情况 </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)程序主动调用panic函数 </span><span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">)程序产生运行时错误,由运行时检测并抛出 过程: ! 发生 panic 后,程序会从调用 panic的函数位置或发生panic 的地方立即返回,逐层向上执行函数的defer语句, 然后逐层打印函数调用堆栈,直到被 recover 捕获或运行到最外层函数而退出。 ! panic的参数是一个空接口类型 </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)">{},所以任意类型的变量都可以传递给 panic(xxx) ! panic 不但可以在函数正常流程中抛出,在 defer 逻辑里也可以再次调用 panic 或抛出 panic a defer 里面的 panic 能够被后续执行的 defer 捕获。 recover()用来捕获 panic,阻止panic继续向上传递recover()和defer一起使用 ,但是recover() 只 有在defer后面的函数体内被直接调用才能捕获panic终止异常否则返回 nil,异常继续向外传递</span></p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">//这会获取失败
defer recover()
defer fmt.println(recover())
//嵌套两层也会获取失败
defer func() {
func(){
println("defer inner")
recover() //无效
}()
}
</pre>
</div>
<p> 以下场景会获取成功</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">package main
func f(){
defer func() {
println(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">defer inner</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
recover()
}()
}
func except(){
recover()
}
func test(){
defer except()
panic(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">test panic</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
}
func main(){
f()
except()
test()
}</span></pre>
</div>
<p> 可以有多个panic被抛出,连续多个panic的场景只能出现在延迟调用里面,否则不会出现多个panic被抛出的场景。但只有最后一次panic能被捕获</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">package main
import </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fmt</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
func main(){
defer func() {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> err :=recover();err !=<span style="color: rgba(0, 0, 0, 1)">nil{
fmt.Println(err)
}
}()
//只有最后一次panic调用被捕获
defer func() {
panic(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">first defer panic</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">) //打印结构是这个
}()
defer func() {
panic(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">second defer panic</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
}()
panic(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">main body panic</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
}</span></pre>
</div>
<p> 包中 in it 函数引发的 panic 只能在 in it 函数中捕获,在 main 中无法被捕获,原因是 in it<br>数先于 main 执行,函数并不能捕获内部新启动的 goroutine 所抛<em id="__mceDel">出的 panic 。</em></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">package main
import (
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fmt</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">time</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
)
func </span><span style="color: rgba(0, 0, 255, 1)">do</span><span style="color: rgba(0, 0, 0, 1)">(){
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里并不能获取da函数中的panic</span>
<span style="color: rgba(0, 0, 0, 1)"> defer func() {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> err := recover();err !=<span style="color: rgba(0, 0, 0, 1)"> nil{
fmt.Println(err)
}
}()
go da()
go db()
time.Sleep(</span><span style="color: rgba(128, 0, 128, 1)">3</span>*<span style="color: rgba(0, 0, 0, 1)">time.Second)
}
func da(){
panic(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">panic da</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i := <span style="color: rgba(128, 0, 128, 1)">0</span>; i<<span style="color: rgba(128, 0, 128, 1)">10</span>; i++<span style="color: rgba(0, 0, 0, 1)">{
fmt.Println(i)
}
}
func db(){
</span><span style="color: rgba(0, 0, 255, 1)">for</span> i := <span style="color: rgba(128, 0, 128, 1)">0</span>; i<<span style="color: rgba(128, 0, 128, 1)">10</span>; i++<span style="color: rgba(0, 0, 0, 1)">{
fmt.Println(i)
}
}
func main(){
fmt.Println(</span><span style="color: rgba(0, 0, 255, 1)">do</span><span style="color: rgba(0, 0, 0, 1)">)
fmt.Println(da)
fmt.Println(db)
}</span></pre>
</div>
<p> 示例</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">package main
import </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fmt</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">系统抛异常</span>
<span style="color: rgba(0, 0, 0, 1)">func test01() {
a :</span>= [<span style="color: rgba(128, 0, 128, 1)">5</span>]<span style="color: rgba(0, 0, 255, 1)">int</span>{<span style="color: rgba(128, 0, 128, 1)">0</span>, <span style="color: rgba(128, 0, 128, 1)">1</span>, <span style="color: rgba(128, 0, 128, 1)">2</span>, <span style="color: rgba(128, 0, 128, 1)">3</span>, <span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">a = 123</span>
index := <span style="color: rgba(128, 0, 128, 1)">10</span><span style="color: rgba(0, 0, 0, 1)">
a </span>= <span style="color: rgba(128, 0, 128, 1)">123</span><span style="color: rgba(0, 0, 0, 1)">
}
func main() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">panic: runtime error: index out of range
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">test01()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">test02()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">test03()</span>
<span style="color: rgba(0, 0, 0, 1)"> test04()
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">自己抛异常</span>
<span style="color: rgba(0, 0, 0, 1)">func test02() {
getCircleArea(</span>-<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">)
}
func getCircleArea(radius float32) (area float32) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> radius < <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">抛异常</span>
panic(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">半径不能为负数</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">3.14</span> * radius *<span style="color: rgba(0, 0, 0, 1)"> radius
}
func test03() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">延时执行匿名函数
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">延时到什么时候?要么正常结束,要么出异常
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">recover()是复活的意思</span>
<span style="color: rgba(0, 0, 0, 1)"> defer func() {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> err := recover(); err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
fmt.Println(err)
}
}()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">会报错</span>
getCircleArea(-<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">下句话没有打印</span>
fmt.Println(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">这里有没有执行?</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
}
func test04(){
test03()
fmt.Println(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">GAME OVER</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
}</span></pre>
</div>
<p>返回异常</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">package main
import (
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">errors</span><span style="color: rgba(128, 0, 0, 1)">"</span>
<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">fmt</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">算半径</span>
<span style="color: rgba(0, 0, 0, 1)">func getCircleArea(radius float32) (ret float32, err error) {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> radius < <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建异常</span>
err = errors.New(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">沙雕,半径不能为负数</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
ret </span>= <span style="color: rgba(128, 0, 128, 1)">3.14</span> * radius *<span style="color: rgba(0, 0, 0, 1)"> radius
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
}
func main() {
ret, err :</span>= getCircleArea(<span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> err !=<span style="color: rgba(0, 0, 0, 1)"> nil {
fmt.Println(err)
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
fmt.Println(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ret=</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, ret)
}
}</span></pre>
</div>
<p>使用场景:</p>
<div class="cnblogs_code">
<pre>(<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)程序遇到了无法正常执行下去的错误,主动调用 panic 函数结束程序运行
(</span><span style="color: rgba(128, 0, 128, 1)">2</span>)在调试程序时,通过主动调用 panic 实现快速退出, panic 打印出的堆枝能够更快地定位错误。</pre>
</div>
<p>为了保证程序的健壮性,需要主动在程序的分支流程上使用 recover()拦截运行时错误。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Go 提供了两种处理错误 方式,
一 种是借助 panic和 recover 的抛出捕获机制,
另一种使用error 错误类型</span></pre>
</div>
<p>error:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">go 语言内置错误接口类型
任何类型只要实现 error() </span><span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> 方法,都可以传递 eηor接口类型变量。<br>Go 语言典型的错误处理方式是将error作为函数最后一个返回值 在调用函数
通过检测其返回的error值是否为nil来进行错误处理。</span></pre>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:go;gutter:true;">typeerror interface{
Error()string
}
</pre>
</div>
<p> </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">• 在多个返回值的函数中,error 通常作为函数最后一个返回值
• 如果一个函数返回error 类型变量 ,则先用if语句处理 error </span>!= nil 异常场景,正常逻辑放到 <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> 语句块的后面,
保持代码平坦。
• defer 吾句应该放到四判断的后面,不然有可能产生 panic
• 在错误逐级向上传递的过程中,错误信息应该不断地丰富和完善,而不是简单地抛出下层调用的错误。
这在错误日志分析 非常有用和友好。</span></pre>
</div>
<p>错误和异常</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">广义上的错误: 发生非期望的行为。
狭义的错误:发生非期望的己知行为,这里的己知是指错误的类型是预料并定义好的。
异常: 发生非期待的未知行为。这里的未知是指错误的类型不在预先定义的范围内。异常又被称为未捕获的错误
( untrapped error )。程序在执行时发生未预先定义的错误,程序编译器和运行时都没有及时将其捕获处理。
而是由操作系统进行异常处理,比如 语言程序里面经常出现的 Segmentation Fault (段异常错误),这个就属
于异常范畴。</span></pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/1513688/201905/1513688-20190503172856464-1849501660.png"></p>
<p>Go 是一门类型安全的语言,其运行时不 出现这种编译器和运行时都无法捕获的错 ,也就是说,<br>不会出现 untrapped error ,所以从这个角度来说, Go 语言不存在所谓的异常,出现的“异常”全是错误</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">Go 程序需要处理的这些错误可 分为两类
</span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> 一类是运行时错误( runtime errors ),此类错误语言的运行时能够捕获,并采取措施一一隐式或显式地抛出 panic
</span><span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">一类是程序逻辑错误:程序执行结果不符合预期,但不会引发运行时错误
对于运行时错误程序员无法完全避免其发生,只能尽量减少其发生的概率,并在不影响程序主功能的分支流程上
“ rcover ”这些 panic,避免其因为一个panic引发整个程序的崩溃。</span></pre>
</div>
<p>Go 对于错误提供了两种处理机制:</p>
<div class="cnblogs_code">
<pre>(<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">) 通过函数返回错误类型的值来处理错误。
(</span><span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">) 通过 panic 打印程序调用枝,终止程序执行来处理错误。
所以对错误的处理也有两种方法,
一种是通过返回 个错误类型值来处理错误,
另一种是直接调用 panic 抛出错误,退出程序。
Go 是静态强类型语言,程序的大部分错误是可以在编译器检测到的,但是有些错误行为需要在运行期才能检测出来。
此种错误行为将导致程序异常退出 。其表现出的行为就和直接调用panic 一样 打印出函数调用技信息,并且终止程序执行
在实际的编程中,error和panic 的使用应该遵循如下三条原则:
</span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)程序局部代码的执行结果不符合预期,但此种行为不是运行时错误范围内预定义的错
误,此种非期望的行为不会导致程序无法提供服务,此类场景应该使用函数返回 rror 类型变量
进行错误处理。
</span><span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">)程序执行过程中发生错误,且该种错误是运行时错误范围内预定义的错误,此时 Go
语言默认的隐式处理动作就是调用 panic ,如果此种 panic 发生在程序的分支流程不影响主要更
能,则可以在发生 panic 的程序分支上游处使用 recove 进行捕获,避免引发整个程序的崩溃。
</span><span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)">)程序局部代码执行结果不符合预期,此种行为虽然不是运行时错误范围内预定义的错
误,但此种非期望的行为会导致程序无法继续提供服务,此类场景在代码中应该主动调用 panic
终止程序的执行。
进一步浓缩为两条规则
(</span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">)程序发生的错误导致程序不能容错继续执行,此时程序应该主动调用 panic 或由运行
时抛出 panic
(</span><span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">)程序虽然发生错误 但是程序能够容错继续执行,此时应该使用错误返回值的方式处理错误,
或者在可能发生运行时错误的非关键分支上使用 recover 捕获 panic</span></pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/1513688/201905/1513688-20190503173847441-586934243.png"></p>
<p> </p><br><br>
来源:https://www.cnblogs.com/liucsxiaoxiaobai/p/10805788.html
頁:
[1]