语言结构 main.go
1 2 3 4 5 6 7 8 package mainimport "fmt" func main () { fmt.Println("Hello, World!" ) }
package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
运行 直接运行
生成二进制文件
1 2 3 go build main .go ./main
注意 1 2 3 4 func main () { fmt.Println("Hello, World!" ) }
变量 1 2 3 4 5 6 7 8 9 10 11 12 13 var a = "initial" var b, c int = 1 , 2 var d = true var e float64 f := float32 (e) g := a + "foo" const s string = "constant"
循环 只有for循环,break,continue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func main () { i := 1 for { fmt.Println("loop" ) break } for j := 7 ; j < 9 ; j++ { fmt.Println(j) } for n := 0 ; n < 5 ; n++ { if n%2 == 0 { continue } fmt.Println(n) } for i <= 3 { fmt.Println(i) i = i + 1 } }
条件 if-else if-else if-else
1 2 3 4 5 6 7 if num := 9 ; num < 0 { fmt.Println(num , "is negative" ) } else if num < 10 { fmt.Println(num , "has 1 digit" ) } else { fmt.Println(num , "has multiple digits" ) }
switch 不需要break,直接跳出判断,只能写一句话
可以不需要变量,直接进入case,判断条件True or False
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import ( "fmt" "time" ) func main() { a := 2 switch a { case 1 : fmt.Println ("one" ) case 2 : fmt.Println ("two" ) case 3 : fmt.Println ("three" ) case 4 , 5 : fmt.Println ("four or five" ) default : fmt.Println ("other" ) } t := time.Now() switch { case t.Hour() < 12 : fmt.Println ("It's before noon" ) default : fmt.Println ("It's after noon" ) } }
select 超时判断
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var resChan = make (chan int )func test () { select { case data := <-resChan: doData(data) case <-time.After(time.Second * 3 ): fmt.Println("request time out" ) } }func doData (data int ) { }
退出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var shouldQuit=make (chan struct {}) fun main(){ { } select { case <-c.shouldQuit: cleanUp() return default : } }close (shouldQuit)
判断channel是否堵塞
1 2 3 4 5 6 7 8 9 ch := make (chan int , 5 ) data:=0 select {case ch <- data:default : }
数组 1 2 3 4 5 6 7 8 9 var a [5] inta [4] = 100 fmt.Println ("get:" , a [2] ) fmt.Println ("len:" , len (a))b := [5] int{1 , 2 , 3 , 4 , 5 } fmt.Println (b)var twoD [2] [3] int
切片 主要使用的数组
1 2 3 4 5 6 7 8 9 10 11 s := make ([] string, 3 ) s[0] = "a" s[1] = "b" s[2] = "c" s = append (s, "d" ) s = append (s, "e" , "f" ) c := make ([] string, len (s)) copy (c, s)
len() 和 cap() 函数 len返回当前长度
cap返回最大长度
字典 map
1 2 m := make (map [string ]int ) m["one" ] = 1
如果没有键值对,
1 2 3 4 5 6 fmt.Println (m["unknow" ] ) r , ok := m["unknow" ] fmt.Println (r , ok) // 0 false ok为True,表示键值对存在,反之则否
完全无序,遍历时随机输出
遍历 range遍历
遍历数组,i为索引,num为值
遍历map,前为键,后为值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func main () { nums := []int {2 , 3 , 4 } sum := 0 for i, num := range nums { sum += num if num == 2 { fmt.Println("index:" , i, "num:" , num) } } fmt.Println(sum) m := map [string ]string {"a" : "A" , "b" : "B" } for k, v := range m { fmt.Println(k, v) } for k := range m { fmt.Println("key" , k) }
函数 1 2 3 4 5 6 7 8 9 10 11 12 func add (a int , b int ) int { return a + b }func add2 (a, b int ) int { return a + b }func exists (m map [string ]string , k string ) (v string , ok bool ) { v, ok = m[k] return v, ok }
闭包 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package main import "fmt" func main () { x := 10 increment := func () int { x ++ return x } fmt.Println (increment ()) fmt.Println (increment ()) fmt.Println (increment ()) }
这种使用方式应该是变量声明在函数声明前,变量作用域的关系应该局部到全局,到内置。
对increment函数来说,x在局部变量中为找到,相对于它本身,在它之前声明的x变量比它函数自身的局部变量更高,或者说是相对与它的全局变量,但并不准确
延迟调用(defer) 1 2 调用直到 return 前才被执行 多个语句,按先进后出的方式执行。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行。
用处 1 2 3 1. 关闭文件句柄2. 锁资源释放3. 数据库连接释放
defer+闭包 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func main () { var whatever [5 ]struct {} for i := range whatever { defer func () { fmt.Println(i) }() } } output:4 4 4 4 4
当defer执行时,函数已经准备return,此时i为4,数组的大小
close 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport "fmt" type Test struct { name string }func (t *Test) Close() { fmt.Println(t.name, " closed" ) }func main () { ts := []Test{{"a" }, {"b" }, {"c" }} for _, t := range ts { defer t.Close() } } 输出结果: c closed c closed c closed
并不是我们想要的结果
多写一个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport "fmt" type Test struct { name string }func (t *Test) Close() { fmt.Println(t.name, " closed" ) }func Close (t Test) { t.Close() }func main () { ts := []Test{{"a" }, {"b" }, {"c" }} for _, t := range ts { defer Close(t) } } 输出结果: c closed b closed a closed
不用多写函数
1 2 3 4 5 6 7 func main () { ts := []Test{{"a" }, {"b" }, {"c" }} for _, t := range ts { t2 := t defer t2.Close() } }
defer后面的语句在执行的时候,函数调用的参数会被保存起来,但是不执行。也就是复制了一份。
*
延迟调用参数在注册时求值或复制,可用指针或闭包 “延迟” 读取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainfunc test () { x, y := 10 , 20 defer func (i int ) { println ("defer:" , i, y) }(x) x += 10 y += 100 println ("x =" , x, "y =" , y) }func main () { test() }
输出结果:
1 2 x = 20 y = 120 defer : 10 120
defer性能问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package main import ( "fmt" "sync" "time" )var lock sync.Mutex func test () { lock.Lock () lock.Unlock () } func testdefer () { lock.Lock () defer lock.Unlock () } func main () { func () { t1 := time.Now () for i := 0 ; i < 10000 ; i ++ { test () } elapsed := time.Since (t1) fmt.Println ("test elapsed: " , elapsed) }() func () { t1 := time.Now () for i := 0 ; i < 10000 ; i ++ { testdefer () } elapsed := time.Since (t1) fmt.Println ("testdefer elapsed: " , elapsed) }() }
输出结果:
test elapsed: 223.162µs
testdefer elapsed: 781.304µs
defer陷阱 defer和closure 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport ( "errors" "fmt" )func foo (a, b int ) (i int , err error ) { defer fmt.Printf("first defer err %v\n" , err) defer func (err error ) { fmt.Printf("second defer err %v\n" , err) }(err) defer func () { fmt.Printf("third defer err %v\n" , err) }() if b == 0 { err = errors.New("divided by zero!" ) return } i = a / b return }func main () { foo(2 , 0 ) }
输出结果:
1 2 3 third defer err divided by zero! second defer err <nil > first defer err <nil >
如果 defer 后面跟的不是一个 closure 最后执行的时候我们得到的并不是最新的值。
socond的err是在执行时传入的。闭包调用
defer与return 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func foo () (i int ) { i = 0 defer func () { fmt.Println(i) }() return 2 }func main () { foo() } 输出结果: 2
在有具名返回值的函数中(这里具名返回值为 i),执行 return 2 的时候实际上已经将 i 的值重新赋值为 2。
也就是说返回值其实是return后的值,
修改i值为1,删除2,
最终返回值为1
defer nil函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ( "fmt" )func test () { var run func () = nil defer run() fmt.Println("runs" ) }func main () { defer func () { if err := recover (); err != nil { fmt.Println(err) } }() test() }
输出结果:
1 2 runs runtime error : invalid memory address or nil pointer dereference
错误使用defer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "net/http" func do () error { res, err := http.Get("http://www.google.com" ) defer res.Body.Close() if err != nil { return err } return nil }func main () { do() }
输出结果:
1 panic : runtime error : invalid memory address or nil pointer dereference
当它失败的时候,我们访问了 Body 中的空变量 res ,因此会抛出异常
1 2 3 if res != nil { defer res.Body.Close() }
错误检查 f.Close() 可能会返回一个错误,可这个错误会被我们忽略掉
1 2 3 4 5 6 7 8 9 10 11 12 f, err := os.Open("book.txt" )if err != nil { return err }if f != nil { defer func () { if err := f.Close(); err != nil { } }() }
两个相同的变量释放不同的资源,可能无法正常执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 func do () error { f, err := os.Open("book.txt" ) if err != nil { return err } if f != nil { defer func () { if err := f.Close(); err != nil { fmt.Printf("defer close book.txt err %v\n" , err) } }() } f, err = os.Open("another-book.txt" ) if err != nil { return err } if f != nil { defer func () { if err := f.Close(); err != nil { fmt.Printf("defer close another-book.txt err %v\n" , err) } }() } return nil }func main () { do() }
解决方案:传参保存变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 func do () error { f, err := os.Open("book.txt" ) if err != nil { return err } if f != nil { defer func (f io.Closer) { if err := f.Close(); err != nil { fmt.Printf("defer close book.txt err %v\n" , err) } }(f) } f, err = os.Open("another-book.txt" ) if err != nil { return err } if f != nil { defer func (f io.Closer) { if err := f.Close(); err != nil { fmt.Printf("defer close another-book.txt err %v\n" , err) } }(f) } return nil }func main () { do() }
指针 主要是可用于修改函数参数
1 2 3 func add2ptr(n *int ) { *n += 2 }
传入的是指针类型,直接修改内存空间对应的值,如何是数据量较大的结构体,推荐这种方式,可节省消耗
结构体 未初始化为空值
1 2 3 4 5 6 7 8 9 10 type user struct { name string password string } a := user {name : "wang", password : "1024"} b := user {"wang", "1024"} c := user {name : "wang"} c.password = "1024" var d user
结构体作为函数参数 指针参数,可修改结构体数据,节约大结构体的开销
1 2 3 func checkPassword2(u *user , password string) bool { return u.password == password }
结构体标签 json序列化时会使用定义的标签作为key
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 type Student struct { ID int `json:"id"` Gender string name string }func main () { s1 := Student{ ID: 1 , Gender: "女" , name: "pprof" , } data, err := json.Marshal(s1) if err != nil { fmt.Println("json marshal failed!" ) return } fmt.Printf("json str:%s\n" , data) }
结构体方法 类似类的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 func (u user ) checkPassword(password string) bool { return u.password == password } func (u *user ) resetPassword(password string) { u.password = password } func main() { a := user {name : "wang", password : "1024"} a.resetPassword("2048") fmt.Println(a.checkPassword("2048")) // true }
匿名字段 匿名字段可实现类似继承和覆盖的操作
继承
1 2 3 4 5 6 7 8 9 10 11 12 type User struct { id int name string }type Manager struct { User }func (self *User) ToString() string { return fmt.Sprintf("User: %p, %v" , self, self) }
覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type User struct { id int name string }type Manager struct { User title string }func (self *User) ToString() string { return fmt.Sprintf("User: %p, %v" , self, self) }func (self *Manager) ToString() string { return fmt.Sprintf("Manager: %p, %v" , self, self) }
方法集 • 类型 T 方法集包含全部 receiver T 方法。 • 类型 *T 方法集包含全部 receiver T + *T 方法。 • 如类型 S 包含匿名字段 T,则 S 和 *S 方法集包含 T 方法。 • 如类型 S 包含匿名字段 *T,则 S 和 *S 方法集包含 T + *T 方法。 • 不管嵌入 T 或 *T,*S 方法集总是包含 T + *T 方法。
这些描述是正确的,但go语言的类型方法集规则允许方法的自动提升和方法集的继承
1 2 3 4 5 6 7 8 9 10 11 type T struct { int }func (t T) testT() { fmt.Println("类型 *T 方法集包含全部 receiver T 方法。" ) }func (t *T) testP() { fmt.Println("类型 *T 方法集包含全部 receiver *T 方法。" ) }
当你在一个 T
类型的变量上调用 testP()
方法时,Go 会自动取该变量的地址(将 T
转换为 *T
)。
Go允许在值类型上调用指针接收者的方法,并会自动将值转换为指针。
表达式 调用者不同,方法的两种表现形式
1 instance.method(args...) ---> <type >.func (instance, args...)
前:method value,绑定实例
后:method expression,显式传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type User struct { id int name string }func (self *User) Test() { fmt.Printf("%p, %v\n" , self, self) }func main () { u := User{1 , "Tom" } u.Test() mValue := u.Test mValue() mExpression := (*User).Test mExpression(&u) }
实例绑定 1 2 3 4 5 6 7 8 9 func main () { u := User{1 , "Tom" } mValue := u.Test u.id, u.name = 2 , "Jack" u.Test() mValue() }
值和指针 1 2 3 4 5 值接收者(User )方法会在调用时创建接收者的副本。 指针接收者(*User )方法通过指针引用原始值,不会创建副本。 当通过指针调用值接收者方法时,Go 会自动将指针转换为值。 当通过值调用指针接收者方法时,Go 会自动将值转换为指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func (self *User) TestPointer() { fmt.Printf("TestPointer: %p, %v\n" , self, self) }func (self User) TestValue() { fmt.Printf("TestValue: %p, %v\n" , &self, self) }func main () { u := User{1 , "Tom" } fmt.Printf("User: %p, %v\n" , &u, u) mv := User.TestValue mv(u) mp := (*User).TestPointer mp(&u) mp2 := (*User).TestValue mp2(&u) }
输出:
1 2 3 4 User : 0 xc42000a060, {1 Tom}TestValue : 0 xc42000a0a0, {1 Tom}TestPointer : 0 xc42000a060, &{1 Tom}TestValue : 0 xc42000a100, {1 Tom}
第二个和第四个都是副本,第四个进行了指针到值的转换,再进行副本操作
自定义error 抛出
返回
自定义 1 2 3 4 5 6 7 8 9 10 11 type PathError struct { path string op string createTime string message string }func (p *PathError) Error() string { return fmt.Sprintf("path=%s \nop=%s \ncreateTime=%s \nmessage=%s" , p.path, p.op, p.createTime, p.message) }
错误 在函数的返回值返回错误error类型数据,error是一个接口类型
定义:
1 2 3 type error interface { Error () string }
返回值生成错误信息,使用errors.new
1 2 3 4 5 6 func Sqrt (f float64 ) (float64 , error ) { if f < 0 { return 0 , errors.New("math: square root of negative number" ) } }
自定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 type DivideError struct { dividee int divider int }func (de *DivideError) Error() string { strFormat := ` Cannot proceed, the divider is zero. dividee: %d divider: 0 ` return fmt.Sprintf(strFormat, de.dividee) }
Go语言中,nil
是一个预定义的标识符,用于表示指针、接口、切片、映射、通道和函数类型的零值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 func findUser (users []user, name string ) (v *user, err error ) { for _, u := range users { if u.name == name { return &u, nil } } return nil , errors.New("not found" ) }func main () { u, err := findUser([]user{{"wang" , "1024" }}, "wang" ) if err != nil { fmt.Println(err) return } fmt.Println(u.name) if u, err := findUser([]user{{"wang" , "1024" }}, "li" ); err != nil { fmt.Println(err) return } else { fmt.Println(u.name) } }
字符串 中文字符可能占用两个字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func main () { a := "hello" fmt.Println (strings.Contains (a , "ll" )) fmt.Println (strings.Count (a , "l" )) fmt.Println (strings.HasPrefix (a , "he" )) fmt.Println (strings.HasSuffix (a , "llo" )) fmt.Println (strings.Index (a , "ll" )) fmt.Println (strings.Join ([] string{"he" , "llo" }, "-" )) fmt.Println (strings.Repeat (a , 2 )) fmt.Println (strings.Replace (a , "e" , "E" , -1 )) fmt.Println (strings.Split ("a-b-c" , "-" )) fmt.Println (strings.ToLower (a)) fmt.Println (strings.ToUpper (a)) fmt.Println (len (a)) b := "你好" fmt.Println (len (b)) }
输出 换行输出
格式化 %v代表格式化输出中的位置
%+v输出详细信息
%#v输出包,类型等更详细信息
%.2f两位小数
json 导入
1 2 3 4 import ( "encoding/json" "fmt" )
结构体字段首字母大写,或者在数据类型后加
1 2 3 4 5 type userInfo struct { Name string Age int `json:"age"` Hobby []string }
序列化,返回为byte数据,使用string(return_data)打印查看
1 2 json.Marshal (a) fmt.Println (string (return_data))
反序列化
1 2 var b userInfo err = json.Unmarshal (reutrn_data,&b)
时间处理 导入
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 time .NOw ()time .Date (2022 , 3 , 23 , 1 , 12 , 21 , 0 , time .UTC) t.Year ()Month () Day () Hour () Minute Second diff := t2.Sub (t) fmt.Println (diff) fmt.Println (diff.Minutes (),diff.Seconds ()) t.Format ("2003-02-23 12:32:12" ) t3, err := time .Parse ("2006-01-02 15:04:05" , "2022-03-27 01:25:36" ) func Parse (layout, value string) (Time, error) 时间类型数据使用Unix方法 now.Unix ()
语言类型转换 类型断言 类型断言用于将接口类型转换为指定类型
1 2 3 value .(type )or value .(T )
1 2 var i interface {} = "Hello, World" str, ok := i.(string )
value是接口类型,type是目的类型
断言成功,返回,值和布尔值表示成功与否
类型转换 数字解析 导入
类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 f, _ := strconv.ParseFloat ("1.234" , 64 ) fmt.Println (f) n, _ := strconv.ParseInt ("111" , 10 , 64 ) fmt.Println (n) n, _ = strconv.ParseInt ("0x1000" , 0 , 64 ) fmt.Println (n) n2, _ := strconv.Atoi ("123" ) fmt.Println (n2) n2, err := strconv.Atoi ("AAA" ) fmt.Println (n2, err) num := 123 str := strconv.Itoa (num)
进程信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import ("os" "os/exec" ) os.Args 命令行参数1 :二进制文件路径2 :命令参数 os.Getenv ("PATH" ) os.Setenv ("aa" ,"bb" ) exec.Command ("grep" ,"127.0.0.1" ,"/etc/hosts" ).CombinedOutput () 执行获取输出
接口 实现结构体方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 type interface_name interface { method_name1 [return_type] method_name2 [return_type] method_name3 [return_type] ... method_namen [return_type] } type struct_name struct { } func (struct_name_variable struct_name) method_name1 () [return_type] { } ... func (struct_name_variable struct_name) method_namen () [return_type] { }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 type Writer interface { Write([]byte ) (int , error ) }type StringWriter struct { str string }func (sw *StringWriter) Write(data []byte ) (int , error ) { sw.str += string (data) return len (data), nil }
指针接收者实现接口 需要传入对应数据类型的指针作为参数调用,
空接口 空接口作为函数的参数 使用空接口实现可以接收任意类型的函数参数。
1 2 3 4 func show (a interface {}) { fmt.Printf("type:%T value:%v\n" , a, a) }
空接口作为map的值 使用空接口实现可以保存任意值的字典。
1 2 3 4 5 var studentInfo = make (map [string ]interface {}) studentInfo["name" ] = "李白" studentInfo["age" ] = 18 studentInfo["married" ] = false fmt.Println(studentInfo)
类型断言 1 2 3 4 x .(T)x :表示类型为interface{}的变量 T:表示断言x 可能是的类型。
该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败
输入 fmt.Scan和fmt.Scanln 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport "fmt" func main () { var name string var age int fmt.Print("请输入您的名字: " ) fmt.Scan(&name) fmt.Print("请输入您的年龄: " ) fmt.Scanln(&age) _, err := fmt.Scanf("%s" , &name) fmt.Printf("您好, %s! 您的年龄是 %d。\n" , name, age) }
bufio.NewReader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport ( "bufio" "fmt" "os" "strings" )func main () { reader := bufio.NewReader(os.Stdin) fmt.Print("请输入一些文本: " ) text, _ := reader.ReadString('\n' ) text = strings.TrimSpace(text) fmt.Printf("您输入的文本是: %s\n" , text) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport ( "bufio" "fmt" "os" "strconv" "strings" )func main () { reader := bufio.NewReader(os.Stdin) fmt.Print("请输入多个数字,用空格分隔: " ) input, _ := reader.ReadString('\n' ) numbers := strings.Fields(input) var sum int for _, number := range numbers { n, err := strconv.Atoi(strings.TrimSpace(number)) if err != nil { fmt.Println("转换错误:" , err) return } sum += n } fmt.Printf("数字的总和是: %d\n" , sum) }
异常处理 panic抛出错误,recover捕获错误
注意:
1 recover 处理panic ,defer 在panic 之前定义,recover 在defer 调用的函数中有效
recover捕获 1 2 3 4 5 6 7 8 9 func test () { defer func () { if err := recover (); err != nil { println (err.(string )) } }() panic ("panic error!" ) }
仅最后一个错误可被捕获
1 2 3 4 5 6 7 8 9 10 11 func test () { defer func () { fmt.Println(recover ()) }() defer func () { panic ("defer panic" ) }() panic ("test panic" ) }
输出:
无效捕获 recover 只有在延迟调用内直接调用才会终止错误,否则返回nil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 defer func () { fmt.Println (recover()) }() defer recover () defer fmt.Println (recover()) defer func () {func () {println ("defer inner")recover () }() }() func except () { fmt.Println (recover()) } func test () { defer except () panic ("test panic") }
保证后续代码可执行,使用匿名函数调用可保护后续代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func test (x, y int ) { var z int func () { defer func () { if recover () != nil { z = 0 } }() panic ("test panic" ) z = x / y return }() fmt.Printf("x / y = %d\n" , z) }
导致关键流程出现不可修复性错误的使用 panic,其他使用 error。
获取数据类型