Golang异常处理-panic与recover

                                              作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

  在程序设计中,容错是相当重要的一部分工作,在 Go中它是通过错误处理来实现的,error 虽然只是一个接口,但是其变化却可以有很多,我们可以根据自己的需求来实现不同的处理。任何时候当你需要一个新的错误类型,都可以用 errors (必须先 import)包的 errors.New 函数接收合适的错误信息来创建。

一.自定义的一个错误类型

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package main import (
"errors"
"fmt"
) var (
CustomError error //用于定义错误的变量。
PromptInformation string //用于定义提示信息的变量。
) func main() {
PromptInformation = "这是自定义的一个错误类型!"
CustomError = errors.New(PromptInformation) //errors包的New方法就可以创建一个error类型的数据,不过他需要你传入一个字符串类型用于给用户的提示信息。
fmt.Printf("error: %v", CustomError)
} #以上代码执行结果如下:
error: 这是自定义的一个错误类型!

二.调用自定义的错误

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package main import (
"errors"
"fmt"
) type Customer struct {
Name string
Deposit float64
} func CustomError()error { //这是我们自己定义的错误
return errors.New("对不起,您的余额已不足。")
} func TransferAccounts(name1, name2 Customer,money float64) (Customer,Customer,error) { //这个函数是用来实现转账的功能。
if name1.Deposit - money < 0 {
return name1,name2, CustomError()
}else {
name1.Deposit = name1.Deposit - money
name2.Deposit = name2.Deposit + money
}
return name1,name2, nil
} func main() {
yzj := Customer{"尹正杰",1000000}
Linus := Customer{"林纳斯·托瓦兹",100} name1,name2,err := TransferAccounts(yzj,Linus,50000) //如果在账户余额充足的情况下,是不会报错的,我们让他输入两个人各自的余额。
if err != nil {
fmt.Println(err)
}else {
fmt.Println(name1,name2)
} name1,name2,err = TransferAccounts(yzj,Linus,600000000000000) //注意,这是第二次调转账啦。这回我们故意把转账的金额写的远远大于存款。就会抛出我们定义的错误。
if err != nil {
fmt.Println(err)
}else {
fmt.Println(name1,name2)
}
} #以上代码执行结果如下:
{尹正杰 950000} {林纳斯·托瓦兹 50100}
对不起,您的余额已不足。

三.用 fmt 创建错误对象

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package main import (
"fmt"
) type Customer struct {
Name string
Deposit float64
} func TransferAccounts(name1, name2 Customer,money float64) (Customer,Customer,error) { //这个函数是用来实现转账的功能。
if name1.Deposit - money < 0 {
return name1,name2, fmt.Errorf("对不起,【%s】的用户余额已不足!",name1.Name) //通常你想要返回包含错误参数的更有信息量的字符串就可以用可以用 fmt.Errorf() 来实现。
}else {
name1.Deposit = name1.Deposit - money
name2.Deposit = name2.Deposit + money
}
return name1,name2, nil
} func main() {
yzj := Customer{"尹正杰",1000000}
Linus := Customer{"林纳斯·托瓦兹",100} name1,name2,err := TransferAccounts(yzj,Linus,50000) //如果在账户余额充足的情况下,是不会报错的,我们让他输入两个人各自的余额。
if err != nil {
fmt.Println(err)
}else {
fmt.Println(name1,name2)
} name1,name2,err = TransferAccounts(yzj,Linus,600000000000000) //注意,这是第二次调转账啦。这回我们故意把转账的金额写的远远大于存款。就会抛出我们定义的错误。
if err != nil {
fmt.Println(err)
}else {
fmt.Println(name1,name2)
}
} #以上代码执行结果如下:
{尹正杰 950000} {林纳斯·托瓦兹 50100}
对不起,【尹正杰】的用户余额已不足!

四.运行时异常和 panic

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package main import (
"bufio"
"os"
"fmt"
) var (
String string
Input string
) func main() {
f := bufio.NewReader(os.Stdin) //读取输入的内容
for {
fmt.Print("请输入您的用户名>")
Input,_ = f.ReadString('\n') //定义一行输入的内容分隔符。
if len(Input) == 1 {
continue //如果用户输入的是一个空行就让用户继续输入。
}
fmt.Printf("您输入的是:%s",Input)
fmt.Sscan(Input,&String)
if String == "stop" {
break
}
if String == "yinzhengjie" {
fmt.Println("欢迎登陆!")
}else {
panic("您输入的用户不存在!") //该程序要求用户输入一个字符串,一旦输入的字符串不是“yinzhengjie”就让程序直接崩溃掉。
}
}
} #以上代码执行结果如下:
请输入您的用户名>yinzhengjie
您输入的是:yinzhengjie
欢迎登陆!
请输入您的用户名>linus
您输入的是:linus
panic: 您输入的用户不存在! goroutine 1 [running]:
main.main()
E:/Code/Golang/Golang_Program/错误处理/4.运行时异常和 panic.go:38 +0x3b1 Process f

