Go语言学习笔记 第八章 错误处理 和资源管理

0x00 defer 调用

  1. defer 可以确保在函数返回时 语句的调用发生
  2. 参数在defer时计算,即defer fmt.Println(i) 这个i的值在defer时就已经传入给了Println ,不论defer后i怎么变,在最后执行Println这个函数时,输出的都是defer时传入给它的值
  3. defer栈为先进后出
package main

import "fmt"

func testDefer() {
	defer fmt.Println(1) //在函数返回之间执行该语句
	defer fmt.Println(2) //defer相当于一个栈,加了defer的语句按照顺序入栈,然后在函数返回前依次出栈
	fmt.Println(3) //执行结果 3 2 1 
}

func main() {
	testDefer()
}

fmt.Println()

fmt.Fprintln(w io.Writer,a ... interface{}) //输出到io.Write

fmt.Printf() 格式化输出

fmt.Fpintf(w io.Writer,格式化字符串,参数) 格式化输出到io.Write

fmt.Sprintf(format string,a ... interface{}) 返回一个格式化字符串

fmt.Sprintln(a ...interface{}) 返回一个字符串

向文件中写入斐波那契数列:

defer.go

package main

import (
	"bufio"
	"fmt"
	"learngo/functional/fibanacc/fib"
	"os"
)

func writeFile(filename string) {
	file, err := os.Create(filename) //创建一个文件
	if err != nil {
		panic(err)
	}
	defer file.Close() //删除文件句柄

	//先向内存中去写,当内存写入到一定程度,再整块写入磁盘更快
	writer := bufio.NewWriter(file) //将file封装成write
	defer writer.Flush()            //将内存中的内容写入到文件中

	generateNumber := fib.Fibanacci()
	for i := 0; i < 20; i++ {
		//输出到io.Writer
		fmt.Fprintln(writer, generateNumber())
	}
}
func main() {
	writeFile("fib.txt")
}

fib/fib.go


package fib

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

何时使用defer调用

Open/Close

Lock/Unlock

PrintHeader/PrintFooter

0x02 错误处理的概念

package main

import (
	"bufio"
	"fmt"
	"learngo/functional/fibanacc/fib"
	"os"
)

func writeFile(filename string) {
	// file, err := os.Create(filename) //创建一个文件
	//如果文件已经存在就报错
	file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666)
	if err != nil {
		//err 是一个鸭子类型(接口) 这里err中封装了一个类型为*os.PathError的东西,
		//err.(*os.PathError) 获取封装的值
		//如果这个err不是 封装的 *os.PathError 那么就报错
		if pathError, ok := err.(*os.PathError); !ok {
			panic(err)
		} else {
			//如果这个err封装的是 *os.PathError 就自定义错误处理
			fmt.Printf("操作:%s 错误原因:%s 错误路径:%s\n", pathError.Op, pathError.Err, pathError.Path)
		}
	}
	defer file.Close() //删除文件句柄

	//先向内存中去写,当内存写入到一定程度,再整块写入磁盘更快
	writer := bufio.NewWriter(file) //将file封装成write
	defer writer.Flush()            //将内存中的内容写入到文件中

	generateNumber := fib.Fibanacci()
	for i := 0; i < 20; i++ {
		//输出到io.Writer
		fmt.Fprintln(writer, generateNumber())
	}
}
func main() {
	writeFile("fib.txt")
}

0x03 实现统一的错误处理逻辑

这里只是提供一种统一错误处理的思路:

在功能层 函数的外层加层 ErrorWrapper 将函数包起来,ErrorWrapper层中进行统一的错误处理

至于具体如何包?可以直接包,也可以使用函数式编程,传入一个函数,然后返回一个函数

/*
 * @Author: your name
 * @Date: 2020-11-04 11:03:35
 * @LastEditTime: 2020-11-04 13:18:35
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /learngo/errorhandling/filelistserver/filelistserver.go
 */
package main

import (
	"learngo/errorhandling/filelistserver/filelist"
	"net/http"
	"os"
)

// func errorWrapper(writer http.ResponseWriter, request *http.Request) {
// 	err := filelist.Todo(writer, request)
// 	if nil != err {
// 		code := http.StatusOK
// 		switch {
// 		case os.IsNotExist(err):
// 			code = http.StatusNotFound
// 		case os.IsPermission(err): //没有权限
// 			code = http.StatusForbidden
// 		default:
// 			code = http.StatusInternalServerError //未知错误码 500
// 		}
// 		http.Error(writer, http.StatusText(code), code)
// 	}
// }

// 函数式编程改写errorWrapper 输入一个函数 输出一个函数
type oldHandle func(writer http.ResponseWriter, request *http.Request) error
type newHandle func(writer http.ResponseWriter, request *http.Request)

func errorWrapper(todo oldHandle) newHandle {
	return func(writer http.ResponseWriter, request *http.Request) {
		err := todo(writer, request)
		if nil != err {
			code := http.StatusOK
			switch {
			case os.IsNotExist(err):
				code = http.StatusNotFound
			case os.IsPermission(err): //没有权限
				code = http.StatusForbidden
			default:
				code = http.StatusInternalServerError //未知错误码 500
			}
			http.Error(writer, http.StatusText(code), code)
		}
	}
}
func main() {
	//注册路由,todo是路由的处理函数,
	http.HandleFunc("/", errorWrapper(filelist.Todo))
	//启动http 服务器
	err := http.ListenAndServe(":8885", nil)
	if nil != err {
		panic(err)
	}
}

filelist.go

/*
 * @Author: your name
 * @Date: 2020-11-04 12:39:39
 * @LastEditTime: 2020-11-04 12:48:47
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /learngo/errorhandling/filelistserver/filelist/filelist.go
 */

package filelist

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

func Todo(writer http.ResponseWriter, request *http.Request) error {
	//分析请求头
	path := request.URL.Path
	//根据url的路径 打开文件
	file, err := os.Open(path) //打开一个文件
	if nil != err {
		return err
	}
	defer file.Close()
	all, err := ioutil.ReadAll(file) //读取文件中的全部数据
	if nil != err {
		return err
	}

	writer.Write(all) //返回响应
	return nil
}

0x04 Panic 和 recover

panic :停止当前函数的执行,一直向上返回,并执行每一层的defer。如果没有遇见recover,程序就退出

panic 是一个很重的词,要避免多次调用

recover:仅在defer中调用,获取panic的值 封装成接口并返回,如果无法处理可以重新panic

recover 的简单使用:

/*
 * @Author: your name
 * @Date: 2020-11-04 13:45:53
 * @LastEditTime: 2020-11-04 13:58:12
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /learngo/errorhandling/recover/recover.go
 */
package main

import "fmt"

func tryRecover() {
	defer func() {
		r := recover() //recover的返回值是任意类型 interface{}
		//如果返回值 是将error 封装成的接口
		if err, ok := r.(error); ok {
			fmt.Println("发生错误:", err)
		} else {
			buffer := fmt.Sprintf("我不知道该做什么了:%v", r)
			panic(buffer)
		}
	}() //加() 连定义带执行
	// b := 0
	// a := 5 / b
	// fmt.Println(a)
	panic(123)
}

func main() {
	tryRecover()
}

什么时候用error 什么时候用panic?

尽量不要用panic

凡是能意料到的错误 都用 error 处理 ,例如 文件打不开,error 就是一个鸭子类型,一个interface,凡是具有 Error方法的数据类型都可以作为error。 然后可以用log.Prinf("发生错误:%s",e.Error()) 来处理错误

意料之外的错误 用panic 处理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值