
🌈 个人主页:Zfox_
🔥 系列专栏:Go

目录
- [一:🔥 环境搭建](#一:🔥 环境搭建)
-
-
- [🎀 windows下的安装](#🎀 windows下的安装)
- [🎀 linux下的安装](#🎀 linux下的安装)
- [🎀 macos 下安装](#🎀 macos 下安装)
- [🎀 开发工具的选择](#🎀 开发工具的选择)
-
- [二:🔥 基本介绍](#二:🔥 基本介绍)
-
-
- [🔧 主要特性](#🔧 主要特性)
- [🎯 适用场景](#🎯 适用场景)
-
- [三:🔥 变量定义](#三:🔥 变量定义)
-
-
- [🎀 iota](#🎀 iota)
-
- [四:🔥 输出输入](#四:🔥 输出输入)
-
- [🦋 输出](#🦋 输出)
- [🦋 输入](#🦋 输入)
- [五:🔥 基本数据类型](#五:🔥 基本数据类型)
-
-
- [🎀 整数型](#🎀 整数型)
- [🎀 浮点型](#🎀 浮点型)
- [🎀 字符型](#🎀 字符型)
- [🎀 字符串类型](#🎀 字符串类型)
- [🎀 转义字符](#🎀 转义字符)
- [🎀 多行字符串](#🎀 多行字符串)
- [🎀 布尔类型](#🎀 布尔类型)
- [🎀 零值问题](#🎀 零值问题)
-
- [六:🔥 数组、切片、map](#六:🔥 数组、切片、map)
-
- [🦋 数组](#🦋 数组)
-
- [🎀 索引](#🎀 索引)
- [🦋 切片](#🦋 切片)
-
- [🎀 make 函数](#🎀 make 函数)
- [🦋 map](#🦋 map)
-
- [🎀 map取值](#🎀 map取值)
- [七:🔥 判断语句](#七:🔥 判断语句)
-
- [🦋 if语句](#🦋 if语句)
- [🦋 switch 语句](#🦋 switch 语句)
- [八:🔥 for 循环](#八:🔥 for 循环)
-
- [🦋 for循环的五种变体](#🦋 for循环的五种变体)
-
- 传统for循环
- 死循环
- [while 模式](#while 模式)
- [do-while 模式](#do-while 模式)
- 遍历切片,map
- break,continue
- [九:🔥 函数](#九:🔥 函数)
-
-
- [🎀 高阶函数](#🎀 高阶函数)
- [🎀 闭包](#🎀 闭包)
- [🦋 init 函数和 defer 函数](#🦋 init 函数和 defer 函数)
-
- [init 函数](#init 函数)
- [defer 函数](#defer 函数)
-
- [十:🔥 共勉](#十:🔥 共勉)
一:🔥 环境搭建
- 官网
访问不了的就访问中文网就好了
- go安装包下载
安装指定版本的安装包就好了

🎀 windows下的安装
windows 就选 windows-arm64.zip 就好了
然后需要将 go 的对应bin目录设置为环境变量,这一步是方便可以在命令行里面直接使用 go 命令
还需要将 go 的第三方 bin 目录设置为环境变量,一般是在用户目录下,这一步是为了以后使用 go install 安装的第三方可执行文件可以直接使用

🎀 linux下的安装
cpp
cd /opt
wget https://studygolang.com/dl/golang/go1.21.3.linux-amd64.tar.gz
tar -xvf go1.21.3.linux-amd64.tar.gz
编辑环境变量
cpp
vim /etc/profile
在文件后追加以下内容
export GOPROXY=https://goproxy.cn
export GOROOT=/opt/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=/opt/go/pkg
export PATH=$PATH:$GOPATH/bin
退出并保存,刷新环境变量
source /etc/profile
🎀 macos 下安装
超详细❗️❗️MacBook Pro(M1)配置GO语言环境(图文超详细版)
🎀 开发工具的选择
- 首选肯定是 goland,当然 vscode 也是可以的
https://www.jetbrains.com/zh-cn/go/download/other.html
- vscode下载
https://code.visualstudio.com/
然后去下载 go 的插件就好了
二:🔥 基本介绍
Go(又称 Golang)是由 Google 开发的一种开源编程语言,具有以下特点:
- 静态类型:编译时进行类型检查
- 编译型语言:编译成机器码执行,性能接近 C/C++
- 并发支持:内置 goroutine 和 channel,轻松实现高并发
- 简洁语法:语法简单清晰,易于学习和使用
🔧 主要特性
-
高效并发
- Goroutines:轻量级线程,占用资源少
- Channels:用于 goroutine 间通信和同步
-
快速编译
- 编译速度极快,大型项目也能快速构建
-
内存安全
- 垃圾回收机制自动管理内存
- 无悬空指针等内存安全问题
-
标准库丰富
- 内置网络编程、加密、JSON 处理等常用库
-
跨平台支持
- 支持多种操作系统和架构
- 可直接在 windows 上跨平台编译出 linux 系统的可执行文件,目标系统完全不需要安装 go 环境
🎯 适用场景
- Web 后端开发:API 服务、微服务(内置 http 库)
- 云计算和 DevOps:Docker、Kubernetes 等知名项目都用 Go 编写
- 网络编程:高性能服务器开发
- 命令行工具:CLI 应用程序
- 分布式系统:因其出色的并发特性而广泛应用于分布式系统
Go 语言以其简洁性和高性能著称,特别适合现代云计算和网络服务开发。
三:🔥 变量定义
标准的变量定义
cpp
package main
import "fmt"
func main() {
// 先定义,再赋值
var name string
name = "枫枫"
fmt.Println(name)
// var 变量名 类型 = "变量值"
var userName string = "枫枫"
fmt.Println(userName)
}
如果一个变量定义了,但是没有赋值,那么这个变量的值就是这个类型的 "零值"
变量类型省略
cpp
// 省略类型
var name2 = "枫枫"
fmt.Println(name2)
简短声明
cpp
// 声明并赋值 短声明符号
name3 := "枫枫"
fmt.Println(name3)
全局变量与局部变量
- 定义在函数体(包括main函数)内的变量都是局部变量,定义了就必须使用
- 定义在外部的变量就是全局变量,可以只定义不使用
cpp
package main
import "fmt"
var userName = "枫枫知道" // 可以不使用
func main() {
// var 变量名 类型 = "变量值"
var name = "枫枫"
// 在函数体内定义的变量,必须要使用
fmt.Println(name)
}
定义多个变量
cpp
package main
var (
name string = "枫枫"
userName = "枫枫知道"
)
func main() {
var name1, name2, name3 string // 定义多个变量
var a1, a2 = "枫枫", "知道" // 定义多个变量并赋值
a3, a4 := "枫枫", "知道" // 简短定义多个变量并赋值
}
常量定义
- 定义的时候就要赋值
- 赋值之后就不能再修改了
cpp
const name string = "枫枫" // 定义就要赋值
//name = "知道" // 不能再修改了
fmt.Println(name)
cpp
const (
male = 0
famle
unknow
people
)
func main() {
fmt.Println(male, famle, people)
}
值得注意的是,对于像 const 这样的场景来说,如果在对应的位置不指定具体的值,那么后面的值不会递增,而是会选择继续延续前面值的内容,也就是说,对于上述的场景来说,所有的值的结果都是 0
🎀 iota
那如果想定义一个增长该如何进行定义呢?在 go 中提供了一个关键字叫做 iota
它简化了常量用于增长数字的定义,给以上相同的值以准确的分类
cpp
package main
import "fmt"
const (
male = iota
fmale
unknow
people
)
func main() {
fmt.Println(male, people)
}
这里值得注意的是,iota代表的含义还有更多,比如它实际上是一个以0开始进行增长的一个值,所以这就意味着它本质上其实是可以进行更加复杂的表达式运算的,比如:
cpp
package main
import "fmt"
const (
male = iota * 10
fmale
unknow
people
)
func main() {
fmt.Println(male, fmale, unknow, people)
}
输出:
0 10 20 30
本质上来说,可以把对于iota的理解,定义理解为是一个0,1,2,3递增的这样的一串序列,并且这个序列并不是和c语言中的enum一样是直接进行递增的,而是从更加本质的角度来说是一种表达式中的x的值
上述的例子就是最好的说明
当你在把两个常量定义在一行的时候会发生什么?
cpp
const (
Apple, Banana = iota + 1, iota + 2
Cherimoya, Durian
Elderberry, Fig
)
对于 iota 来说,它是会在下一行进行递增,而不是直接进行引用 +1,因此上述的运行结果应该是
cpp
// Apple: 1
// Banana: 2
// Cherimoya: 2
// Durian: 3
// Elderberry: 3
// Fig: 4
命名规范
核心思想:首字母大写的变量、函数。方法,属性可在包外进行访问
四:🔥 输出输入
🦋 输出
常用的输出函数
cpp
package main
import "fmt"
func main() {
fmt.Println("枫枫知道")
fmt.Println(1)
fmt.Println(true)
fmt.Println("什么", "都", "可以", "输出")
}
格式化输出
cpp
package main
import "fmt"
func main() {
fmt.Printf("%v\n", "你好") // 可以作为任何值的占位符输出
fmt.Printf("%v %T\n", "枫枫", "枫枫") // 打印类型
fmt.Printf("%d\n", 3) // 整数
fmt.Printf("%.2f\n", 1.25) // 小数
fmt.Printf("%s\n", "哈哈哈") // 字符串
// 区分空串
fmt.Printf("%#v\n", "") // 用go的语法格式输出,很适合打印空字符串
}
还有一个用的比较多的就是将格式化之后的内容赋值给一个变量
cpp
var f = fmt.Sprintf("%.2f", 3.1452)
fmt.Println(f)
🦋 输入
cpp
package main
import "fmt"
func main() {
fmt.Println("输入您的名字:")
var name string
fmt.Scan(&name) // 这里记住,要在变量的前面加个&, 后面讲指针会提到
fmt.Println("你输入的名字是", name)
}
cpp
package main
import "fmt"
func main() {
fmt.Print("请输入你的年龄:")
var age int
n, err := fmt.Scan(&age)
// age 为什么是0呢?
fmt.Println(n, err, age)
}
五:🔥 基本数据类型
go 语言的基本数据类型有
- 整数形
- 浮点型
- 复数
- 布尔
- 字符串
🎀 整数型
go 语言的整数类型,具体细分有很多
cpp
var n1 uint8 = 2
var n2 uint16 = 2
var n3 uint32 = 2
var n4 uint64 = 2
var n5 uint = 2
var n6 int8 = 2
var n7 int16 = 2
var n8 int32 = 2
var n9 int64 = 2
var n10 int = 2
大家只需要记住以下几点
- 默认的数字定义类型是int类型
- 带个u就是无符号,只能存正整数
- 后面的数字就是2进制的位数
- uint8 还有一个别名 byte, 一个字节=8个bit位
- int 类型的大小取决于所使用的平台
我是 64 位操作系统,那么我会试一下 int 是不是就是 int64 的最大上限
2 的 63 次方 -1 = 9223372036854775807
cpp
fmt.Printf("%.0f\n", math.Pow(2, 63))
var n1 int = 9223372036854775807
fmt.Println(n1)
var n2 int = 9223372036854775808 // 看它报不报错
fmt.Println(n2)
🎀 浮点型
Go 语言支持两种浮点型数:float32 和 float64
- float32 的浮点数的最大范围约为3.4e38,可以使用常量定义:math.MaxFloat32
- float64 的浮点数的最大范围约为 1.8e308,可以使用一个常量定义:math.MaxFloat64
如果没有显式声明,则默认是float64
🎀 字符型
注意哦,是字符,不是字符串
比较重要的两个类型是 byte(单字节字符)和 rune(多字节字符)
cpp
package main
import "fmt"
func main() {
var c1 = 'a'
var c2 = 97
fmt.Println(c1) // 直接打印都是数字
fmt.Println(c2)
fmt.Printf("%c %c\n", c1, c2) // 以字符的格式打印
var r1 rune = '中'
fmt.Printf("%c\n", r1)
}
- 在 Go 中,字符的本质是一个整数,直接输出时,是该字符对应的 UTF-8 编码的码值
- 可以直接给某个变量赋一个数字,然后按格式化输出时 %c ,会输出该数字对应的 unicode 字符
- 字符类型是可以进行运算的,相当于一个整数,因为它都对应有 Unicode 码。
🎀 字符串类型
和字符不一样的是,字符的赋值是单引号,字符串的赋值是双引号
cpp
var s string = "枫枫知道"
fmt.Println(s)
🎀 转义字符
一些常用的转义字符
cpp
fmt.Println("枫枫\t知道") // 制表符
fmt.Println("枫枫\n知道") // 回车
fmt.Println("\"枫枫\"知道") // 双引号
fmt.Println("枫枫\r知道") // 回到行首
fmt.Println("C:\\pprof\\main.exe") // 反斜杠
🎀 多行字符串
cpp
package main
import "fmt"
func main() {
var s = `今天
天气
真好
`
fmt.Println(s)
}
在 `` 这个里面,再出现转义字符就会原样输出了
🎀 布尔类型
布尔型数据只有 true(真)和 false(假)两个值
- 布尔类型变量的默认值为 false
- Go 语言中不允许将整型强制转换为布尔型
- 布尔型无法参与数值运算,也无法与其他类型进行转换
🎀 零值问题
如果我们给一个基本数据类型只声明不赋值
那么这个变量的值就是对应类型的零值,例如 int 就是 0,bool 就是 false,字符串就是 ""
cpp
package main
import "fmt"
func main() {
var a1 int
var a2 float32
var a3 string
var a4 bool
fmt.Printf("%#v\n", a1)
fmt.Printf("%#v\n", a2)
fmt.Printf("%#v\n", a3)
fmt.Printf("%#v\n", a4)
}
六:🔥 数组、切片、map
🦋 数组
数组(Array)是一种非常常见的数据类型,几乎所有的计算机编程语言中都会用到它
- 数组里的元素必须全部为同一类型,要嘛全部是字符串,要嘛全部是整数
- 声明数组时,必须指定其长度或者大小
cpp
package main
import "fmt"
func main() {
var array [3]int = [3]int{1, 2, 3}
fmt.Println(array)
var array1 = [3]int{1, 2, 3}
fmt.Println(array1)
var array2 = [...]int{1, 2, 3}
fmt.Println(array2)
}
如果要修改某个值,只能根据索引去找然后替换
cpp
var array1 = [3]int{1, 2, 3}
array1[0] = 10 // 根据索引找到对应的元素位置,然后替换
fmt.Println(array1)
🎀 索引
cpp
package main
import "fmt"
func main() {
// 以定义一个字符串数组 a b c d 为例
var sList = []string{"a", "b", "c", "d"}
// 正向索引就从0开始,从左往右
/*
a b c d
0 1 2 3
*/
// 取值就通过索引去取
fmt.Println(sList[0]) // 拿到a这个元素
// 当然,有的语言支持负向索引,go不支持
/*
a b c d
-4 -3 -2 -1
*/
// 如果在go语言里面,我想拿到倒数第二个元素,怎么办?
fmt.Println(sList[len(sList)-2]) // 拿到a这个元素
}
🦋 切片
很明显啊,go 里面的数组,长度被限制死了,所以不经常用
所以 go 出了一个数组 plus,叫做 slice(切片)
切片(Slice)相较于数组更灵活,因为在声明切片后其长度是可变的
cpp
package main
import "fmt"
func main() {
// 定义一个字符串切片
var list []string
list = append(list, "枫枫")
list = append(list, "知道")
fmt.Println(list)
fmt.Println(len(list)) // 切片长度
// 修改第二个元素
list[1] = "不知道"
fmt.Println(list)
}
🎀 make 函数
除了基本数据类型,其他数据类型如果只定义不赋值,那么实际的值就是 nil
cpp
// 定义一个字符串切片
var list []string
fmt.Println(list == nil) // true
那么我们可以通过make函数创建指定长度,指定容量的切片了
cpp
make([]type, length, capacity)
cpp
package main
import "fmt"
func main() {
// 定义一个字符串切片
var list = make([]string, 0)
fmt.Println(list, len(list), cap(list))
fmt.Println(list == nil) // false
list1 := make([]int, 2, 2)
fmt.Println(list1, len(list1), cap(list1))
}
为什么叫切片
因为切片是数组切出来的
cpp
package main
import "fmt"
func main() {
var list = [...]string{"a", "b", "c"}
slices := list[:] // 左一刀,右一刀 变成了切片
fmt.Println(slices)
fmt.Println(list[1:2]) // b
}
切片排序
cpp
var list = []int{4, 5, 3, 2, 7}
fmt.Println("排序前:", list)
sort.Ints(list)
fmt.Println("升序:", list)
sort.Sort(sort.Reverse(sort.IntSlice(list)))
fmt.Println("降序:", list)
🦋 map
Go 语言中的 map (映射、字典)是一种内置的数据结构,它是一个无序的 key-value 对的集合
map 的 key 必须是基本数据类型,value可以是任意类型
注意,map使用之前一定要初始化
cpp
package main
import "fmt"
func main() {
// 声明
var m1 map[string]string
// 初始化1
m1 = make(map[string]string)
// 初始化2
m1 = map[string]string{}
// 设置值
m1["name"] = "枫枫"
fmt.Println(m1)
// 取值
fmt.Println(m1["name"])
// 删除值
delete(m1, "name")
fmt.Println(m1)
}
也可以声明并赋值
cpp
// 声明并赋值
var m1 = map[string]string{}
var m2 = make(map[string]string)
🎀 map取值
如果只有一个参数接,那这个参数就是值,如果没有,这个值就是类型的零值
如果两个参数接,那第二个参数就是布尔值,表示是否有这个元素
cpp
package main
import "fmt"
func main() {
// 声明并赋值
var m1 = map[string]int{
"age": 21,
}
age1 := m1["age1"] // 取一个不存在的
fmt.Println(age1)
age2, ok := m1["age1"]
fmt.Println(age2, ok)
}
七:🔥 判断语句
🦋 if语句
以年龄为例,输入的年龄在某一个区间,就输出对应的提示信息
cpp
<=0 未出生
1-18 未成年
18-35 青年
>=35 中年
很明显,这是一个多选一的情况
我们有很多中方式来实现
中断式
package main
cpp
import "fmt"
func main() {
fmt.Println("请输入你的年龄:")
var age int
fmt.Scan(&age)
if age <= 0 {
fmt.Println("未出生")
return
}
if age <= 18 {
fmt.Println("未成年")
return
}
if age <= 35 {
fmt.Println("青年")
return
}
fmt.Println("中年")
}
它也有个好听的名字,叫卫语句
嵌套式
cpp
package main
import "fmt"
func main() {
fmt.Println("请输入你的年龄:")
var age int
fmt.Scan(&age)
if age <= 18 {
if age <= 0 {
fmt.Println("未出生")
} else {
fmt.Println("未成年")
}
} else {
if age <= 35 {
fmt.Println("青年")
} else {
fmt.Println("中年")
}
}
}
多条件式
cpp
package main
import "fmt"
func main() {
fmt.Println("请输入你的年龄:")
var age int
fmt.Scan(&age)
if age <= 0 {
fmt.Println("未出生")
}
if age > 0 && age <= 18 {
fmt.Println("未成年")
}
if age > 18 && age <= 35 {
fmt.Println("青年")
}
if age > 35 {
fmt.Println("中年")
}
}
🦋 switch 语句
还是上面那个案例,如果是用 switch 就很直观了
cpp
package main
import "fmt"
func main() {
fmt.Println("请输入你的年龄:")
var age int
fmt.Scan(&age)
switch {
case age <= 0:
fmt.Println("未出生")
case age <= 18:
fmt.Println("未成年")
case age <= 35:
fmt.Println("青年")
default:
fmt.Println("中年")
}
}
除了这样的写法,还有枚举所有的可能值
例如
cpp
1 星期一
2 星期二
3 星期三
cpp
package main
import "fmt"
func main() {
fmt.Println("请输入星期数字:")
var week int
fmt.Scan(&week)
switch week {
case 1:
fmt.Println("周一")
case 2:
fmt.Println("周二")
case 3:
fmt.Println("周三")
case 4:
fmt.Println("周四")
case 5:
fmt.Println("周五")
case 6, 7:
fmt.Println("周末")
default:
fmt.Println("错误")
}
}
可以理解为 case 的值就是 switch 的枚举结果
一般来说,go 的 switch 的多选一,满足其中一个结果之后,就结束 switch 了
加上 fallthrough 则可以继续执行
例如
cpp
package main
import "fmt"
func main() {
fmt.Println("请输入你的年龄:")
var age int
fmt.Scan(&age)
switch {
case age <= 0:
fmt.Println("未出生")
case age <= 18:
fmt.Println("未成年")
case age <= 35:
fmt.Println("青年")
default:
fmt.Println("中年")
}
}
我输入一个12,我希望它能输出满足的所有条件,例如我希望它输出,未成年,青年
cpp
package main
import "fmt"
func main() {
fmt.Println("请输入你的年龄:")
var age int
fmt.Scan(&age)
switch {
case age <= 0:
fmt.Println("未出生")
fallthrough
case age <= 18:
fmt.Println("未成年")
fallthrough
case age <= 35:
fmt.Println("青年")
default:
fmt.Println("中年")
}
}
八:🔥 for 循环
任何编程语言,都会有for循环,它的一般写法是
cpp
for 初始化;条件;操作{
}
例如求1+2+...+100的和
cpp
package main
import "fmt"
func main() {
var sum = 0
for i := 0; i <= 100; i++ {
sum += i
}
fmt.Println(sum)
}
🦋 for循环的五种变体
传统for循环
如上
死循环
每隔1秒打印当前的时间
cpp
package main
import (
"fmt"
"time"
)
func main() {
for {
time.Sleep(1 * time.Second)
fmt.Println(time.Now().Format("2006-01-02 15:04:05")) // 年月日时分秒的固定格式
}
}
while 模式
由于 golang 没有 while 循环,如果需要,则是由 for 循环稍微变化得来
cpp
package main
import "fmt"
func main() {
i := 0
sum := 0
for i <= 100 {
sum += i
i++
}
fmt.Println(sum)
}
do-while 模式
do-while 模式就是先执行一次循环体,再判断
cpp
package main
import "fmt"
func main() {
i := 0
sum := 0
for {
sum += i
i++
if i == 101 {
break
}
}
fmt.Println(sum)
}
遍历切片,map
遍历切片
第一个参数是索引,第二个参数是
cpp
package main
import "fmt"
func main() {
s := []string{"枫枫", "知道"}
for index, s2 := range s {
fmt.Println(index, s2)
}
}
遍历 map
第一个参数就是 key,第二个就是 value
cpp
package main
import "fmt"
func main() {
s := map[string]int{
"age": 24,
"price": 1000,
}
for key, val := range s {
fmt.Println(key, val)
}
}
break,continue
break 用于跳出当前循环
continue 用于跳过本轮循环
例如打印九九乘法表
cpp
package main
import "fmt"
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <= i; j++ {
fmt.Printf("%d * %d = %d\t", i, j, i*j)
}
fmt.Println()
}
}
除了这样写,还能这样写
cpp
package main
import "fmt"
func main() {
for i := 1; i <= 9; i++ {
for j := 1; j <= 9; j++ {
if j > i {
// 去掉 列比行大的数据
continue
}
fmt.Printf("%d * %d = %d\t", i, j, i*j)
}
fmt.Println()
}
}
九:🔥 函数
函数定义
cpp
package main
import "fmt"
// 使用func关键字定义一个函数
func sayHello() {
fmt.Println("hello")
}
func main() {
// 函数()调用函数
sayHello()
}
函数参数
cpp
package main
import "fmt"
func add(n1 int, n2 int) {
fmt.Println(n1, n2)
}
// 参数类型一样,可以合并在一起
func add1(n1, n2 int) {
fmt.Println(n1, n2)
}
// 多个参数
func add2(numList ...int) {
fmt.Println(numList)
}
func main() {
add(1, 2)
add1(1, 2)
add2(1, 2)
add2(1, 2, 3, 4)
}
函数返回值
cpp
package main
import "errors"
// 无返回值
func fun1() {
return // 也可以不写
}
// 单返回值
func fun2() int {
return 1
}
// 多返回值
func fun3() (int, error) {
return 0, errors.New("错误")
}
// 命名返回值
func fun4() (res string) {
return // 相当于先定义再赋值
//return "abc"
}
func main() {
}
匿名函数
cpp
package main
import "fmt"
func main() {
var add = func(a, b int) int {
return a + b
}
fmt.Println(add(1, 2))
}
🎀 高阶函数
根据用户输入的不同,执行不同的操作
cpp
package main
import "fmt"
func main() {
fmt.Println("请输入要执行的操作:")
fmt.Println(`1:登录
2:个人中心
3:注销`)
var num int
fmt.Scan(&num)
var funcMap = map[int]func(){
1: func() {
fmt.Println("登录")
},
2: func() {
fmt.Println("个人中心")
},
3: func() {
fmt.Println("注销")
},
}
funcMap[num]()
}
提取出来
cpp
package main
import "fmt"
func login() {
fmt.Println("登录")
}
func userCenter() {
fmt.Println("个人中心")
}
func logout() {
fmt.Println("注销")
}
func main() {
fmt.Println("请输入要执行的操作:")
fmt.Println(`1:登录
2:个人中心
3:注销`)
var num int
fmt.Scan(&num)
var funcMap = map[int]func(){
1: login,
2: userCenter,
3: logout,
}
funcMap[num]()
}
🎀 闭包
设计一个函数,先传一个参数表示延时,后面再次传参数就是将参数求和
例如
cpp
fun(2)(1,2,3) // 延时2秒求1+2+3
package main
import (
"fmt"
"time"
)
func awaitAdd(t int) func(...int) int {
time.Sleep(time.Duration(t) * time.Second)
return func(numList ...int) int {
var sum int
for _, i2 := range numList {
sum += i2
}
return sum
}
}
func main() {
fmt.Println(awaitAdd(2)(1, 2, 3))
}
值传递和引用传递
稍微了解过编程的都应该知道,计算机上显示的所有的数据都是在内存里面的
也就是说,我们定义的一个变量,它也在内存里面有一块地
正常情况下来说,函数的参数是将之前那块地复制了一份出来
如何证明呢
cpp
package main
import "fmt"
func add(num int) {
fmt.Println(&num) // 可以看到,这个n的内存地址和外面num的内存地址是明显不一样的
num = 2 // 这里的修改不会影响外面的num
}
func main() {
num := 20
fmt.Println(&num)
add(num)
fmt.Println(num) // 20
}
也就是说,在函数里面不管怎么修改这个参数,都不会影响原来的那个值
但是,如果我需要在函数体内修改变量的值呢?
这就需要用到引用传递了
我们直接将变量的内存地址传递进去
cpp
package main
import "fmt"
func add(num *int) {
fmt.Println(num) // 内存值是一样的
*num = 2 // 这里的修改会影响外面的num
}
func main() {
num := 20
fmt.Println(&num)
add(&num)
fmt.Println(num) // 成功修改 2
}
指针
上面那个案例搞清楚之后,指针也就不难了
我们只需要知道
&是取地址,*是解引用,去这个地址指向的值
🦋 init 函数和 defer 函数
init 函数
init() 函数是一个特殊的函数,存在以下特性:
- 不能被其他函数调用,而是在 main 函数执行之前,自动被调用
- init 函数不能作为参数传入
- 不能有传入参数和返回值
- 一个 go 文件可以有多个 init 函数,谁在前面谁就先执行
cpp
package main
import "fmt"
func init() {
fmt.Println("init1")
}
func init() {
fmt.Println("init2")
}
func init() {
fmt.Println("init3")
}
func main() {
fmt.Println("main")
}
执行顺序
cpp
init1
init2
init3
main

虽然一个 package 里面可以写任意多个 init 函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个 package 中每个文件只写一个 init 函数。
go 程序会自动调用 init() 和 main(),所以你不需要在任何地方调用这两个函数。每个 package 中的 init 函数都是可选的,但 package main 就必须包含一个 main 函数。
程序的初始化和执行都起始于 main 包。
如果 main 包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到 fmt 包,但它只会被导入一次,因为没有必要导入多次)。
当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。
等所有被导入的包都加载完毕了,就会开始对 main 包中的包级常量和变量进行初始化,然后执行 main 包中的 init 函数(如果存在的话),最后执行 main 函数
defer 函数
- 关键字 defer 用于注册延迟调用
- 这些调用直到 return 前才被执。因此,可以用来做资源清理
- 多个 defer 语句,按先进后出的方式执行,谁离 return 近谁先执行
- defer 语句中的变量,在 defer 声明时就决定了
cpp
package main
import "fmt"
func Func() {
defer fmt.Println("defer2")
fmt.Println("func")
defer fmt.Println("defer1")
}
func main() {
defer fmt.Println("defer4")
Func()
defer fmt.Println("defer3")
}
执行顺序
cpp
func
defer1
defer2
defer3
defer4
十:🔥 共勉
😋 以上就是我对 【Go】环境搭建与基本使用 的理解, 觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~ 😉
