0x00 defer 调用
- defer 可以确保在函数返回时 语句的调用发生
- 参数在defer时计算,即defer fmt.Println(i) 这个i的值在defer时就已经传入给了Println ,不论defer后i怎么变,在最后执行Println这个函数时,输出的都是defer时传入给它的值
- 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 处理