宝剑
今天也要元气满满哦!(。・∀・)ノ゙
Go语言学习笔记
本文最后更新于3 天前,其中的信息可能已经过时,如有错误请发送邮件到chengkun257@gmail.com

————本笔记对有基础的人有用,因有一定基础所以前面的基础语法等知识就不做笔记记载了


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…)

  1. 如果 len + 新增元素 ≤ cap→ 直接放,不扩容,底层数组不变
  2. 如果不够→ 自动扩容,底层数组换新,数据复制过去
  • 容量 < 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 }

常见坑(必看)

  1. 截取后共用数组,修改互相污染
  2. append 扩容后,切片地址变了
  3. 函数内 append 不影响外部原切片
  4. 越界 panic:只能访问 0~len-1
  5. 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 语言递归函数

递归函数通常包含两个部分:

  1. 基准条件(Base Case):这是递归的终止条件,防止函数无限调用自身。
  2. 递归条件(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 → float64float64(10)
float → intint(3.99) → 3(截断)
int → 字符串 “123”strconv.Itoa(123)
string “123” → intstrconv.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 语言文件处理

(东西太多了,俺记不住,用的时候俺都是现查)

  1. os 是核心库:提供底层文件操作(创建、读写、删除等),大多数场景优先使用。
  2. io 提供通用接口:如 Reader/Writer,可与文件、网络等数据源交互。
  3. bufio 优化性能:通过缓冲减少 I/O 操作次数,适合频繁读写。
  4. ioutil 已弃用:Go 1.16 后其功能迁移到 osio 包。
  5. path/filepath 处理路径:跨平台兼容(Windows/Unix 路径分隔符差异)。
库名主要方法/函数用途说明示例代码
osCreate(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(".")
ioCopy(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)
bufioNewScanner(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)
ioutilReadFile(filename string) ([]byte, error)(已弃用,推荐 os.ReadFiledata, err := ioutil.ReadFile("old.txt")
WriteFile(filename string, data []byte, perm os.FileMode) error(已弃用,推荐 os.WriteFileerr := ioutil.WriteFile("out.txt", data, 0644)
TempDir(dir, pattern string) (name string, err error)(已弃用,推荐 os.MkdirTempdir, err := ioutil.TempDir("", "tmp")
TempFile(dir, pattern string) (f *os.File, err error)(已弃用,推荐 os.CreateTempfile, err := ioutil.TempFile("", "temp-*")
path/filepathJoin(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 包中常用的函数和方法:

  1. CompileMustCompile 用于编译正则表达式。Compile 返回一个 *Regexp 对象和一个错误,而 MustCompile 在编译失败时会直接 panic。
  2. MatchString 检查字符串是否匹配正则表达式。
  3. FindStringFindAllString 用于查找匹配的字符串。FindString 返回第一个匹配项,FindAllString 返回所有匹配项。
  4. ReplaceAllString 用于替换匹配的字符串。
  5. Split 根据正则表达式分割字符串。

正则表达式的基本语法(相当于C语言字符串哪里的操作)

以下是一些常用的正则表达式语法:

  • .:匹配任意单个字符(除了换行符)。
  • *:匹配前面的字符 0 次或多次。
  • +:匹配前面的字符 1 次或多次。
  • ?:匹配前面的字符 0 次或 1 次。
  • \d:匹配数字字符(等价于 [0-9])。
  • \w:匹配字母、数字或下划线(等价于 [a-zA-Z0-9_])。
  • \s:匹配空白字符(包括空格、制表符、换行符等)。
  • []:匹配括号内的任意一个字符(例如 [abc] 匹配 abc)。
  • ^:匹配字符串的开头。
  • $:匹配字符串的结尾。

Go 类型断言

基本语法

类型断言的基本语法如下:

value, ok := interfaceValue.(Type)
  • interfaceValue 是一个接口类型的变量。
  • Type 是你想要断言的类型。
  • value 是断言成功后的具体类型的值。
  • ok 是一个布尔值,表示断言是否成功。

如果断言成功,value 将是 interfaceValue 的实际值,oktrue;如果断言失败,value 将是 Type 的零值,okfalse

类型断言的常见用途

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

}

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