golang函数特点:

• 无需声明原型。
• 支持不定 变参。
• 支持多返回值。
• 支持命名返回参数。
• 支持匿名函数和闭包。
• 函数也是一种类型,一个函数可以赋值给变量。

• 不支持 嵌套 (nested) 一个包不能有两个名字一样的函数。
• 不支持 重载 (overload)
• 不支持 默认参数 (default parameter)。

1
2
3
4
//函数声明告诉了编译器函数的名称,参数,和返回类型
func function_name( [parameter list] ) [return_types] {
函数体
}
  • func:函数由 func 开始声明

  • function_name:函数名称,参数列表和返回值类型构成了函数签名。

  • parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数是可选的,也就是说函数也可以不包含参数。可以定义多个参数,多个参数之间用逗号分隔。

  • return_types:返回类型,函数返回一列值。 return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。可以有返回值,也可以没有

  • 函数体:函数定义的代码集合。

1
2
3
4
5
6
7
//函数可以返回多个值
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
1
2
3
4
5
6
//具名返回值允许我们不显式地使用 return 语句来返回结果,Go 会自动返回最后的变量值
func swap(a, b int) (x, y int) {
x, y = b, a
return
}
//x 和 y 是具名返回值,不需要显式地写 return x, y,仅 return 即可

函数值传递

在Go语言中,函数可以像任何其他变量一样被赋值给变量或作为参数传递给其他函数。

  • 值类型:在Go语言中,基本数据类型(如intfloatboolstring)和复合数据类型(如数组、结构体)都是值类型。

  • 存储位置:值类型通常存储在栈(stack)上。

  • 参数传递:当值类型作为函数参数传递时,实际上是传递了该值的拷贝。

  • 修改影响:在函数内部对参数值的修改不会影响到函数外部的原始值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//基本数据类型(int)
package main
import "fmt"

func main() {
num := 10
change(num)//调用 change 函数,并将 num 作为参数传递
fmt.Println(num) // 输出 10
}

