————本笔记对有基础的人有用,因有一定基础所以前面的基础语法等知识就不做笔记记载了
1.切片——>就是相相当于动态数组
切片(slice)是对数组的引用,是 Go 里最常用的动态序列结构。
它本质是一个结构体,包含 3 个东西:
- 指针 ptr:指向底层数组
- 长度 len:当前有多少元素
- 容量 cap:底层数组最多能放多少元素切片的构造方法:1. 字面量(最常用)s := []int{1, 2, 3} // len=3, cap=3 –>这代表创建的长度为3容量为32.make(推荐)s := make([]int, 5) // len=5, cap=5 s := make([]int, 0, 10) // len=0, cap=10(预分配,性能高)3.声明空切片var s []int // nil 切片,len=0, cap=0 s := []int{} // 非 nil,len=0, cap=04.从数组 / 切片截取arr := [5]int{1,2,3,4,5} s := arr[1:4] // [2,3,4] // 左闭右开 [start, end)新切片 len = high – low新切片 cap = 原 cap – low截取出来的切片共用底层数组,修改会互相影响
len 和 cap 的区别
- len:你能访问、能赋值的元素个数
- cap:底层数组大小,决定 append 什么时候扩容
s := make([]int, 3, 5)
s[0] = 1 // 可以
s[3] = 4 // 报错!越界,因为 len=3
append(最核心操作)
s = append(s, 1)
s = append(s, 2, 3)
s = append(s, anotherSlice…)
- 如果
len + 新增元素 ≤ cap→ 直接放,不扩容,底层数组不变 - 如果不够→ 自动扩容,底层数组换新,数据复制过去
- 容量 < 1024:翻倍
- 容量 ≥ 1024:每次扩 1.25 倍
- 最终容量会按内存对齐调整
切片复制 copy
copy 会真正复制数据,不再共用底层数组:
a := []int{1,2,3}
b := make([]int, len(a))
copy(b, a)
切片遍历
for i := 0; i < len(s); i++ { fmt.Println(s[i]) }
多维切片–>和二维数组的区别较大
rows := 3 cols := 4
// 1. 先创建外层切片,有 rows 个元素 mat := make(int, rows)
// 2. 逐行初始化每一行
for i := range mat { mat[i] = make([]int, cols) // 每行长度为 cols }
常见坑(必看)
- 截取后共用数组,修改互相污染
- append 扩容后,切片地址变了
- 函数内 append 不影响外部原切片
- 越界 panic:只能访问 0~len-1
- cap 只和底层数组有关,和 len 无关通道的相关知识Channel 就是协程(goroutine)之间的 “传送带”,用来发数据、收数据、同步执行。Go 里有很多 goroutine(轻量级线程)同时跑,它们之间要传数据、互相等对方,就用 channel。你可以把它理解成:
- 一个管道
- 一端塞数据(send)
- 一端拿数据(receive)定义一个 channelch := make(chan int)
chan是关键字int是通道里传的数据类型发送数据ch <- 10接收数据x := <-ch案例func main() { ch := make(chan string) go func() { // 往通道发数据 ch <- “hello channel” }() // 从通道取数据 msg := <-ch fmt.Println(msg) }1.无缓冲通道(默认)ch := make(chan int)特点:- 必须有人收,你才能发
- 必须有人发,你才能收
- 一步一步同步走
2.带缓冲通道
ch := make(chan int, 3) // 缓冲大小 3
特点:
- 可以先存 3 个数据
- 存满了才会阻塞
- 适合异步、限流、任务队列关闭通道close(ch)关闭后不能再发送还可以继续接收,直到取完配合
for range遍历通道for v := range ch { fmt.Println(v) }通道的方向(只发 / 只收)// 只允许发送 func send(ch chan<- int) { ch <- 1 } // 只允许接收 func recv(ch <-chan int) { x := <-ch }
Go 语言范围(Range)
go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,
读取数组和切片
for key, value := range oldMap { newMap[key] = value }–>特别说明,key代表下标,value代表数值,可以只读取其中的一个,也可以两个一起读取
for key, _ := range oldMap
for _, value := range oldMap
for i, c := range “hello” ——>字符串同理
range 遍历从通道接收的值,直到通道关闭。
func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 close(ch)
for v := range ch { fmt.Println(v) } }–>如果看到这个有疑问可以专门看看上面的通道的部分知识点
Map(集合)
// 创建一个空的 Map m := make(map[string]int)–>就是把前面的字符串和后面的int类型联系起来
// 创建一个初始容量为 10 的 Map m := make(map[string]int, 10)
例如:
m := map[string]int{ “apple”: 1, “banana”: 2, “orange”: 3, }
// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"] // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
// 删除键值对
delete(m, "banana")
Go 语言递归函数
递归函数通常包含两个部分:
- 基准条件(Base Case):这是递归的终止条件,防止函数无限调用自身。
- 递归条件(Recursive Case):这是函数调用自身的部分,用于将问题分解为更小的子问题。例如阶乘的递归:package mainimport “fmt”// 递归函数计算阶乘 func factorial(n int) int { // 基准条件 if n == 0 { return 1 } // 递归条件 return n * factorial(n-1) }func main() { fmt.Println(factorial(5)) // 输出: 120 }
强制类型转化(太麻烦了没有其它语言简洁,用到的话上网查吧)
| 转换场景 | 写法 |
|---|---|
| int → float64 | float64(10) |
| float → int | int(3.99) → 3(截断) |
| int → 字符串 “123” | strconv.Itoa(123) |
| string “123” → int | strconv.Atoi("123") |
| string ↔ []byte | []byte("abc") / string(b) |
| interface {} → 真实类型 | i, ok := x.(int) |
| 类型判断 | switch x.(type) |
接口
type 接口名 interface {
方法名(参数) 返回值
方法名(参数) 返回值
}
实例:接口的定义加实现:
type Animal interface { Speak() string }
type Dog struct{}
// Dog 实现了 Speak 方法
func (d Dog) Speak() string
{ return “汪汪汪” }
func main() {
var a Animal // 接口类型变量
a = Dog{} // 因为 Dog 实现了 Animal,所以可以赋值 fmt.Println(a.Speak()) // 汪汪汪 }
接口套用和动态接口就不详细说明了
Go 语言泛型
泛型是 Go 语言在 1.18 版本中引入的重要特性,它让开发者能够编写更加灵活和可重用的代码。
泛型主要通过以下两个核心概念来实现:
- 类型参数(Type Parameters):允许你在函数或类型定义中使用一个或多个类型作为参数。
- 类型约束(Type Constraints):指定类型参数必须满足的条件,确保在函数内部可以安全地操作这些类型。
| 概念 | 作用 | 示例 |
|---|---|---|
| 类型参数 | 在函数或类型名后声明,表示待定的类型。 | [T any] |
| 类型约束 | 定义类型参数必须满足的条件(如支持的操作符或方法)。 | int,float64,comparable,constraints.Ordered,any |
any | 约束类型参数为任何类型。 | [T any] |
comparable | 约束类型参数为可比较的类型。 | [K comparable] |
特别说明:可比较的类型就是说能用==/=/!=的类型
func Max[T comparable] (a,b T) T
{
if a>b{
return a;
}
return b;
}
func 函数名[T 约束] (参数 T) 返回值类型 { // 函数体 }
type 类型名[T 约束] struct { // 结构体字段 }
当接口和泛化连用时可以指定个别类型
type Number interface { int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64 }
func Add [T Number] (a, b T) T { return a + b }
// 使用示例 fmt.Println(Add(10, 20)) // 输出: 30 fmt.Println(Add(3.14, 2.71)) // 输出: 5.85
func Add [T int | float64 | string] (a, b T) T { return a + b }//这样也可以
具体常用泛化实践看Go 语言泛型 | 菜鸟教程
Go语言的错误处理看这里:Go 错误处理 | 菜鸟教程()
主要本人错误处理是真看不懂了(不论是java还是go的错误处理,但是go语言的错误处理还是简单的)
Go语言的并发运行
Goroutines:
- Go 中的并发执行单位,类似于轻量级的线程。
- Goroutine 的调度由 Go 运行时管理,用户无需手动分配线程。
- 使用
go关键字启动 Goroutine。 - Goroutine 是非阻塞的,可以高效地运行成千上万个 Goroutine。
Channel:
- Go 中用于在 Goroutine 之间通信的机制。
- 支持同步和数据共享,避免了显式的锁机制。
- 使用
chan关键字创建,通过<-操作符发送和接收数据。
package main
import ( “fmt” “time” )
func sayHello() { for i := 0; i < 5; i++ { fmt.Println(“Hello”) time.Sleep(100 * time.Millisecond) } }
func main() { go sayHello() // 启动 Goroutine for i := 0; i < 5; i++ { fmt.Println(“Main”) time.Sleep(100 * time.Millisecond) } }
这就是两个进程并行运算:sayHello和for循环同步进行,你会看到输出的 Main 和 Hello。输出是没有固定先后顺序
第二种并行的话就是通道了往上翻
select 超直白讲解
select 就是专门用来 同时等待多个通道(channel) 的语句
select {
case <-ch1: // ch1 有数据了,执行这里
case ch2 <- 123: // 能往 ch2 发数据,执行这里
default: // 上面都不满足,立刻走这里
}
同时盯着好几个通道
谁先就绪,就跑谁
其他的不管
像多路监听、多路等待。
Go 语言文件处理
(东西太多了,俺记不住,用的时候俺都是现查)
os是核心库:提供底层文件操作(创建、读写、删除等),大多数场景优先使用。io提供通用接口:如Reader/Writer,可与文件、网络等数据源交互。bufio优化性能:通过缓冲减少 I/O 操作次数,适合频繁读写。ioutil已弃用:Go 1.16 后其功能迁移到os和io包。path/filepath处理路径:跨平台兼容(Windows/Unix 路径分隔符差异)。
| 库名 | 主要方法/函数 | 用途说明 | 示例代码 |
|---|---|---|---|
os | Create(name string) (*File, error) | 创建文件(若存在则清空) | file, err := os.Create("test.txt") |
Open(name string) (*File, error) | 只读方式打开文件 | file, err := os.Open("data.txt") | |
OpenFile(name string, flag int, perm FileMode) (*File, error) | 自定义模式打开文件(可指定读写、追加等) | file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_WRONLY, 0644) | |
ReadFile(name string) ([]byte, error) | 一次性读取整个文件内容(小文件适用) | data, err := os.ReadFile("config.json") | |
WriteFile(name string, data []byte, perm FileMode) error | 一次性写入文件(覆盖原有内容) | err := os.WriteFile("out.txt", []byte("Hello"), 0644) | |
Remove(name string) error | 删除文件或空目录 | err := os.Remove("temp.txt") | |
Rename(oldpath, newpath string) error | 重命名或移动文件 | err := os.Rename("old.txt", "new.txt") | |
Stat(name string) (FileInfo, error) | 获取文件信息(大小、权限等) | info, err := os.Stat("file.txt") | |
Mkdir(name string, perm FileMode) error | 创建单个目录 | err := os.Mkdir("mydir", 0755) | |
MkdirAll(path string, perm FileMode) error | 递归创建多级目录 | err := os.MkdirAll("path/to/dir", 0755) | |
ReadDir(name string) ([]DirEntry, error) | 读取目录内容 | entries, err := os.ReadDir(".") | |
io | Copy(dst Writer, src Reader) (written int64, err error) | 从 Reader 复制数据到 Writer(如文件复制) | io.Copy(dstFile, srcFile) |
ReadAll(r Reader) ([]byte, error) | 从 Reader 读取所有数据(类似 os.ReadFile,但针对接口) | data, err := io.ReadAll(file) | |
bufio | NewScanner(r Reader) *Scanner | 创建逐行扫描器(适合逐行读取) | scanner := bufio.NewScanner(file) |
NewReader(rd io.Reader) *Reader | 创建带缓冲的读取器(提高大文件读取效率) | reader := bufio.NewReader(file) | |
NewWriter(w io.Writer) *Writer | 创建带缓冲的写入器(提高写入效率) | writer := bufio.NewWriter(file) | |
ioutil | ReadFile(filename string) ([]byte, error) | (已弃用,推荐 os.ReadFile) | data, err := ioutil.ReadFile("old.txt") |
WriteFile(filename string, data []byte, perm os.FileMode) error | (已弃用,推荐 os.WriteFile) | err := ioutil.WriteFile("out.txt", data, 0644) | |
TempDir(dir, pattern string) (name string, err error) | (已弃用,推荐 os.MkdirTemp) | dir, err := ioutil.TempDir("", "tmp") | |
TempFile(dir, pattern string) (f *os.File, err error) | (已弃用,推荐 os.CreateTemp) | file, err := ioutil.TempFile("", "temp-*") | |
path/filepath | Join(elem ...string) string | 跨平台安全的路径拼接 | path := filepath.Join("dir", "file.txt") |
Walk(root string, fn WalkFunc) error | 递归遍历目录树 | filepath.Walk(".", func(path string, info os.FileInfo, err error) error {...}) | |
Abs(path string) (string, error) | 获取绝对路径 | absPath, err := filepath.Abs("file.txt") |
不同场景推荐使用方法
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 读取小文件 | os.ReadFile("file.txt") | 简洁高效,自动处理打开/关闭 |
| 逐行读取大文件 | bufio.NewScanner(file) | 内存友好,逐行处理 |
| 高效写入大量数据 | bufio.NewWriter(file) + writer.WriteString() | 缓冲减少磁盘 I/O 次数 |
| 递归遍历目录 | filepath.Walk("/path", callback) | 自动处理子目录和错误 |
| 跨平台路径拼接 | filepath.Join("dir", "file.txt") | 自动处理不同操作系统的路径分隔符(/ 或 \) |
Go 语言正则表达式
正则表达式(Regular Expression,简称 regex 或 regexp)是一种用于匹配字符串的强大工具。
正则表达式通过定义一种模式(pattern),可以快速搜索、替换或提取符合该模式的字符串,详细可以参见正则表达式教程。
在 Go 语言中,正则表达式通过 regexp 包来实现。
正则 = 字符串的查找 / 匹配 / 提取 / 替换规则
日常最常用正则模板(手机号、邮箱、身份证、IP、URL)
Go 语言中的 regexp 包
Go 语言的标准库提供了 regexp 包,用于处理正则表达式。以下是 regexp 包中常用的函数和方法:
Compile和MustCompile用于编译正则表达式。Compile返回一个*Regexp对象和一个错误,而MustCompile在编译失败时会直接 panic。MatchString检查字符串是否匹配正则表达式。FindString和FindAllString用于查找匹配的字符串。FindString返回第一个匹配项,FindAllString返回所有匹配项。ReplaceAllString用于替换匹配的字符串。Split根据正则表达式分割字符串。
正则表达式的基本语法(相当于C语言字符串哪里的操作)
以下是一些常用的正则表达式语法:
.:匹配任意单个字符(除了换行符)。*:匹配前面的字符 0 次或多次。+:匹配前面的字符 1 次或多次。?:匹配前面的字符 0 次或 1 次。\d:匹配数字字符(等价于[0-9])。\w:匹配字母、数字或下划线(等价于[a-zA-Z0-9_])。\s:匹配空白字符(包括空格、制表符、换行符等)。[]:匹配括号内的任意一个字符(例如[abc]匹配a、b或c)。^:匹配字符串的开头。$:匹配字符串的结尾。
Go 类型断言
基本语法
类型断言的基本语法如下:
value, ok := interfaceValue.(Type)
interfaceValue是一个接口类型的变量。Type是你想要断言的类型。value是断言成功后的具体类型的值。ok是一个布尔值,表示断言是否成功。
如果断言成功,value 将是 interfaceValue 的实际值,ok 为 true;如果断言失败,value 将是 Type 的零值,ok 为 false。
类型断言的常见用途
1. 处理多种类型的接口值
Go 还提供了特殊的 type switch 语法来测试多种类型:
switch v := i.(type) {
case T1:
// v的类型是T1
case T2:
// v的类型是T2
default:
// 默认情况
}
当接口变量可能存储多种类型的值时,类型断言可以帮助我们根据实际类型执行不同的操作。
Go 中的 “继承”
package main
import “fmt”
// 父结构体 type Animal struct { Name string }
// 父结构体的方法 func (a *Animal) Speak() {//(a Animal) fmt.Println(a.Name, “says hello!”) }
// 子结构体 type Dog struct { Animal // 嵌入 Animal 结构体 Breed string }
func main() { dog := Dog{ Animal: Animal{Name: “Buddy”}, Breed: “Golden Retriever”, }
dog.Speak() // 调用父结构体的方法
fmt.Println("Breed:", dog.Breed)
}
package main
import “fmt”
// 定义接口 type Speaker interface { Speak() }
// 父结构体 type Animal struct { Name string }
// 实现接口方法 func (a *Animal) Speak() {//(a Animal) fmt.Println(a.Name, “says hello!”) }
// 子结构体 type Dog struct { Animal Breed string }
func main() { var speaker Speaker
dog := Dog{
Animal: Animal{Name: "Buddy"},
Breed: "Golden Retriever",
}
speaker = &dog
speaker.Speak() // 通过接口调用方法
}
继承
package main
import “fmt”
// 基类 type Vehicle struct { Brand string }
func (v *Vehicle) Start() { fmt.Println(v.Brand, “started”) }
// 派生类 type Car struct { Vehicle // 嵌入Vehicle Model string }
// 重写Start方法 func (c *Car) Start() { fmt.Println(c.Brand, c.Model, “car started”) }
func main() { v := Vehicle{Brand: “Toyota”} c := Car{ Vehicle: Vehicle{Brand: “Honda”}, Model: “Civic”, }
v.Start() // Toyota started
c.Start() // Honda Civic car started
c.Vehicle.Start() // Honda started
}







