一: panic和recover

作用:panic 用来主动抛出错误; recover 用来捕获 panic 抛出的错误。

概述: ,引发panic有两种情况 )程序主动调用panic函数 )程序产生运行时错误,由运行时检测并抛出 过程: ! 发生 panic 后,程序会从调用 panic的函数位置或发生panic 的地方立即返回,逐层向上执行函数的defer语句, 然后逐层打印函数调用堆栈,直到被 recover 捕获或运行到最外层函数而退出。 ! panic的参数是一个空接口类型 interface{},所以任意类型的变量都可以传递给 panic(xxx) ! panic 不但可以在函数正常流程中抛出,在 defer 逻辑里也可以再次调用 panic 或抛出 panic a defer 里面的 panic 能够被后续执行的 defer 捕获。 recover()用来捕获 panic,阻止panic继续向上传递recover()和defer一起使用 ,但是recover() 只  有在defer后面的函数体内被直接调用才能捕获panic终止异常否则返回 nil,异常继续向外传递
//这会获取失败
defer recover()
defer fmt.println(recover())
//嵌套两层也会获取失败
defer func() {
func(){
println("defer inner")
recover() //无效
}()
}

  以下场景会获取成功

package main
func f(){
defer func() {
println("defer inner")
recover()
}()
}
func except() {
recover()
}
func test(){
defer except()
panic("test panic")
}
func main() {
f()
except()
test()
}

  可以有多个panic被抛出,连续多个panic的场景只能出现在延迟调用里面,否则不会出现多个panic被抛出的场景。但只有最后一次panic能被捕获

package main

import "fmt"

func main(){
defer func() {
if err :=recover();err !=nil{
fmt.Println(err)
}
}()
  //只有最后一次panic调用被捕获
defer func() {
panic("first defer panic") //打印结构是这个
}()
defer func() {
panic("second defer panic")
}()
panic("main body panic")
}

  包中 in it 函数引发的 panic 只能在 in it 函数中捕获,在 main 中无法被捕获,原因是 in it
数先于 main 执行,函数并不能捕获内部新启动的 goroutine 所抛出的 panic 。

package main

import (
"fmt"
"time"
) func do() {
//这里并不能获取da函数中的panic
defer func() {
if err := recover();err != nil{
fmt.Println(err)
}
}()
go da()
go db()
time.Sleep(*time.Second)
}
func da(){
panic("panic da")
for i := ; i<; i++{
fmt.Println(i)
}
}
func db(){
for i := ; i<; i++{
fmt.Println(i)
}
}
func main() {
fmt.Println(do)
fmt.Println(da)
fmt.Println(db)
}

  示例

package main

import "fmt"

//系统抛异常
func test01() {
a := []int{, , , , }
//a[10] = 123
index :=
a[index] =
}
func main() {
//panic: runtime error: index out of range
//test01()
//test02()
//test03()
test04()
}
//自己抛异常
func test02() {
getCircleArea(-)
}
func getCircleArea(radius float32) (area float32) {
if radius < {
//抛异常
panic("半径不能为负数")
}
return 3.14 * radius * radius
}
func test03() {
//延时执行匿名函数
//延时到什么时候?要么正常结束,要么出异常
//recover()是复活的意思
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
//会报错
getCircleArea(-)
//下句话没有打印
fmt.Println("这里有没有执行?")
}
func test04() {
test03()
fmt.Println("GAME OVER")
}

返回异常

package main

import (
"errors"
"fmt"
) //算半径
func getCircleArea(radius float32) (ret float32, err error) {
if radius < {
//创建异常
err = errors.New("沙雕,半径不能为负数")
return
}
ret = 3.14 * radius * radius
return
} func main() {
ret, err := getCircleArea()
if err != nil {
fmt.Println(err)
} else {
fmt.Println("ret=", ret)
}
}

使用场景:

()程序遇到了无法正常执行下去的错误,主动调用 panic 函数结束程序运行
()在调试程序时,通过主动调用 panic 实现快速退出, panic 打印出的堆枝能够更快地定位错误。

为了保证程序的健壮性,需要主动在程序的分支流程上使用 recover()拦截运行时错误。

Go 提供了两种处理错误 方式,
一 种是借助 panic和 recover 的抛出捕获机制,
另一种使用error 错误类型

error:

go 语言内置错误接口类型
任何类型只要实现 error() string 方法,都可以传递 eηor接口类型变量。
Go 语言典型的错误处理方式是将error作为函数最后一个返回值 在调用函数
通过检测其返回的error值是否为nil来进行错误处理。
type  error interface{
Error() string
}

  

• 在多个返回值的函数中,error 通常作为函数最后一个返回值

• 如果一个函数返回error 类型变量 ,则先用if语句处理 error != nil 异常场景,正常逻辑放到 if 语句块的后面,
保持代码平坦。
• defer 吾句应该放到四判断的后面,不然有可能产生 panic
• 在错误逐级向上传递的过程中,错误信息应该不断地丰富和完善,而不是简单地抛出下层调用的错误。
这在错误日志分析 非常有用和友好。

错误和异常

广义上的错误: 发生非期望的行为。
狭义的错误:发生非期望的己知行为,这里的己知是指错误的类型是预料并定义好的。
异常: 发生非期待的未知行为。这里的未知是指错误的类型不在预先定义的范围内。异常又被称为未捕获的错误
( untrapped error )。程序在执行时发生未预先定义的错误,程序编译器和运行时都没有及时将其捕获处理。
而是由操作系统进行异常处理,比如 语言程序里面经常出现的 Segmentation Fault (段异常错误),这个就属
于异常范畴。

Go 是一门类型安全的语言,其运行时不 出现这种编译器和运行时都无法捕获的错 ,也就是说,
不会出现 untrapped error ,所以从这个角度来说, Go 语言不存在所谓的异常,出现的“异常”全是错误

Go 程序需要处理的这些错误可 分为两类
一类是运行时错误( runtime errors ),此类错误语言的运行时能够捕获,并采取措施一一隐式或显式地抛出 panic
一类是程序逻辑错误:程序执行结果不符合预期,但不会引发运行时错误
对于运行时错误程序员无法完全避免其发生,只能尽量减少其发生的概率,并在不影响程序主功能的分支流程上
“ rcover ”这些 panic,避免其因为一个panic引发整个程序的崩溃。

Go 对于错误提供了两种处理机制:

() 通过函数返回错误类型的值来处理错误。
() 通过 panic 打印程序调用枝,终止程序执行来处理错误。
所以对错误的处理也有两种方法,
一种是通过返回 个错误类型值来处理错误,
另一种是直接调用 panic 抛出错误,退出程序。
Go 是静态强类型语言,程序的大部分错误是可以在编译器检测到的,但是有些错误行为需要在运行期才能检测出来。
此种错误行为将导致程序异常退出 。其表现出的行为就和直接调用panic 一样 打印出函数调用技信息,并且终止程序执行
在实际的编程中,error和panic 的使用应该遵循如下三条原则:
)程序局部代码的执行结果不符合预期,但此种行为不是运行时错误范围内预定义的错
误,此种非期望的行为不会导致程序无法提供服务,此类场景应该使用函数返回 rror 类型变量
进行错误处理。
)程序执行过程中发生错误,且该种错误是运行时错误范围内预定义的错误,此时 Go
语言默认的隐式处理动作就是调用 panic ,如果此种 panic 发生在程序的分支流程不影响主要更
能,则可以在发生 panic 的程序分支上游处使用 recove 进行捕获,避免引发整个程序的崩溃。
)程序局部代码执行结果不符合预期,此种行为虽然不是运行时错误范围内预定义的错
误,但此种非期望的行为会导致程序无法继续提供服务,此类场景在代码中应该主动调用 panic
终止程序的执行。
进一步浓缩为两条规则
()程序发生的错误导致程序不能容错继续执行,此时程序应该主动调用 panic 或由运行
时抛出 panic
()程序虽然发生错误 但是程序能够容错继续执行,此时应该使用错误返回值的方式处理错误,
或者在可能发生运行时错误的非关键分支上使用 recover 捕获 panic