func change(num int) {
num = 998
//参数 num 被修改为 998,但这不会影响 main 函数中的 num,因为整数是值类型,传递的是值的拷贝
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//数组
package main
import "fmt"

func main() {
arr := [3]int{1, 3, 5}
change(arr)//调用 change 函数,并将 arr 作为参数传递。
fmt.Println(arr) // 输出 [1 3 5]
}

func change(arr [3]int) {
arr[1] = 8
//参数 arr 的第二个元素被修改为 8,但这不会影响 main 函数中的 arr,因为数组是值类型,传递的是数组的拷贝
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//结构体
package main
import "fmt"

type Person struct {
name string
age int
}

func main() {
p := Person{"lnj", 33}
change(p)//调用 change 函数,并将 p 作为参数传递
fmt.Println(p.name) // 输出 lnj
}

func change(p Person) {
p.name = "zs"
//参数 p 的 name 字段被修改为 "zs",但这不会影响 main 函数中的 p,因为结构体是值类型,传递的是结构体的拷贝
}

函数引用传递

  • 引用类型:在Go语言中,指针、切片(slice)、映射(map)和通道(channel)是引用类型。

  • 存储位置:引用类型通常存储在堆(heap)上,并通过指针来访问。

  • 参数传递:当引用类型作为函数参数传递时,实际上是传递了指向该值的指针,即引用传递。

  • 修改影响:在函数内部对参数的修改会影响到函数外部的原始值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//指针
package main
import "fmt"

func main() {
num := 10
change(&num)//调用 change 函数,并将 num 的地址作为参数传递
fmt.Println(num) // 输出 998
}

func change(num *int) {
*num = 998
//参数 num 是一个指向整数的指针,通过解引用 *num 将值修改为 998。
//因为传递的是地址,所以 main 函数中的 num 也被修改。
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//切片
package main
import "fmt"

func main() {
arr := []int{1, 3, 5}
change(arr)//调用 change 函数,并将 arr 作为参数传递
fmt.Println(arr) // 输出 [1 8 5]
}

func change(arr []int) {
arr[1] = 8
//参数 arr 指向同一个切片,所以修改 arr 的第二个元素为 8 会影响到 main 函数中的 arr
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//映射
package main
import "fmt"

func main() {
mp := map[string]string{"name": "lnj", "age": "33"}
change(mp)//调用 change 函数,并将 mp 作为参数传递
fmt.Println(mp["name"]) // 输出 zs
}

func change(mp map[string]string) {
mp["name"] = "zs"
//参数 mp 指向同一个映射,所以修改 mp 中的 "name" 键对应的值为 "zs" 会影响到 main 函数中的 mp
}

init函数

在Go语言里,init函数就像是一个特殊的“准备”按钮。它是一个不需要你手动去按的按钮,程序在开始运行的时候会自动帮你按这个按钮。这个按钮的作用就是做一些准备工作,比如设置好环境,让你的程序能够顺利地开始工作。

  • 定义init函数是一个特殊的函数,用于初始化操作。它没有参数,没有返回值,且不能被显式调用。

  • 自动调用:程序在启动时会自动调用init函数,且在main函数执行之前。

1
2
3
4
5
6
func init(){
fmt.Println("init先执行")
}
func main(){
fmt.Println("main后执行")
}
  • 执行顺序:在同一个包中,init函数的执行顺序是按照它们在代码中出现的顺序。如果一个包中有多个文件,那么init函数的执行顺序是按照文件名的字典顺序。

  • 全局变量初始化:全局变量的初始化是在init函数执行之前进行的。如果全局变量依赖于函数调用的结果,那么这个函数会在相关的init函数执行之前被调用。

  • 无参数和返回值init函数没有参数,也不返回任何值。

  • 可重复定义:一个包中可以定义多个init函数,它们会按照它们在代码中出现的顺序执行。

  • 用途init函数通常用于那些不能通过简单声明或全局变量赋值来完成的初始化工作,比如配置文件的加载、环境变量的设置、日志系统的初始化等。

1
2
3
4
5
6
7
8
package mypackage

import "fmt"

func init() {
fmt.Println("mypackage 初始化")
}

  1. 作用与调用时机

    • init()函数用于在包被导入时执行一次性的初始化操作。每个包可以包含多个init()函数,它们会在包被导入时自动执行。

    • 当包被导入时,init()函数会按照导入的顺序自动执行。同一个包中的多个init()函数按照编写的顺序执行。

  2. 使用方式

    • init()函数的定义和普通函数类似,只是函数名为init。它没有参数和返回值,不需要手动调用,而是在包被导入时自动执行。
  3. 应用场景

    • 常用于初始化包的配置信息,例如读取配置文件,进行初始化设置。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package config

    import (
    "log"
    "os"
    )

    var Config = struct {
    Database string
    Port int
    }{}

    func init() {
    // 假设配置文件名为 config.ini
    configFile, err := os.Open("config.ini")
    if err != nil {
    log.Fatal("Failed to open config file: ", err)
    }
    defer configFile.Close()

    // 读取配置文件并设置到Config变量中
    // 这里省略了具体的解析代码
    // ...
    }
    • 用于数据库初始化,比如建立数据库连接,进行必要的数据表创建等操作。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package db

    import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "log"
    )

    var DB *sql.DB

    func init() {
    var err error
    // 连接数据库
    DB, err = sql.Open("mysql", "user:password@/dbname")
    if err != nil {
    log.Fatal("Error connecting to the database: ", err)
    }

    // 测试数据库连接
    err = DB.Ping()
    if err != nil {
    log.Fatal("Error pinging the database: ", err)
    }
    }
    • 注册功能插件,当包中存在多个功能插件需要在包被导入时注册到主程序中。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package plugin

    var Plugins = []func(){}

    func init() {
    // 注册插件
    Plugins = append(Plugins, func() {
    // 插件1的代码
    })
    Plugins = append(Plugins, func() {
    // 插件2的代码
    })
    }
  • main函数的异同

    • 相同点:两个函数在定义时不能有任何的参数和返回值,且Go程序自动调用(一般函数都要先声明,再调用。而 init 函数和 main 函数只声明就行,Go 程序会自己进入)。

    • 不同点:init()可以应用于任意包中,且可以重复定义多个;main函数只能用于main包中,且只能定义一个。

在Go语言开发中,我们经常需要在不同的文件之间共享代码,比如在一个文件中定义函数,在另一个文件中使用这个函数。这就是包(package)的作用之一。此外,如果两个程序员都想在同一个项目中定义同名的函数,使用包可以避免命名冲突。

在Go语言中,包(package)是代码组织的基本单位。一个包可以包含多个.go源文件,它们共享相同的包名。包的使用包括定义包、编译包以及在包中定义函数、变量和类型。

1
2
3
4
5
6
// math.go
package math

func Add(a, b int) int {
return a + b
}
1
2
3
4
5
6
// utils.go
package utils

func Add(a, b int) int {
return a + b + 100 // 这个Add函数有一些额外的操作
}
1
2
3
4
5
6
7
8
9
10
11
12
package main

import (
"fmt"
"project/math"
"project/utils"
)

func main() {
fmt.Println(math.Add(1, 2)) // 输出 3
fmt.Println(utils.Add(1, 2)) // 输出 103
}

main.go文件中,我们导入了mathutils包,并使用它们各自的Add函数。由于每个函数都通过其包名进行了限定,因此即使函数名称相同,也不会发生冲突。我们可以通过math.Add来调用math包中的Add函数,通过utils.Add来调用utils包中的Add函数。

作用

  • 区分标识符:包的使用可以区分相同名字的函数、变量等标识符,即使在不同的包中。

  • 项目管理:当程序文件很多时,包可以帮助我们更好地管理和组织项目。

  • 控制访问:包可以控制函数、变量等的访问权限,即它们的作用域。

1
2
3
4
package 包名
//包名应该小写,因为Go的包名是全局唯一的,并且通常以小写字母开头。
//包的目录结构应该反映包的名称。
//包应该尽量保持小而专注,每个包只做一件事情。
1
import "包的路径"

main包

main包是Go程序的入口点。每个可执行的Go程序都必须包含一个main包,其中包含main函数。

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Println("Hello, World!")
}

匿名函数

匿名函数是Go语言中没有函数名的函数。它们通常用于需要函数的地方,但又不想为函数命名的场景。匿名函数可以被赋值给一个变量,或者直接作为参数传递给另一个函数。

匿名函数使用方法

直接调用

就像是你直接用工具做了一件事。

1
2
3
4
5
6
7
8
9
10
11
12
package main

import "fmt"

func main() {
// 定义即调用,用于输出一条日志信息
func() {
fmt.Println("程序启动成功")
}()

// 程序的其他逻辑...
}

定义了一个匿名函数来输出一条日志信息,然后立即调用它。这个函数只在程序启动时使用一次,之后就不再需要了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

func main() {
// 尝试执行一个操作
if err := doSomething(); err != nil {
// 定义即调用,用于处理错误
func() {
fmt.Println("发生错误:", err)
// 执行一些清理工作
}()

// 返回或者结束程序
return
}

// 程序的其他逻辑...
}

func doSomething() error {
// 模拟一个可能失败的操作
return nil // 或者返回一个错误
}

如果doSomething函数返回一个错误,我们定义并立即调用一个匿名函数来处理这个错误,比如输出错误信息并执行清理工作。

保存到变量

就像是你把这个工具保存起来,以后还可以再用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
myTool := createTool()
myTool() // 使用保存的工具
myTool() // 再次使用保存的工具
}

func createTool() func() {
return func() {
fmt.Println("这个函数被保存到变量中,可以多次使用")
}
}

作为参数和返回值

就像是你把这个工具带到了别的地方(作为参数),或者你得到了一个新的工具(作为返回值)。

作为参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func main() {
processTask(handleTask)
}

func processTask(task func()) {
fmt.Println("处理任务前...")
task() // 调用传递来的工具
fmt.Println("处理任务后...")
}

func handleTask() {
fmt.Println("这是传递给processTask的任务")
}

handleTask 是一个匿名函数,我们把它作为参数传递给了 processTask 函数

作为返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
myTool := getTool()
myTool() // 使用新得到的工具
}