五.从 panic 中恢复(Recover)

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package main import (
"fmt"
) func badCall() { //定义一个让程序运行时崩溃的函数
panic("bad end")
} func test() {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panicing %s\n", e) //我们知道这个程序已经抛出了panic的错误了,但是我们用recover函数是可以处理这个错误的,我们这里的做法就是打印这个错误的输出并且不让程序崩溃。
}
}()
badCall() //调用这个运行时崩溃的函数,因此下面的一行代码是不会被执行的,而是直接结束当前函数,而结束函数之后就会触发defer关键字,因此会被recover函数捕捉。
fmt.Printf("After bad call\r\n") // <-- wordt niet bereikt
} func main() {
fmt.Printf("Calling test\r\n")
test() //调用我们定义的函数,发现程序并没有崩溃,而是可以继续执行下一行代码的哟!
fmt.Printf("Test completed\r\n")
} #以上代码执行结果如下:
Calling test
Panicing bad end
Test completed

六.自定义包中的错误处理和 panicking

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package main import (
"fmt"
"strings"
"strconv"
) type ParseError struct { //定义一个处理错误的结构体
key int
Value string
Err error
} func (p *ParseError) String() string { //给ParseError定义一个String()方法。
return fmt.Sprintf(" [%q] type is not int!" , p.Value)
} func JudgmentType(fields []string) (numbers []int) { //这个函数是判断fields切片中的每个元素是否都可以转换成INT类型的。
if len(fields) == 0 {
panic("Nothing can be explained!")
}
for key, value := range fields {
num, err := strconv.Atoi(value) //这里是讲每一个切片元素中的字符串进行转换。
if err != nil {
panic(&ParseError{key, value, err}) //如果解析出错就将自定义的ParseError结构体的error对象返回。
}
numbers = append(numbers, num) //如果转换成int类型没有出错的话就会被追加到一个提前定义好的切片中。
}
return //我们这里可以写numbers,说白了只要写一个[]int类型的都可以,当然,如果你不写的话默认就会返回我们提前定义好的numbers哟。
} func StringParse(input string) (numbers []int, err error) { //这个函数是用来解析字符串的。
defer func() { //用recover函数来接受panic抛出的错误信息。
if ErrorOutput := recover(); ErrorOutput != nil {
var ok bool
err, ok = ErrorOutput.(error) //很显然,这里是一种断言操作,即判断是否有error类型出现。
if !ok {
err = fmt.Errorf("Parse error: %v", ErrorOutput)
}
}
}()
fields := strings.Fields(input)
numbers = JudgmentType(fields)
return
} func main() {
var yzj = []string{
"100 200 300",
"1 2 2.5 3",
"30 * 40",
"yinzhengjie Golang",
"",
} for _, ex := range yzj {
fmt.Printf("正在解析[ %q]:\n ", ex)
result, err := StringParse(ex)
if err != nil {
fmt.Println("解析结果:",err)
continue
}
fmt.Println("解析结果:",result)
}
} #以上代码执行结果如下:
正在解析[ "100 200 300"]:
解析结果: [100 200 300]
正在解析[ "1 2 2.5 3"]:
解析结果: Parse error: ["2.5"] type is not int!
正在解析[ "30 * 40"]:
解析结果: Parse error: ["*"] type is not int!
正在解析[ "yinzhengjie Golang"]:
解析结果: Parse error: ["yinzhengjie"] type is not int!
正在解析[ ""]:
解析结果: Parse error: Nothing can be explained!

七.一种用闭包处理错误的模式

 /*
#!/usr/bin/env gorun
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
EMAIL:y1053419035@qq.com
*/ package main import "fmt" func A() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in A", r)
}
}()
fmt.Println("Calling A.")
B(0)
fmt.Println("Returned normally from g.")
} func B(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in B", i)
fmt.Println("Printing in B", i)
B(i + 1) //这里是一个递归函数函数。
} func main() {
A()
fmt.Println("程序结束!")
} #以上地面执行结果如下:
Calling A.
Printing in B 0
Printing in B 1
Printing in B 2
Printing in B 3
Panicking!
Defer in B 3
Defer in B 2
Defer in B 1
Defer in B 0
Recovered in A 4
程序结束!