go语言之抛出异常的更多相关文章

  1. 05. Go 语言函数

    Go 语言函数 函数是组织好的.可重复使用的.用来实现单一或相关联功能的代码段,其可以提高应用的模块性和代码的重复利用率. Go 语言支持普通函数.匿名函数和闭包,从设计上对函数进行了优化和改进,让函 ...

  2. Swift 中异常抛出和四种异常处理

    在Swift中你可以像其他语言一样抛出异常处理异常,今天我们就详细地说说Swift中的异常抛出和处理. 在一开始我们要定义错误或者说是异常,Swift中的一些简单异常可以使用枚举定义,注意这个枚举要继 ...

  3. 一文读懂Lua元表

    元表 Lua语言中的每种类型的值都有一套可预见的操作集合.例如,我们可以将数字相加,可以连接字符串,还可以在表中插入键值对等,但是我们无法将两个表相加,无法对函数作比较,也无法调用一个字符串,除非使用 ...

  4. Go基础3:函数、结构体、方法、接口

    目录 1. 函数 1.1 函数返回值 同一种类型返回值 带变量名的返回值 函数中的参数传递 函数变量 1.2 匿名函数--没有函数名字的函数 在定义时调用匿名函数 将匿名函数赋值给变量 匿名函数用作回 ...

  5. 【Java面试题】22 JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?

    throws是获取异常throw是抛出异常try是将会发生异常的语句括起来,从而进行异常的处理,catch是如果有异常就会执行他里面的语句,而finally不论是否有异常都会进行执行的语句. thro ...

  6. 编译器开发系列--Ocelot语言2.变量引用的消解

    "变量引用的消解"是指确定具体指向哪个变量.例如变量"i"可能是全局变量i,也可能是静态变量i,还可能是局部变量i.通过这个过程来消除这样的不确定性,确定所引用 ...

  7. AVL树原理及实现(C语言实现以及Java语言实现)

    欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. AVL定义 AVL树是一种改进版的搜索二叉树.对于一般的搜索二叉树而言,如果数据恰好 ...

  8. javascript 核心语言笔记 5 - 语句

    表达式在 JavaScript 中是短语(phrases),那么语句(statements)就是 JavaScript 整句或命令,语句以分号结束.表达式计算出一个值,语句用来执行以使某件事情发生 表 ...

  9. Module Zero之语言管理

    返回<Module Zero学习目录> 概览介绍 如何开启 管理语言 管理本地化文本 概览介绍 ABP定义了一个健壮的UI本地化系统,它可用于服务端和客户端.它允许在不同的资源中(Reso ...

随机推荐

  1. PHP asXML() 函数

    实例 格式化 XML(版本 1.0)中的 SimpleXML 对象的数据: <?php$note=<<<XML<note>高佣联盟 www.cgewang.com& ...

  2. 5.15 省选模拟赛 T1 点分治 FFT

    LINK:5.15 T1 对于60分的暴力 都很水 就不一一赘述了. 由于是询问所有点的这种信息 确实不太会. 想了一下 如果只是询问子树内的话 dsu on tree还是可以做的. 可以自己思考一下 ...

  3. linux条件测试操作(test)和if判断语句,while循环语句,break控制语句和for循环和case多分枝语句和select语句

    条件测试操作 条件测试是专为影响"$?"的操作,是条件转移.循环语句的基础   test测试命令: test 用途:测试特定的表达式是否成立,当条件成立时,命令执行后的返回值为0, ...

  4. Raft协议理解

    raft协议最关键的部分是领导选举和日志复制 日志复制 日志匹配原则:如果两个日志在相同索引位置的entry的任期号相同,那么这两个日志从头到这个索引位置之前完全相同. 日志匹配原则可以解释为如下两条 ...

  5. 分类模型的F1-score、Precision和Recall 计算过程

    分类模型的F1分值.Precision和Recall 计算过程 引入 通常,我们在评价classifier的性能时使用的是accuracy 考虑在多类分类的背景下 accuracy = (分类正确的样 ...

  6. property补充

    property补充 # class Foo: # @property # def AAA(self): # print('get的时候运行我啊') # # @AAA.setter # def AAA ...

  7. JVM补充篇

    1.对象分配原则 1)对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC 2)大对象直接进入老年代(大对象是指需要大量连续内存空间的对象),这样做的目的是避免在E ...

  8. 记Java中有关内存的简单认识

    一.Java内存划分 分为五个部分,可以参考这篇笔记简单认识一下: https://www.cnblogs.com/unleashed/p/13268027.html 栈 堆 方法区 本地方法栈 寄存 ...

  9. 搞大数据,Java 工程师需要掌握哪些知识?

    先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个有颜值却假装靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有一线大厂整理的面试题,以及我的 ...

  10. css笔记 定位的分类

    定位:解决元素摆放的问题 使用定位可以将元素摆放到任意位置 分类 1.默认的定位 块元素垂直排列,行内元素左右排列,称之为流 流:元素有序排列而形成的队伍 特殊的定位 浮动定位:可以让块元素左右排列 ...