# go **Repository Path**: swordying/go ## Basic Information - **Project Name**: go - **Description**: Go 语言入门 - **Primary Language**: Go - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-14 - **Last Updated**: 2026-09-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Go 语言学习笔记 ## 1、简介 1. 定义:一种静态强类型、编译型、并发支持的通用编程语言 2. 开发者:Robert Griesemer、Rob Pike、Ken Thompson(Google) 3. 开发时间:2007 年开始设计,2009年正式发布 4. 设计目的:解决大规模软件开发中的效率与可维护性问题,融合动态语言的开发效率与静态语言的安全性和性能 5. 文件扩展名:`.go` ## 2、语言特性 1. 静态类型:每个变量在编译时都有确定的类型,提供速度与安全性 2. 编译型语言:编译为单个二进制文件,无外部依赖,部署简单 3. 原生并发支持:通过 goroutine 和 channel 实现并发编程 4. 垃圾回收:自动内存管理 5. 简洁语法:关键字少,学习曲线平缓 6. 跨平台编译:支持交叉编译,可在不同操作系统间轻松构建 ## 3、应用场景 - 高并发、低延迟的服务 1. 云原生开发:Docker、Kubernetes、Istio 等核心项目均使用 Go 构建 2. 分布式网络服务:API 网关、微服务框架、Web 服务器 3. 命令行工具:Terraform、Hugo 等,编译后启动快、分发方便 4. 网络编程:Etcd、NATS、Traefik 等 5. 区块链:Hyperledger Fabric 等 ## 4、Go 注释 1. 单行注释:`// 这是单行注释` 2. 多行注释: ```go /* 这是多行注释 可以跨越多行 */ ``` 注释规则:`//` 适合短注释,`/* */` 适合长注释 ## 5、Go 文件构造 Go 程序从 `main` 包开始执行: ```go package main // import 别名 "包名" import "fmt" func main() { fmt.Println("Hello World! My name is Go.") } ``` - `main()` 函数:程序入口,每个可执行程序必须有一个,每个模块也只须有一个 - `init()` 函数:每个包可以有多个 `init()` 函数,在 `main()` 之前自动按文件名称字典顺序调用 ## 6、Go 变量 ### 1. 变量声明方式 Go 提供三种变量声明方式: ```go var x int = 42 // 完整形式:显式类型 + 值 var x = 42 // 类型推断:从值推断类型 x := 42 // 短形式:仅在函数内使用(最常用) ``` #### 变量插值 ```go // 注意变量的类型 s string = fmt.Sprintf("变量%v", "通用插值") ``` ### 2. 零值概念 Go 中每个声明的变量都会被初始化,不存在"未初始化"的垃圾值: | 类型 | 零值 | |------|------| | `int`, `int8`..`int64`, `uint` 等 | `0` | | `float32`, `float64` | `0` | | `bool` | `false` | | `string` | `""`(空字符串) | | pointer, slice, map, channel, function, interface | `nil` | | struct | 每个字段为其零值的结构体 | ### 3. 常量与 iota ```go const Pi = 3.14159 const Greeting = "hello" // iota 用于枚举 const ( Sunday = iota // 0 Monday // 1 Tuesday // 2 ) ``` ## 7、Go 数据类型 ### 1. 数值类型 - 整型:`int`、`uint`(平台相关,32或64位)、`int8`~`int64`、`uint8`~`uint64` - 别名:`int32` = `rune`(Unicode 码点),`uint8` = `byte`(原始字节) - 浮点型:`float32`、`float64`(推荐使用 `float64`) - 复数:`complex64`、`complex128`(较少使用) ### 2. 字符串类型 - Go 字符串是不可变的字节序列: ```go // 解释型字符串(双引号) s1 := "Hello\nWorld" // 原始字符串(反引号,不解析转义) s2 := `Hello World` // 字符串是 UTF-8 编码 // 索引字符串得到的是 byte,不是字符 ``` ### 3. 布尔类型 ```go var b bool = true // 或 false // Go 不支持隐式 int 到 bool 的转换,if 1 {} 无法编译 ``` ### 4. 数组 Array - 数组是固定长度的同类型元素序列: ```go // 声明数组 var a [3]int // [0, 0, 0] var q [3]int = [3]int{1, 2, 3} // [1, 2, 3] r := [...]int{1, 2, 3} // 长度由初始化值决定 // 数组长度是类型的一部分 // [3]int 和 [4]int 是不同的类型 // 数组是值传递,函数接收的是副本 ``` ### 5. 切片 Slice - 切片是动态长度的数组视图,是 Go 中最常用的数据结构: ```go // 创建切片 s1 := []int{1, 2, 3} // 字面量 s2 := make([]int, 5) // 长度5,容量5 s3 := make([]int, 3, 10) // 长度3,容量10 // 切片操作 s4 := s1[1:3] // [2, 3] s1 = append(s1, 4) // 追加元素 // 切片是引用类型 ``` ### 6. 映射 Map - Map 是键值对集合: ```go // 创建 map m1 := map[string]int{"a": 1, "b": 2} m2 := make(map[string]int) // 操作 m2["key"] = 100 value, ok := m2["key"] // 检查键是否存在 delete(m2, "key") // 删除键 // 遍历 for key, value := range m1 { fmt.Printf("%s: %d\n", key, value) } ``` ### 7. 结构体 Struct - 结构体是字段的集合: ```go type Person struct { Name string Age int } // 创建实例 p1 := Person{Name: "Alice", Age: 30} p2 := Person{"Bob", 25} // 访问字段 fmt.Println(p1.Name) ``` ### 8. 指针 Pointer ```go x := 42 p := &x // p 是 *int 类型,指向 x fmt.Println(*p) // 42,解引用 *p = 100 // 通过指针修改值 ``` ### 9. 接口 Interface - 接口定义方法集合,类型隐式实现接口: ```go // 定义接口 type PaymentProcessor interface { ProcessPayment(amount float64) } // 定义实现结构体 type CreditCardProcessor struct{} // 结构体实现接口的方法 func (c CreditCardProcessor) ProcessPayment(amount float64) { fmt.Printf("处理信用卡支付: $%.2f\n", amount) } // 使用 var processor PaymentProcessor = CreditCardProcessor{} processor.ProcessPayment(100.0) ``` ## 8、Go 函数与方法 ### 1. 函数 ```go // 基本函数 func add(a int, b int) int { return a + b } // 多返回值 func divmod(a, b int) (int, int) { return a / b, a % b } // 命名返回值 func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return // 自动返回 x, y } ``` ### 2. 方法与接收者 - 方法是与特定类型关联的函数: ```go // 定义一个新类型 type MyInt int // 定义了一个方法 add,绑定到 MyInt 类型上,接收者是值接收者 func (mi MyInt) add(other MyInt) MyInt { return mi + other } // 指针接收者(可修改接收者) func (mi *MyInt) double() { *mi *= 2 } // 使用,声明自定义类型变量 var a MyInt = 1 result := a.add(2) // 3 a.double() // a 变为 2 ``` 接收者选择原则: - 需要修改接收者时使用指针接收者 - 接收者为大型结构体时使用指针接收者以避免复制 - 同一类型的方法应统一使用值接收者或指针接收者 ### 3. 方法到函数的转换 - 方法在底层会被转换为函数,接收者作为第一个参数: ```go // 方法值 f := a.add fmt.Println(f(2)) // 3 ``` ## 9、流程控制 ### 1. 判断语句 Go 中 if 不需要括号: ```go if a == 1 { fmt.Println("a is 1") } else if a == 2 { fmt.Println("a is 2") } else { fmt.Println("a is other") } // if 可以包含初始化语句 if err := doSomething(); err != nil { // 处理错误 } ``` ### 2. 分支语句 switch ```go switch a { case 1: fmt.Println("a is 1") case 2, 3, 4: fmt.Println("a is 2, 3 or 4") default: fmt.Println("a is other") } // fallthrough 穿透到下一个 case switch b { case 10: fmt.Println("10") fallthrough case 20: fmt.Println("20") // 也会执行 } ``` - 注意:Go 的 switch 默认自带 break,匹配成功后不会执行其他 case ### 3. 循环语句 for Go 只有 for 循环,没有 while: ```go // 标准形式 for i := 0; i < 5; i++ { fmt.Println(i) } // 类似 while i := 0 for i < 5 { fmt.Println(i) i++ } // 无限循环 for { // break 退出 } // range 遍历 for index, value := range slice { fmt.Printf("%d: %v\n", index, value) } // 只要 value for _, value := range slice { fmt.Println(value) } // 只要 index for index := range slice { fmt.Println(index) } ``` ### 4. 中断语句 - `break`:中断当前循环 - `continue`:跳过本次循环,执行下一次 - `goto`:跳转到标签(不推荐使用) ## 10、Go 包与模块 ### 1. 包 Package - 导出可以理解为封装性 ```go // 声明包 package mypackage // 导出标识符(大写字母开头) func ExportedFunction() {} // 未导出(小写字母开头) func unexportedFunction() {} ``` 命名规范: - 包名尽量小写,不使用下划线或驼峰 - 导出的变量/函数需注释(大写开头) - 包级注释放在 `package` 前 ### 2. 模块 Module - Go Modules 是官方依赖管理方案: ```bash # 初始化模块 go mod init github.com/you/mytool # 添加依赖 go get github.com/google/uuid@v1.6.0 # 整理依赖 go mod tidy ``` go.mod 文件结构: ```go module github.com/you/mytool go 1.22 require ( github.com/google/uuid v1.6.0 ) ``` 重要文件: - `go.mod`:声明模块路径、Go 版本、依赖列表 - `go.sum`:记录依赖的加密校验和,必须提交到版本控制 ### 3. internal 包 - `internal` 目录下的包只能被同一模块内的代码导入: ``` mymodule/ go.mod internal/ auth/ token.go cmd/ server/ main.go // 可以导入 mymodule/internal/auth ``` ## 11、错误处理 - Go 使用显式错误返回值: ```go // 返回错误的函数 func divide(a, b float64) (float64, error) { if b == 0 { return 0, errors.New("division by zero") } return a / b, nil } // 调用时检查错误 result, err := divide(10, 0) if err != nil { fmt.Println("Error:", err) return } fmt.Println(result) ``` 错误处理规范: - 错误信息格式:`<动作 + 操作对象 + 错误提示>` - 尽量不使用 `panic`,除非是未知错误或系统关键组件初始化失败 - 可以使用自定义错误类型进行统一处理 ## 12、并发编程 ### 1. Goroutine - 轻量级线程,使用 `go` 关键字启动: ```go func say(s string) { for i := 0; i < 5; i++ { fmt.Println(s) time.Sleep(100 * time.Millisecond) } } func main() { go say("world") // 在新 goroutine 中执行 say("hello") // 在主 goroutine 中执行 } ``` ### 2. Channel - 不要通过共享内存来通信,而要通过通信来共享内存 - 用于 goroutine 之间通信: ```go // 创建 channel ch := make(chan int) // 发送数据 go func() { ch <- 42 }() // 接收数据 value := <-ch fmt.Println(value) // 带缓冲的 channel ch2 := make(chan int, 10) ch2 <- 1 // 不会阻塞 ``` - nil channel 收发都永久阻塞(可用于动态禁用 select 分支) - 已关闭 channel 发就 panic,收就拿到零值 ### 使用策略 - 需要同步会合、信号通知、严格背压 → 无缓冲。 - 需要解耦、削峰、吸收速度差、批量处理 → 有缓冲。 - 缓冲大小根据速度差和可容忍延迟估算,不是越大越好。 ## 13、Go 程序睡眠 ```php time.Sleep(500 * time.Millisecond) // 毫秒 time.Sleep(2 * time.Second) // 秒 time.Sleep(1 * time.Minute) // 分钟 time.Sleep(100 * time.Microsecond) // 微秒 time.Sleep(1 * time.Hour) // 小时 ``` ## 附录:其他 1. Go 中分号由编译器自动插入,通常不需要手写 2. Go 没有三元运算符,需使用 if-else 3. Go 中变量未定义与变量等于零值是不同概念 4. Go 采用组合优于继承的设计哲学 5. 接口应该保持小巧,专注于特定职责 6. 推荐使用 `gofmt` 格式化代码,保持风格统一