Go语言基础
基础语法
1 | package main |
格式化输入输出
Go 语言中使用 fmt.Sprintf 或 fmt.Printf 格式化字符串并赋值给新串:
1 | package main |
变量
变量声明的两种主要形式——
1 | var a string = "hello" |
常见变量声明——
1 | var ( |
go 语言跟一般的语言不同,它使用变量首字母的大小写来区分变量的可导出性质
- 大写 (如果使用中文作为变量名称,默认是可导出的) 代表可导出
- 小写代表仅限包内部使用 (包这一级,多个文件只要是同一个包就可以使用)
1 | package Example |
其中 OutPutName
是一个可导出的变量,inName
是不可导出变量
小心 shadow 的变量
当有两个两个以上的变量在赋值时,如果其中有一个未被提前声明,那么就需要使用 :=
,这个时候系统会自动判断有哪些未提前声明
1 | func WithName(){ |
在外层,a 已经提前声明,但是在 if 这个作用域中,由于 err 并未提前声明,所以使用了 :=
,由于系统无法获知这里的 a 是否需要再次声明,所以 go 语言默认 a 是一个新的变量,这样外层的 a 就无法得到新的值,外层 a 也就被内层的 a 给 shadow 了。
解决——可以将 err 也提前声明,或者也可以改变内部的变量名称,再赋值回去1
2ai,err := example.Method()
a = ai
常量
普通常量
常量不可包含计算
在 Go 中,常量必须在编译时就可以确定其值。因此,常量的值必须是一个编译时的常量表达式,不能包含运行时的计算。1
2
3
4
5const (
x = 2 + 3 // 常量表达式,结果为 5
y = (x * 2) % 10 // 常量表达式,结果为 0,注意默认x=0,因为是编译时就确定值了!!!
z = x == 5 || y == 0 // 常量表达式,结果为 true,注意默认x,y=0
)
复制与枚举常量 iota
go 语言提供隐式重复前一个非空表达式的机制
1 | package main |
这段代码将会全部输出 3.14,因为a~f未声明,表示其复制了上一个
iota 是 go 语言的一个预定义标识符,它表示 const 声明块中,每一个常量所处位置的偏移量
1 | const( |
零值
下面是所有的原生 go 类型的零值:
- 整数类型:0
- 浮点数:0.0
- 布尔类型:false
- 字符串类型:“”
- 指针,接口,切片,channel,map,function:nil
一般情况下,0值都是可以直接用的。但是下列情况除外——
- slice 零值赋值
- map 零值赋值
- 互斥锁的值复制
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// slice的例子
var s slice
s[0] = 1 // 错误 ❌
//map的例子
var m map[int]int
m[1] = 12 // 错误 ❌
// 互斥锁的例子
package main
import (
"sync"
)
func main() {
var mu sync.Mutex
mu.Lock()
foo(mu)
mu.Unlock()
}
func foo(mu sync.Mutex) {
mu.Lock()
mu.Unlock()
}
分支语句
IF语句
1 | package main |
FOR循环
1 | package main |
FOR- EACH类循环
1 | package main |
函数
1 | /* 函数返回两个数的最大值 */ |
Go语言支持打包传回多个参数
1 | package main |
默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
类型
常用类型
数组
数组的格式为1
2
3
4
5
6
7
8
9
10
11
12var arrayName [size]dataType
// 例如
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
// 若数组的元素大小size暂不确定,可以使用...来代替,或省略
var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
// 创建三一维数组,各数组长度不同
row1 := []string{"fish", "shark", "eel"}
row2 := []string{"bird"}
row3 := []string{"lizard", "salamander"}
多维数组1
2
3
4
5var variable_name [SIZE1][SIZE2]...[SIZEN] variable_type
var threedim [5][10][4]int
// 创建空的二维数组
animals := [][]string{}
向函数传递数组
如果你想要在函数内修改原始数组,可以通过传递数组的指针来实现。
1 | // 对比区分 |
特殊类型
指针
指针声明:var ip *int /* 指向整型*/
简单的指针使用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package main
import "fmt"
func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
fmt.Printf("a 变量的地址是: %x\n", &a )
/* 指针变量的存储地址 */
fmt.Printf("ip 变量储存的指针地址: %x\n", ip )
/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}
当一个指针被定义后没有分配到任何变量时,它的值为 nil。
指针数组:var ptr [MAX]*int;
每个元素都是一个指针
结构体
结构体的声明与导出 & 调用
1 | type People struct { |
- Peopel 首字母大写,根据我们所学的首字母大写可导出的知识,它是包级可导出结构
- Addr 首字母大写,所以它是可导出字段,name 和 year 都是小写,所以他们俩不可导出
1 | // 结构体的调用 |
切片Slice
Go 语言切片是对数组的抽象。
切片基本操作
1 | // 初始化一个切片,设置长度为5,容量为10(容量为可选参数) |
一个切片在未初始化之前默认为 nil,长度为 0
切片复制
copy 是 go 语言的内置函数,全局使用,copy(a,b []Type)
,copy 是深度拷贝,它将后者的数据完全拷贝给前者。
要注意的是,将要被复制的元素能复制的量取决于前者的 length
比如下面这种情况,被复制的元素就是 0,但是并不会 panic
1 | src := []int{1,2,3} |
一般来说,我们会使用相同的 length:
1 | src := []int{1,2,3} |
或者直接使用 append 也能做到 copy
1 | src := []int{1,2,3} |
切片中的 range 注意事项
range 时,我们直接修改返回的值是不会生效的,因为返回的值并不是原始的数据,而是数据的复制。
1 | type Student struct { |
也就是说,这里的 student :=
student 的改变不会影响 result 中的任何数据,除非这里的 []Student
是 []*Student
下面我们演示一下,正确的在 range 时的操作方式:
1 | type Student struct { |
正确的方式就是直接使用 result 本身进行操作,就可以真正的去改变 result 了。
Map
1 | // 创建一个空的 Map |
通道Chan
理解为队列,先进先出
1 | // 声明不带缓冲的通道 |
完整代码
1 | ch1 := make(chan string, 10) |
- close 以后不能再写入,写入会出现 panic
- 重复 close 会出现 panic
- 只读的 chan 不能 close
- close 以后还可以读取数据
类型转换和判断
类型转换
1 | var a int = 10 |
字符串转换为整数
注意,strconv.Atoi 函数返回两个值,第一个是转换后的整型值,第二个是可能发生的错误,我们可以使用空白标识符 _ 来忽略这个错误
1 | package main |
类型判断
1 | func main() { |
接口
它定义了一个或多个方法签名。接口是 Go 语言中实现多态的关键特性之一。接口在 Go 中的使用与许多其他编程语言中的接口或抽象类相似
1 | package main |
空接口
空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口。
空接口类型的变量可以存储任意类型的变量。
1 | func main() { |
- 空接口与函数传参
可以传入任意的参数,就等同于TS的any
1 | // 空接口作为函数参数 |
- 空接口存储任意的map
1
2
3
4
5
6// 空接口作为map值
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "李白"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo)
其他
defer
关键字
1 | defer dao.Close() // 程序退出关闭数据库连接 |
defer关键字用于延迟执行函数。当你在函数中使用defer时,它会将你要执行的函数以及参数暂存起来,等到当前函数执行完毕后再执行这些暂存的函数。
这通常用于资源管理,比如打开的文件、网络连接或者数据库连接,你可以在函数结束时确保这些资源被正确关闭。
[1] https://github.com/shgopher/GOFamily
[2] https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-context/
[3] https://www.topgoer.cn/docs/golang/golang-1ccjbpfstsfi1