Golang异常处理-panic与recover的更多相关文章

  1. 【GoLang】panic defer recover 深入理解

    唉,只能说C程序员可以接受go的错误设计,相比java来说这个设计真的很差劲! 我认为知乎上说的比较中肯的: 1. The key lesson, however, is that errors ar ...

  2. GO_05_2:Golang 中 panic、recover、defer 的用法

     函数 defer 1. 它的执行方式类似其他语言中的折构函数,在函数体执行结束后按照调用顺序的 相反顺序 逐个执行 2. 即使函数发生 严重错误 也会被执行,类似于 java 中 try{...} ...

  3. Golang的panic和recover

    panic 关键字panic的作用是制造一次宕机,宕机就代表程序运行终止,但是已经“生效”的延迟函数仍会执行(即已经压入栈的defer延迟函数,panic之前的). 为什么要制造宕机呢?是因为宕机不容 ...

  4. golang之panic,recover,defer

    defer,recover: 运行时恐慌一旦被引发,就会向调用方传播直至程序崩溃. recover内建函数用于“拦截”运行时恐慌,可以使当前的程序从恐慌状态中恢复并重新获得流程控制权. recover ...

  5. GOLANG错误处理最佳方案errors wrap, Defer, Panic, and Recover

    Simple error handling primitives:        https://github.com/pkg/errors Defer, Panic, and Recover:    ...

  6. Golang 高效实践之defer、panic、recover实践

    前言 我们知道Golang处理异常是用error返回的方式,然后调用方根据error的值走不同的处理逻辑.但是,如果程序触发其他的严重异常,比如说数组越界,程序就要直接崩溃.Golang有没有一种异常 ...

  7. Golang错误处理函数defer、panic、recover、errors.New介绍

    在默认情况下,当发生错误(panic)后,程序就会终止运行 如果发生错误后,可以捕获错误,并通知管理人员(邮件或者短信),程序还可以继续运行,这当然无可厚非 errors.New("错误信息 ...

  8. golang错误处理机制:panic与recover

    原文地址:http://www.niu12.com/article/14 panic知识点 package main import ( "fmt" "github.com ...

  9. panic和recover的使用规则

    转自个人博客 chinazt.cc 在上一节中,我们介绍了defer的使用. 这一节中,我们温习一下panic和recover的使用规则. 在golang当中不存在tye ... catch 异常处理 ...

随机推荐

  1. Elasticsearch Java Rest Client API 整理总结 (二) —— SearchAPI

    目录 引言 Search APIs Search API Search Request 可选参数 使用 SearchSourceBuilder 构建查询条件 指定排序 高亮请求 聚合请求 建议请求 R ...

  2. Security3: 架构和权限

    架构(Schema)是数据库对象(比如,Table,View,存储过程等)的容器,授予用户对Schema访问的权限,就是授予用户对Schema下所有object的访问权限. 一,架构(Schema)是 ...

  3. Partition2:对现有表分区

    在SQL Server中,普通表可以转化为分区表,而分区表不能转化为普通表,普通表转化成分区表的过程是不可逆的,将普通表转化为分区表的方法是: 在分区架构(Partition Scheme)上创建聚集 ...

  4. ABP中文文档及源码说明

    目录 ABP.ModuleZero(ABP自带的一套用户模块) 版本管理(Edition) 特性管理(Feature)

  5. openstack horizon 开发第二天

    依照上次的简单的仪表盘添加动作额外添加或修改的文件mydashboard/├── mypanel│   ├── forms.py│   ├── tables.py│   ├── templates│  ...

  6. Vue 路由详解

    Vue 路由详解 对于前端来说,其实浏览器配合超级连接就很好的实现了路由功能.但是对于单页面应用来说,浏览器和超级连接的跳转方式已经不能适用,所以各大框架纷纷给出了单页面应用的解决路由跳转的方案. V ...

  7. Linux内核分析 实验三:跟踪分析Linux内核的启动过程

    贺邦 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程 http://mooc.study.163.com/course/USTC-1000029000 一. 实验过程 ...

  8. We're Chronos! ----- Meet the team 团队作业 #1

    Meet Us —————————————————La ligne de séparation————————————————— Kane Lim [林珣玙] < PM, Programmer ...

  9. iptables之四表五链

    iptables可谓是SA的看家本领,需要着重掌握.随着云计算的发展和普及,很多云厂商都提供类似安全组产品来修改机器防火墙. iptables概念 iptables只是Linux防火墙的管理工具而已. ...

  10. 浅谈final修饰的变量

    一直大概的知道final关键字的作用,但是自己实际工作中却很少用,除非在声明一些常量值的时候,今天忽然自己在项目中用一个map进行存储一些值.一开始我只是用private修饰的,心里想的是如果fina ...