func getTool() func() {
return func() {
fmt.Println("这是从函数返回的新工具")
}
}

getTool 函数返回一个匿名函数,我们得到了这个新工具,并在 main 函数中使用它

一次性匿名函数

在定义匿名函数的时候就调用,此时匿名函数就只能使用一次

1
2
3
4
5
6
7
fun main(){
//定义即调用
func(n1 int,n2 int)int{
return n1 + n2
}(10,20)
//此时在定义的时候同时调用
}

🚀 编译结果如下:

1
30

赋值给变量调用 可重复使用的匿名函数

这种方式的匿名函数可以多次调用,之前我们说过函数也是一种数据类型,那么将这个函数直接定义一个变量然后赋值。

这种方式就像是你有一本食谱,你可以按照食谱上的指示多次制作同一种蛋糕。每次你想要做蛋糕时,都会按照食谱上的步骤来操作。

1
2
3
4
5
myFunc := func() {
fmt.Println("这个函数可以多次使用")
}
myFunc() // 第一次调用
myFunc() // 第二次调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
// 定义匿名函数并赋值给变量a
a := func(n1 int, n2 int) int {
return n1 - n2
}
// 通过变量a调用匿名函数
res := a(30, 20)
fmt.Println("res=", res) // 正确打印res的值
res2 := a(30, 40)
fmt.Println("res2=", res2) // 修正变量名,并正确打印res2的值
}

