zoukankan      html  css  js  c++  java
  • Golang异常处理-panic与recover

                        Golang异常处理-panic与recover

                                                  作者:尹正杰

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

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

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

     1 /*
     2 #!/usr/bin/env gorun
     3 @author :yinzhengjie
     4 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/
     5 EMAIL:y1053419035@qq.com
     6 */
     7 
     8 package main
     9 
    10 import (
    11     "errors"
    12     "fmt"
    13 )
    14 
    15 var (
    16     CustomError error            //用于定义错误的变量。
    17     PromptInformation string    //用于定义提示信息的变量。
    18 )
    19 
    20 func main() {
    21     PromptInformation = "这是自定义的一个错误类型!"
    22     CustomError = errors.New(PromptInformation) //errors包的New方法就可以创建一个error类型的数据,不过他需要你传入一个字符串类型用于给用户的提示信息。
    23     fmt.Printf("error: %v", CustomError)
    24 }
    25 
    26 
    27 
    28 #以上代码执行结果如下:
    29 error: 这是自定义的一个错误类型!

    二.调用自定义的错误

     1 /*
     2 #!/usr/bin/env gorun
     3 @author :yinzhengjie
     4 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/
     5 EMAIL:y1053419035@qq.com
     6 */
     7 
     8 package main
     9 
    10 import (
    11     "errors"
    12     "fmt"
    13 )
    14 
    15 type Customer struct {
    16     Name string
    17     Deposit float64
    18 }
    19 
    20 func CustomError()error {  //这是我们自己定义的错误
    21     return errors.New("对不起,您的余额已不足。")
    22 }
    23 
    24 func TransferAccounts(name1, name2 Customer,money float64) (Customer,Customer,error) {    //这个函数是用来实现转账的功能。
    25     if name1.Deposit - money < 0 {
    26         return name1,name2, CustomError()
    27     }else {
    28          name1.Deposit = name1.Deposit - money
    29          name2.Deposit = name2.Deposit + money
    30     }
    31     return name1,name2, nil
    32 }
    33 
    34 func main() {
    35     yzj := Customer{"尹正杰",1000000}
    36     Linus := Customer{"林纳斯·托瓦兹",100}
    37 
    38     name1,name2,err := TransferAccounts(yzj,Linus,50000) //如果在账户余额充足的情况下,是不会报错的,我们让他输入两个人各自的余额。
    39     if err != nil {
    40         fmt.Println(err)
    41     }else {
    42         fmt.Println(name1,name2)
    43     }
    44 
    45     name1,name2,err = TransferAccounts(yzj,Linus,600000000000000)  //注意,这是第二次调转账啦。这回我们故意把转账的金额写的远远大于存款。就会抛出我们定义的错误。
    46     if err != nil {
    47         fmt.Println(err)
    48     }else {
    49         fmt.Println(name1,name2)
    50     }
    51 }
    52 
    53 
    54 
    55 #以上代码执行结果如下:
    56 {尹正杰 950000} {林纳斯·托瓦兹 50100}
    57 对不起,您的余额已不足。

    三.用 fmt 创建错误对象

     1 /*
     2 #!/usr/bin/env gorun
     3 @author :yinzhengjie
     4 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/
     5 EMAIL:y1053419035@qq.com
     6 */
     7 
     8 package main
     9 
    10 import (
    11     "fmt"
    12 )
    13 
    14 type Customer struct {
    15     Name string
    16     Deposit float64
    17 }
    18 
    19 func TransferAccounts(name1, name2 Customer,money float64) (Customer,Customer,error) {    //这个函数是用来实现转账的功能。
    20     if name1.Deposit - money < 0 {
    21         return name1,name2, fmt.Errorf("对不起,【%s】的用户余额已不足!",name1.Name)    //通常你想要返回包含错误参数的更有信息量的字符串就可以用可以用 fmt.Errorf() 来实现。
    22     }else {
    23         name1.Deposit = name1.Deposit - money
    24         name2.Deposit = name2.Deposit + money
    25     }
    26     return name1,name2, nil
    27 }
    28 
    29 func main() {
    30     yzj := Customer{"尹正杰",1000000}
    31     Linus := Customer{"林纳斯·托瓦兹",100}
    32 
    33     name1,name2,err := TransferAccounts(yzj,Linus,50000) //如果在账户余额充足的情况下,是不会报错的,我们让他输入两个人各自的余额。
    34     if err != nil {
    35         fmt.Println(err)
    36     }else {
    37         fmt.Println(name1,name2)
    38     }
    39 
    40     name1,name2,err = TransferAccounts(yzj,Linus,600000000000000)  //注意,这是第二次调转账啦。这回我们故意把转账的金额写的远远大于存款。就会抛出我们定义的错误。
    41     if err != nil {
    42         fmt.Println(err)
    43     }else {
    44         fmt.Println(name1,name2)
    45     }
    46 }
    47 
    48 
    49 
    50 #以上代码执行结果如下:
    51 {尹正杰 950000} {林纳斯·托瓦兹 50100}
    52 对不起,【尹正杰】的用户余额已不足!

    四.运行时异常和 panic

     1 /*
     2 #!/usr/bin/env gorun
     3 @author :yinzhengjie
     4 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/
     5 EMAIL:y1053419035@qq.com
     6 */
     7 
     8 
     9 package main
    10 
    11 import (
    12     "bufio"
    13     "os"
    14     "fmt"
    15 )
    16 
    17 var   (
    18     String string
    19     Input string
    20 )
    21 
    22 func main()  {
    23     f := bufio.NewReader(os.Stdin) //读取输入的内容
    24     for {
    25         fmt.Print("请输入您的用户名>")
    26         Input,_ = f.ReadString('
    ') //定义一行输入的内容分隔符。
    27         if len(Input) == 1 {
    28             continue //如果用户输入的是一个空行就让用户继续输入。
    29         }
    30         fmt.Printf("您输入的是:%s",Input)
    31         fmt.Sscan(Input,&String)
    32         if String == "stop" {
    33             break
    34         }
    35         if String == "yinzhengjie" {
    36             fmt.Println("欢迎登陆!")
    37         }else {
    38             panic("您输入的用户不存在!")        //该程序要求用户输入一个字符串,一旦输入的字符串不是“yinzhengjie”就让程序直接崩溃掉。
    39         }
    40     }
    41 }
    42 
    43 
    44 
    45 #以上代码执行结果如下:
    46 请输入您的用户名>yinzhengjie
    47 您输入的是:yinzhengjie
    48 欢迎登陆!
    49 请输入您的用户名>linus
    50 您输入的是:linus
    51 panic: 您输入的用户不存在!
    52 
    53 goroutine 1 [running]:
    54 main.main()
    55     E:/Code/Golang/Golang_Program/错误处理/4.运行时异常和 panic.go:38 +0x3b1
    56 
    57 Process f

    五.从 panic 中恢复(Recover)

     1 /*
     2 #!/usr/bin/env gorun
     3 @author :yinzhengjie
     4 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/
     5 EMAIL:y1053419035@qq.com
     6 */
     7 
     8 package main
     9 
    10 import (
    11     "fmt"
    12 )
    13 
    14 func badCall() {        //定义一个让程序运行时崩溃的函数
    15     panic("bad end")
    16 }
    17 
    18 func test() {
    19     defer func() {
    20         if e := recover(); e != nil {
    21             fmt.Printf("Panicing %s
    ", e)   //我们知道这个程序已经抛出了panic的错误了,但是我们用recover函数是可以处理这个错误的,我们这里的做法就是打印这个错误的输出并且不让程序崩溃。
    22         }
    23     }()
    24     badCall()    //调用这个运行时崩溃的函数,因此下面的一行代码是不会被执行的,而是直接结束当前函数,而结束函数之后就会触发defer关键字,因此会被recover函数捕捉。
    25     fmt.Printf("After bad call
    ") // <-- wordt niet bereikt
    26 }
    27 
    28 func main() {
    29     fmt.Printf("Calling test
    ")
    30     test()    //调用我们定义的函数,发现程序并没有崩溃,而是可以继续执行下一行代码的哟!
    31     fmt.Printf("Test completed
    ")
    32 }
    33 
    34 
    35 
    36 
    37 #以上代码执行结果如下:
    38 Calling test
    39 Panicing bad end
    40 Test completed

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

     1 /*
     2 #!/usr/bin/env gorun
     3 @author :yinzhengjie
     4 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/
     5 EMAIL:y1053419035@qq.com
     6 */
     7 
     8 package main
     9 
    10 import (
    11     "fmt"
    12     "strings"
    13     "strconv"
    14 )
    15 
    16 
    17 type ParseError struct {  //定义一个处理错误的结构体
    18     key int
    19     Value string
    20     Err error
    21 }
    22 
    23 func (p *ParseError) String() string {  //给ParseError定义一个String()方法。
    24     return fmt.Sprintf(" [%q] type is not int!" , p.Value)
    25 }
    26 
    27 
    28 func JudgmentType(fields []string) (numbers []int) {  //这个函数是判断fields切片中的每个元素是否都可以转换成INT类型的。
    29     if len(fields) == 0 {
    30         panic("Nothing can be explained!")
    31     }
    32     for key, value := range fields {
    33         num, err := strconv.Atoi(value)    //这里是讲每一个切片元素中的字符串进行转换。
    34         if err != nil {
    35             panic(&ParseError{key, value, err})  //如果解析出错就将自定义的ParseError结构体的error对象返回。
    36         }
    37         numbers = append(numbers, num)        //如果转换成int类型没有出错的话就会被追加到一个提前定义好的切片中。
    38     }
    39     return  //我们这里可以写numbers,说白了只要写一个[]int类型的都可以,当然,如果你不写的话默认就会返回我们提前定义好的numbers哟。
    40 }
    41 
    42 
    43 func StringParse(input string) (numbers []int, err error) {     //这个函数是用来解析字符串的。
    44     defer func() {                                              //用recover函数来接受panic抛出的错误信息。
    45         if ErrorOutput := recover(); ErrorOutput != nil {
    46             var ok  bool
    47             err, ok = ErrorOutput.(error)                        //很显然,这里是一种断言操作,即判断是否有error类型出现。
    48             if !ok {
    49                 err = fmt.Errorf("Parse error: %v", ErrorOutput)
    50             }
    51         }
    52     }()
    53     fields := strings.Fields(input)
    54     numbers = JudgmentType(fields)
    55     return
    56 }
    57 
    58 func main() {
    59     var yzj = []string{
    60         "100 200 300",
    61         "1 2 2.5 3",
    62         "30 * 40",
    63         "yinzhengjie Golang",
    64         "",
    65     }
    66 
    67     for _, ex := range yzj {
    68         fmt.Printf("正在解析[ %q]:
     ", ex)
    69         result, err := StringParse(ex)
    70         if err != nil {
    71             fmt.Println("解析结果:",err)
    72             continue
    73         }
    74         fmt.Println("解析结果:",result)
    75     }
    76 }
    77 
    78 
    79 
    80 #以上代码执行结果如下:
    81 正在解析[ "100 200 300"]:
    82  解析结果: [100 200 300]
    83 正在解析[ "1 2 2.5 3"]:
    84  解析结果: Parse error:  ["2.5"] type is not int!
    85 正在解析[ "30 * 40"]:
    86  解析结果: Parse error:  ["*"] type is not int!
    87 正在解析[ "yinzhengjie Golang"]:
    88  解析结果: Parse error:  ["yinzhengjie"] type is not int!
    89 正在解析[ ""]:
    90  解析结果: Parse error: Nothing can be explained!

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

     1 /*
     2 #!/usr/bin/env gorun
     3 @author :yinzhengjie
     4 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/
     5 EMAIL:y1053419035@qq.com
     6 */
     7 
     8 package main
     9 
    10 import "fmt"
    11 
    12 func A() {
    13     defer func() {
    14         if r := recover(); r != nil {
    15             fmt.Println("Recovered in A", r)
    16         }
    17     }()
    18     fmt.Println("Calling A.")
    19     B(0)
    20     fmt.Println("Returned normally from g.")
    21 }
    22 
    23 func B(i int) {
    24     if i > 3 {
    25         fmt.Println("Panicking!")
    26         panic(fmt.Sprintf("%v", i))
    27     }
    28     defer fmt.Println("Defer in B", i)
    29     fmt.Println("Printing in B", i)
    30     B(i + 1)          //这里是一个递归函数函数。
    31 }
    32 
    33 func main() {
    34     A()
    35     fmt.Println("程序结束!")
    36 }
    37 
    38 
    39 
    40 #以上地面执行结果如下:
    41 Calling A.
    42 Printing in B 0
    43 Printing in B 1
    44 Printing in B 2
    45 Printing in B 3
    46 Panicking!
    47 Defer in B 3
    48 Defer in B 2
    49 Defer in B 1
    50 Defer in B 0
    51 Recovered in A 4
    52 程序结束!
  • 相关阅读:
    CMS 阶段性了结
    Java 代码监控 JVM 运行状态 —— 记一次 JVM 调优的毛招
    ClassLoader PPT 总结
    记一次 CMS 回收异常问题 —— 跨代引用和循环依赖
    读懂 GC 日志
    ClassLoader 学习笔记
    java 可见性简单总结
    Kafka 基本原理整理
    秒杀系统个人总结
    centos 7 mongodb4.0 安装配置
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/7823362.html
Copyright © 2011-2022 走看看