前言

语言学习来源:

原来过语言是看个输入输出直接去做题(但是后面看其实没怎么学跟项目有关的),而且这赶上期末&手上有抓紧的项目,换一种方法。速通一下语言,之前学过一点点go语言皮毛,本文主要记录go在语言方面我觉得有趣不同的地方。ps:下一篇涉及并发编程&单元测试

string&rune

字符串是以 byte 数组形式保存的,类型是 uint8,占1个 byte(中文一般占3byte),打印时需要用 string 进行类型转换&用printf格式化,否则打印的是编码值。
为了更方便地处理字符(尤其是 Unicode 字符),Go 提供了 rune 类型作为字符的抽象,尤其是在处理多字节字符时,例如,字符 汉 是一个 Unicode 字符,在 UTF-8 编码下占用了 3 个字节,但作为 rune 类型,它是一个单独的值,可以直接进行操作。

1
2
3
4
5
6
7
str := "Hello, 世界!"
rs := []rune(str)
for _, r := range runes {
fmt.Printf("%c ", r)
// 输出每个字符,分别是 H e l l o , 世 界 !
}

切片

切片实际上是一个描述符,包含以下三个部分:

  • 指向底层数组的指针:切片的数据存储在底层数组中。
  • 长度(length):切片当前的元素数。
  • 容量(capacity):从切片起始位置到底层数组末尾的最大元素数。

多个切片可能共享同一个底层数组,如果需要独立操作,应使用 copy 创建新的切片。如果切片扩容导致底层数组更换,原切片和新切片将不再共享同一数组。

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//从数组创建
arr := [5]int{1, 2, 3, 4, 5} // 数组
s := arr[1:4] // 从数组创建切片,包含索引 1 到 3 的元素
fmt.Println(s) // [2 3 4]

//使用make
s := make([]int, 5) // 创建长度为 5 的切片,初始值为零值
fmt.Println(s) // [0 0 0 0 0]

s2 := make([]int, 3, 5) // 创建长度为 3,容量为 5 的切片
fmt.Println(s2) // [0 0 0]

//make函数
make(type, len, cap)
//type: 数据结构的类型,必须是 slice、map 或 channel。
//len: 数据结构的长度(对于切片和通道来说是可选参数)。
//cap: 数据结构的容量(仅对切片和带缓冲的通道有效,是可选参数)。
//如果没有显式指定容量,默认容量等于长度:

切片操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//获取长度,容量
s := []int{1, 2, 3, 4, 5}
fmt.Println(len(s)) // 长度: 5
fmt.Println(cap(s)) // 容量: 5

//切片
s := []int{1, 2, 3, 4, 5}
sub := s[1:4] // 从索引 1 到索引 3(不包含 4)
fmt.Println(sub) // [2 3 4]

sub = s[:3] // 从索引 0 到 2
fmt.Println(sub) // [1 2 3]

sub = s[2:] // 从索引 2 到末尾
fmt.Println(sub) // [3 4 5]

//增长
s := []int{1, 2, 3}
s = append(s, 4, 5) // 增加多个元素
fmt.Println(s) // [1 2 3 4 5]

字典

1
2
3
4
5
6
7
// 仅声明
m1 := make(map[string]int)
// 声明时初始化
m2 := map[string]string{
"Sam": "Male",
"Alice": "Female",
}

defer&recover

被 defer 的函数调用会在所在函数返回前(无论是正常返回还是因 panic 提前退出)被执行。
函数运行时,先跑别的,然后去找并运行defer函数(被 defer 的函数调用会在所在函数返回前——无论是正常返回还是因 panic 提前退出——被执行。)。在defer函数内部调用recover函数,捕获恢复程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func get(index int) (ret int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Some error happened!", r)
ret = -1
}
}()
arr := [3]int{2, 3, 4}
return arr[index]
}

func main() {
fmt.Println(get(5))
fmt.Println("finished")
}

接口

核心机制

隐式接口实现

Go 使用隐式接口实现机制,意思是你不需要显式地声明一个类型实现了某个接口。

只要一个类型提供了接口所要求的全部方法(包括方法的名字、参数、返回值类型都一致),那么 Go 就会认为这个类型实现了该接口。

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
type Person interface {
getName() string
}

type Student struct {
name string
age int
}

func (stu *Student) getName() string {
return stu.name
}

type Worker struct {
name string
gender string
}

func (w *Worker) getName() string {
return w.name
}

func main() {
var p Person = &Student{
name: "Tom",
age: 18,
}

fmt.Println(p.getName()) // Tom
}

执行过程:

  • 定义接口 Person 和两个结构体 Student 和 Worker。
  • 实现 getName 方法,使得 Student 和 Worker 都满足 Person 接口。
  • 在 main 函数中:
    • 创建一个 *Student 的实例。
    • 将其赋值给 Person 类型变量 p。
    • 调用 p.getName() 方法,实际执行的是 *Student.getName,返回 Tom。