🚀 编译结果如下:

1
2
res= 10
res3= -10

全局匿名函数

这些是没有名字的函数,但是它们可以在程序的任何地方被调用,因为它们被定义在全局范围内。就像你家里的公共工具,比如剪刀,你可以在任何需要的时候找到并使用它们。

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

var globalFunc = func() {
fmt.Println("这个函数是全局的,在整个程序中都可以访问")
}

func main() {
globalFunc() // 调用全局匿名函数
}

匿名函数就像是你生活中的“即用即抛”工具,它们不需要长期维护,也不需要占用你太多的资源。你可以在需要的时候快速使用它们,然后继续你的工作和生活。它们特别适合那些不需要重复使用或者只在特定情况下需要的功能。通过使用匿名函数,你的代码可以变得更加灵活和高效,就像你的生活因为有了这些即用即抛的工具而变得更加方便一样。

闭包

在一个厨房里做蛋糕,你有一个秘密配方(闭包),这个配方不仅告诉你需要哪些步骤(函数体),还告诉你需要用到哪些特别的调料(外部变量)。即使你离开了厨房,这个配方还是能记住你需要哪些调料,并且每次你按照这个配方做蛋糕时,都能用到这些调料。

闭包就像是一个“记忆盒子”,它是一个函数,但是这个函数能够记住它在哪里被创建,以及那时候周围的情况。即使创建它的那个环境(比如一个函数)已经结束了,闭包仍然能够记住那些信息。

闭包的特点

  • 记住变量:闭包可以记住它被创建时周围的变量,即使那些变量在外部函数中已经不可见。

  • 可以修改记忆:闭包不仅能记住变量,还能改变它们,就像那些变量一直存在一样。

  • 延迟执行:闭包可以在需要的时候才执行,即使创建它的外部函数已经执行完毕。

闭包怎么工作的?

