[toc]

defer调用

介绍: 确保在函数调用结束时发生 defer就是在程序或函数调用结束后,然后执行defer后面的语句 例如:

package main

import "fmt"

func tryDefer(){

	fmt.Println(1)
	fmt.Println(2)
	fmt.Println(3)
	//打印:1、2、3

	defer fmt.Println(1)
	defer fmt.Println(2)
	fmt.Println(3)
	//打印:3、2、1
	
	defer fmt.Println(1)
	defer fmt.Println(2)
	fmt.Println(3)
	return
	fmt.Println(4)
	//打印:3、2、1
	
	defer fmt.Println(1)
	defer fmt.Println(2)
	fmt.Println(3)
	panic("error occurred")
	fmt.Println(4)
	//打印:3、2、1
	//panic: error occurred
}
func mian(){
	tryDefer()
}
  1. 在这个例子第二个打印中,前面加上defer关键字后,其后面的 fmt.Println(1)和 fmt.Println(2)都会该程序所有语句执行结束后执行,而defer里面本身是一个栈(先进后出),所有会先打印2,然后打印1。
  2. 在这个例子的第三个打印中,我们加了关键字return其输出结果还是1、2、3,这里是因为return后程序就会退出,当程序退出前defer仍然会执行
  3. 在这个例子的第四个打印中,我们使用到函数panic()程序会报错退出,但是在结束之前仍然会执行defer

还有一个例子:

func writeFile(filename string){
	file, err := os.Create(filename)  //os.Create()建立文件
	if err != nil{  //err不为空,则文件建立失败,panic输出err信息
		panic(err)
	}
	
	defer File.close() //最后将文件关闭
	
	writer := bufio.NerWriter(file)
	defer writer.Flush()
	
	f := fibonacci()
	for i := 0; i < 20; i++{
		fmt.Fprintln(writer, f())
	}
}

func fibonacci() func() int {
	a := 0, b := 1
	return func() int{
		a, b = b, a+b
		return a
	}
}

func mian(){
	writerFile("fib.txt")
}

错误处理

前面我们提到了panic()函数其实它也是用来做错误处理的,但是,使用panic程序会挂掉,这是我们不希望看到的。

file, err := os.Open("abc.txt")
if err != nil{
	panic(err)
}

像上面代码使用panic后程序就会把err中的内容输出,并且挂掉

file, err := os.Open("abc.txt")
if err != nil{
	pathError, ok := err.(*os.PathError)
	if !ok{
		fmt.Println(pathError)
	}else {
		fmt.Println("unknown error ", err)
	}
}

使用这种处理方式更好

panic&recover

panic

「Golang成长之路」错误处理与资源管理篇

recover

「Golang成长之路」错误处理与资源管理篇

package main
import "fmt"

func tryRecover(){
	defer func(){
	r := recover()
	err, ok := r.(error)
	if ok{
		fmt.Println("Error occurred:", err)
	}else{
		panic("I don`t kown what to : %v", r)
	}
  }()

panic("123")

}

func mian(){
	tryRecover()

}

统一处理

服务器统一出错: web.go:

package main

import (
   "learngo/errhandling/filelistingserver/handle"
 "log" 
  "net/http"
  "os"
)

//定义一个函数类型
type Apphandle func(write http.ResponseWriter,
  request *http.Request) error

//函数式编程,传入一个函数Apphandle类型的函数类型,返回一个匿名函数func(http.ResponseWriter,*http.Request)
func errwarappler(handler Apphandle) func(http.ResponseWriter, *http.Request) {
   return func(writer http.ResponseWriter, request *http.Request) {
      //匿名函数func(http.ResponseWriter,*http.Request)内部构造:

  defer func(){
         r := recover()
         if r != nil{
            log.Printf("panic:%v\n", r)
           //写入返回err信息
            http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
            return
  }

      }()

      err := handler(writer, request)
      if err != nil{
         log.Printf("Error handle request:%s", err.Error())

          if userErr, ok := err.(userError); ok{
            http.Error(writer, userErr.Massage(), http.StatusBadRequest)
            return
  }

         code := http.StatusOK
  switch{
         case os.IsNotExist(err):  //情况一:文件不存在
  code = http.StatusNotFound
  case os.IsPermission(err): //情况二:没有权限
  code = http.StatusForbidden
  default:
            code = http.StatusInternalServerError

  }
         http.Error(writer, http.StatusText(code), code)
      }

   }
}

type userError interface{
   error
  Massage() string
}

func main() {
   //传入部分
  http.HandleFunc("/",errwarappler(handle.Handler))

   err := http.ListenAndServe(":8888" , nil)
   if err != nil{
      panic(err)
   }
}

handle.go:

package handle

import (
   "fmt"
 "io/ioutil"
  "net/http"
  "os"
  "strings"
)

const perfix = "/list/"

type userError string

func (e userError)Error() string{
   return e.Massage()
}

func (e userError)Massage() string {
   return string(e)
}

func Handler(write http.ResponseWriter, Request *http.Request) error{

   if strings.Index(Request.URL.Path, perfix) != 0 {
      return userError(fmt.Sprintf("path %s must start" +
         "whit %s",Request.URL.Path, perfix))
   }

   path := Request.URL.Path[len(perfix):]
   file , err := os.Open(path)
   if err != nil{
      //将错误信息返回出去
  return err
   }
   defer file.Close()

   all, err := ioutil.ReadAll(file)
   if err != nil{
      return err
   }

   write.Write(all)
   return nil
}

注意:error和panic 在意料之外的情况:使用panic 在意料之内的情况:使用error