闭包通过在函数内部定义另一个函数来实现。内部函数能够访问外部函数的变量,即使外部函数已经执行完毕。

闭包是一个特殊的匿名函数, 它是匿名函数和相关引用环境组成的一个整体,

  • 也就是说只要匿名函数中用到了外界的变量, 那么这个匿名函数就是一个闭包。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Go 支持匿名函数,这意味着可以在函数内部定义并调用函数,匿名函数也可以作为变量传递。
func main() {
// 定义一个匿名函数并赋值给变量add
add := func(a, b int) int {
return a + b
}
fmt.Println(add(3, 4)) // 输出 7

// 定义一个返回闭包的函数,是一个秘密配方,每次调用都会记住上次做到哪一步了
counter := func() func() int {
count := 0 // 这是一个在counter函数内部定义的变量。闭包中的外部状态,就像你做蛋糕时的步骤记录
return func() int { // 返回一个匿名函数,这个函数是一个闭包
count++ // 闭包可以访问并修改外部的count变量,闭包函数体,它修改了外部状态,就像你按照配方一步步做
return count
}
}

c := counter() // 调用counter函数,得到一个闭包
// 按照配方做蛋糕,每次都会记住上次做到哪一步
fmt.Println(c()) // 输出 1
fmt.Println(c()) // 输出 2
}
//闭包是可以引用其外层作用域的变量的函数。

  • 闭包中使用的变量和外界的变量是同一个变量, 所以可以闭包中可以修改外界变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
num := 10 // 这是main函数中的变量
a := func() { // 定义一个匿名函数并赋值给变量a
num = 6 // 在闭包中修改了main函数中的num
fmt.Println(num) // 输出修改后的值:6
}
fmt.Println("执行闭包前", num) // 输出:10
a() // 调用闭包
fmt.Println("执行闭包后", num) // 输出闭包修改后的值:6
}

闭包a“记住了”变量num,并且能够修改它。当你调用a()时,闭包不仅打印出了修改后的num值,而且这个修改也影响到了main函数中的num变量。

  • 只要闭包还在使用外界的变量, 那么外界的变量就会一直存在
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
res := addUpper() // 调用addUpper函数,得到一个闭包
fmt.Println(res()) // 输出:2
fmt.Println(res()) // 输出:3
fmt.Println(res()) // 输出:4
fmt.Println(res()) // 输出:5
}

func addUpper() func() int {
x := 1 // 这是addUpper函数中的变量
return func() int { // 返回一个匿名函数,这个函数是一个闭包
x++ // 闭包中修改了addUpper中的x
return x
}
}

addUpper函数返回了一个闭包,这个闭包“记住了”变量x。每次调用res()时,闭包都会增加x的值,并返回新的x值。因为闭包一直在使用x,所以x会一直存在,直到程序结束。

闭包的应用场景

  • 封装状态:就像你的秘密配方不让别人看到,闭包可以封装状态,不让外部直接访问。

  • 生成唯一资源:每次按照配方做蛋糕,都能保证蛋糕是独一无二的,闭包可以生成唯一的资源。

  • 延迟初始化:就像你可以根据需要再决定是否做蛋糕,闭包可以实现延迟初始化。

闭包的注意事项

  • 内存泄漏:如果你的秘密配方一直记得那些调料,即使你不再做蛋糕,那些调料也不会被清理掉,可能会导致资源浪费。

  • 变量捕获:闭包会记住变量的最新状态,如果这些变量在外部被修改,闭包内部看到的也会是最新的状态。

函数异常处理

在Go语言中,处理函数出错的情况有点像我们平时处理生活中的意外。想象你在一个餐厅工作,你的任务是确保顾客的订单正确无误。如果一切都顺利,你会把食物直接送到顾客那里。但有时候,厨房可能会出点问题,比如订单做错了或者食物烧焦了。这时候,你不能直接把问题食物给顾客,你需要先处理这个问题,可能是退回去重做,或者给顾客换个菜。

异常处理方式和其他语言(如 Java 或 Python)不同。Go 不支持传统的 try-catch 异常机制,而是依赖错误处理模式,通过返回值和 defer, panic, recover 机制来管理和捕获异常。

在Go语言中,我们有两种主要的方式来处理这种“厨房错误”:

  1. 错误返回值:这是最常见的方式,就像你检查食物是否做好了,如果没有,你会告诉顾客需要等一下。在Go中,函数会返回一个额外的值,通常是最后一个返回值,用来告诉你有没有错误发生。
  • Go语言中的错误处理主要依赖于error接口。error是一个接口,定义了Error()方法,返回错误信息。

  • 函数可以通过返回error类型的值来指示是否发生错误。调用者需要检查这个错误值是否为nil,以确定函数是否成功执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"errors"
"fmt"
)

// 定义一个简单的除法函数,返回除法结果和可能的错误
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("不能除以零") // 返回错误
}
return a / b, nil // 正常返回结果
}

func main() {
result, err := divide(4, 0) // 测试除以零的情况
if err != nil {
fmt.Println("出错了:", err)
return
}
fmt.Println("结果是:", result)
}
1
出错了: 不能除以零
  • defer:当你在函数中使用 defer 时,它会在函数返回之前执行。这常用于清理资源,比如关闭文件、网络连接等。即使函数中途遇到错误,defer 指定的操作仍会在函数结束前执行。后进先出(LIFO)的顺序执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func cleanup() {
fmt.Println("Cleaning up resources")
}

func main() {
defer cleanup() // 这行代码将cleanup函数调用延迟到main函数返回前执行

fmt.Println("Performing some work...")
// 假设这里发生了一些工作
}
//不管main函数中的工作是否正常完成,或者是否发生错误,cleanup函数都会被调用,以确保资源被清理。
1
2
Performing some work...
Cleaning up resources
  • panic和recover:这种方式有点像处理厨房火灾这种紧急情况。panic就是当错误严重到无法处理时,你按下紧急按钮,让整个餐厅知道出事了。recover就像是紧急响应,可以在错误发生后采取措施,比如清理现场,防止餐厅完全停止营业。
  • panic是Go语言中用于触发异常的内置函数。当调用panic时,程序会立即停止当前函数的执行,并向上一层函数传递错误信息,这个过程会一直持续到程序终止或者被recover捕获。

  • panic可以带一个参数,通常是error类型的值,表示错误信息。

  • panic通常用于不可恢复的错误情况,比如程序逻辑中出现了严重的错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"fmt"
)

func causePanic() {
defer fmt.Println("defer 在 panic 后被执行")
fmt.Println("函数开始")
panic("遇到严重错误!") // 引发 panic。中断了正常执行,触发了所有 defer 执行,并打印错误信息。
fmt.Println("这行代码不会被执行")
}

func main() {
causePanic()
}

1
2
3
函数开始
deferpanic 后被执行
panic: 遇到严重错误!
  • recover是Go语言中用于捕获和处理panic的内置函数。只有在defer函数中调用recover才能成功捕获panic

  • 如果recover成功捕获panic,它会返回panic的值,否则返回nil

  • 使用recover可以恢复程序的执行,避免程序异常终止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
)

func safeFunction() {
defer func() {//recover 只能在 defer 函数中使用,用于捕获 panic。如果 panic 被捕获,程序不会崩溃,可以继续运行。
if r := recover(); r != nil {
fmt.Println("捕获到 panic:", r)
}
}()
fmt.Println("safeFunction 开始")
panic("引发 panic") // 引发 panic
fmt.Println("safeFunction 结束")
}

func main() {
safeFunction()
fmt.Println("main 函数继续执行")
}

1
2
3
safeFunction 开始
捕获到 panic: 引发 panic
main 函数继续执行

本站由 GINA 使用 Stellar 1.29.1 主题创建。

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

Copyright © 2024 GINA 保留所有权利。