Go语言实战教程
从零开始掌握Go语言,从基础语法到并发编程,从Web开发到爬虫实战,全面覆盖Go语言核心知识体系。
目录
- Go语言简介与历史
- 环境搭建与配置
- 基本语法与数据类型
- 变量与常量
- 控制流语句
- 函数与方法
- 数组、切片与映射
- 结构体与接口
- 并发编程:goroutine与channel
- 包管理与模块
- 错误处理
- 文件I/O
- HTTP服务开发
- 数据库操作
- JSON处理
- 测试与基准测试
- 依赖管理
- 常用标准库
- 实战:RESTful API服务
- 实战:并发爬虫
- 部署与优化
1. Go语言简介与历史
1.1 Go语言的诞生背景
Go语言(又称Golang)是Google公司于2009年正式发布的一款开源编程语言。它的诞生源于Google内部对大规模软件工程中遇到的痛点:C++的编译速度过慢、Java的冗长繁琐、Python的性能不足。三位核心创始人——Robert Griesemer、Rob Pike和Ken Thompson——希望创造一门兼具高效编译、高性能运行和简洁语法的现代编程语言。
Ken Thompson是Unix操作系统的联合创始人,Rob Pike是UTF-8编码的共同发明人,Robert Griesemer参与了V8 JavaScript引擎和Java HotSpot编译器的开发。这样的创始团队赋予了Go语言深厚的系统编程基因。
1.2 Go语言的设计哲学
Go语言的设计遵循以下核心原则:
- 简洁性(Simplicity):Go语言的语法非常精简,关键字仅有25个。没有类继承、没有异常机制(使用错误返回值)、没有泛型(Go 1.18开始支持有限泛型),这些设计选择都是为了降低语言的复杂度。
- 并发原生(Concurrency):Go语言从语言层面原生支持并发编程,goroutine和channel是其核心并发原语,使得编写高并发程序变得简单直观。
- 快速编译(Fast Compilation):Go语言的编译器经过精心设计,即使是大型项目也能在秒级完成编译,极大地提升了开发效率。
- 静态类型与垃圾回收(Static Typing with GC):Go语言是静态类型语言,但拥有自动垃圾回收机制,开发者无需手动管理内存。
- 跨平台编译(Cross-Platform):Go语言内置交叉编译支持,可以轻松编译出适用于不同操作系统和架构的可执行文件。
1.3 Go语言的版本演进
| 版本 | 发布时间 | 重要特性 |
|---|---|---|
| Go 1.0 | 2012年3月 | 第一个稳定版本,确定了Go 1兼容性承诺 |
| Go 1.5 | 2015年8月 | 编译器完全用Go重写,移除了C代码 |
| Go 1.11 | 2018年8月 | 引入Go Modules依赖管理系统 |
| Go 1.18 | 2022年3月 | 引入泛型(Generics)、模糊测试 |
| Go 1.21 | 2023年8月 | 新增内置函数min/max/clear,slog结构化日志 |
| Go 1.22 | 2024年2月 | for循环变量语义改进,增强路由模式 |
1.4 Go语言的应用场景
Go语言在以下领域有着广泛的应用:
- 云原生基础设施:Docker、Kubernetes、etcd、Prometheus等核心云原生项目均使用Go语言开发。
- 微服务架构:Go语言的高性能和低内存占用使其成为微服务开发的理想选择。
- 网络编程:Go标准库提供了强大的HTTP/TCP/UDP支持,非常适合开发网络服务。
- 命令行工具:Go编译后生成单一可执行文件,无需运行时依赖,非常适合开发CLI工具。
- 区块链:以太坊的Go实现(go-ethereum)是最主流的以太坊客户端之一。
2. 环境搭建与配置
2.1 安装Go语言
Linux/macOS安装
从官方下载页面获取最新版本的安装包:
# Linux - 下载并解压
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
# 添加到PATH环境变量(写入 ~/.bashrc 或 ~/.zshrc)
export PATH=$PATH:/usr/local/go/bin
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
# 使配置生效
source ~/.bashrc
Windows安装
从 https://go.dev/dl/ 下载Windows安装程序(.msi文件),双击运行安装向导即可。安装程序会自动配置环境变量。
验证安装
go version
# 输出: go version go1.22.0 linux/amd64
go env
# 查看所有Go环境变量
2.2 配置Go工作空间
Go语言从1.11版本开始推荐使用Go Modules管理项目,不再强制要求GOPATH模式。但了解GOPATH仍然重要:
# 查看关键环境变量
go env GOPATH # 默认: ~/go
go env GOROOT # Go安装目录
go env GOPROXY # 模块代理地址
# 推荐配置国内镜像代理(中国大陆用户)
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GONOSUMCHECK=*
2.3 第一个Go程序
创建项目目录并初始化模块:
mkdir hello-go && cd hello-go
go mod init hello-go
创建 main.go 文件:
package main
import "fmt"
func main() {
fmt.Println("你好,Go语言!")
fmt.Printf("Go版本: %s\n", "1.22")
}
运行与编译:
# 直接运行
go run main.go
# 编译为可执行文件
go build -o hello main.go
./hello
2.4 开发工具推荐
- GoLand:JetBrains出品的专业Go IDE,功能最全面,但需要付费。
- VS Code + Go扩展:免费且功能强大,配合
gopls语言服务器使用体验极佳。 - Vim/Neovim + vim-go:适合终端用户,vim-go插件提供了完善的Go开发支持。
3. 基本语法与数据类型
3.1 程序结构
每个Go程序都由包(package)组成,main包是程序的入口:
// 声明包名
package main
// 导入标准库包
import (
"fmt"
"time"
)
// main函数是程序入口
func main() {
// 单行注释
/*
多行注释
*/
fmt.Println("当前时间:", time.Now())
}
Go语言的几个重要语法规则:
- 语句末尾不需要分号(编译器自动添加)
- 左花括号
{必须与语句在同一行,不能另起一行 - 导入的包如果未使用,编译会报错
- 未使用的变量也会导致编译错误
3.2 基本数据类型
Go语言提供了丰富的基本数据类型:
package main
import (
"fmt"
"unsafe"
)
func main() {
// ===== 整数类型 =====
var a int = 42 // 平台相关(32位或64位)
var b int8 = 127 // -128 到 127
var c int16 = 32767 // -32768 到 32767
var d int32 = 2147483647 // 约±21亿
var e int64 = 9223372036854775807 // 约±9.2×10^18
fmt.Printf("int: %d, 大小: %d字节\n", a, unsafe.Sizeof(a))
fmt.Printf("int8: %d, 大小: %d字节\n", b, unsafe.Sizeof(b))
fmt.Printf("int16: %d, 大小: %d字节\n", c, unsafe.Sizeof(c))
fmt.Printf("int32: %d, 大小: %d字节\n", d, unsafe.Sizeof(d))
fmt.Printf("int64: %d, 大小: %d字节\n", e, unsafe.Sizeof(e))
// 无符号整数
var ua uint = 42
var ub uint8 = 255 // 0 到 255
var uc uint16 = 65535 // 0 到 65535
var ud uint32 = 4294967295
var ue uint64 = 18446744073709551615
_ = ua; _ = ub; _ = uc; _ = ud; _ = ue
// 特殊整数类型
var by byte = 255 // 等同于 uint8
var r rune = '中' // 等同于 int32,表示Unicode码点
_ = by
fmt.Printf("rune '中' 的值: %d, 字符: %c\n", r, r)
// ===== 浮点数类型 =====
var f32 float32 = 3.14159
var f64 float64 = 3.141592653589793
fmt.Printf("float32: %.5f\n", f32)
fmt.Printf("float64: %.15f\n", f64)
// ===== 复数类型 =====
var c64 complex64 = complex(1, 2) // 1+2i
var c128 complex128 = complex(3.14, 2.7) // 3.14+2.7i
fmt.Printf("complex64: %v\n", c64)
fmt.Printf("complex128: %v\n", c128)
// ===== 布尔类型 =====
var flag bool = true
var ok bool = false
fmt.Printf("flag: %v, ok: %v\n", flag, ok)
// ===== 字符串类型 =====
var s string = "Hello, 世界!"
fmt.Printf("字符串: %s, 长度: %d字节, 字符数: %d\n",
s, len(s), len([]rune(s)))
// 字符串是不可变的
// s[0] = 'h' // 编译错误:不能修改字符串
// 原始字符串(反引号)
var raw string = `这是
一段多行
原始字符串`
fmt.Println(raw)
}
3.3 类型转换
Go语言不支持隐式类型转换,所有类型转换必须显式进行:
package main
import (
"fmt"
"strconv"
)
func main() {
// 数值类型之间的转换
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
fmt.Printf("int: %d -> float64: %.2f -> uint: %d\n", i, f, u)
// 整数与字符串之间的转换
s := strconv.Itoa(42) // int -> string
n, err := strconv.Atoi("123") // string -> int
if err == nil {
fmt.Printf("string: %s -> int: %d\n", s, n)
}
// 更多转换函数
f2, _ := strconv.ParseFloat("3.14", 64)
b, _ := strconv.ParseBool("true")
fmt.Printf("float64: %.2f, bool: %v\n", f2, b)
// 字符串与字节切片之间的转换(零拷贝优化)
bs := []byte("Hello, Go!")
str := string(bs)
fmt.Printf("bytes: %v, string: %s\n", bs, str)
}
4. 变量与常量
4.1 变量声明
Go语言提供了多种变量声明方式:
package main
import "fmt"
// 包级别变量
var globalVar = "我是全局变量"
var (
name string = "Go"
version int = 22
pi float64 = 3.14159
)
func main() {
// 方式一:var声明(指定类型)
var age int = 30
fmt.Println("age:", age)
// 方式二:var声明(类型推断)
var height = 175.5 // 自动推断为float64
fmt.Println("height:", height)
// 方式三:短变量声明(只能在函数内部使用)
name := "张三"
score := 95.5
active := true
fmt.Printf("name: %s, score: %.1f, active: %v\n", name, score, active)
// 方式四:批量声明
var (
x int = 1
y string = "hello"
z bool = true
)
fmt.Println(x, y, z)
// 多变量同时赋值
a, b := 1, 2
a, b = b, a // 交换值
fmt.Printf("a: %d, b: %d\n", a, b)
// 零值:未初始化的变量会被赋予默认零值
var (
zeroInt int
zeroFloat float64
zeroBool bool
zeroString string
zeroPtr *int
zeroSlice []int
zeroMap map[string]int
)
fmt.Printf("int零值: %d\n", zeroInt) // 0
fmt.Printf("float零值: %f\n", zeroFloat) // 0.000000
fmt.Printf("bool零值: %v\n", zeroBool) // false
fmt.Printf("string零值: %q\n", zeroString) // ""
fmt.Printf("指针零值: %v\n", zeroPtr) // <nil>
fmt.Printf("切片零值: %v, len: %d, nil: %v\n",
zeroSlice, len(zeroSlice), zeroSlice == nil) // [] 0 true
fmt.Printf("映射零值: %v, nil: %v\n",
zeroMap, zeroMap == nil) // map[] true
// 空白标识符 _
_, second := 10, 20 // 忽略第一个值
fmt.Println("second:", second)
}
4.2 常量
package main
import "fmt"
// 单个常量
const Pi = 3.14159265358979
const MaxRetries = 3
// 批量常量
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
// iota枚举器
type Weekday int
const (
Sunday Weekday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
// iota的高级用法
const (
_ = iota // 忽略0
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30
TB // 1 << 40
)
// iota参与运算
type Permission uint8
const (
Read Permission = 1 << iota // 1 (001)
Write // 2 (010)
Execute // 4 (100)
)
// 无类型常量
const (
UntypedInt = 42 // 无类型整数常量
UntypedFloat = 3.14 // 无类型浮点常量
UntypedString = "hello" // 无类型字符串常量
UntypedBool = true // 无类型布尔常量
)
func main() {
fmt.Printf("Pi: %f\n", Pi)
fmt.Printf("Sunday: %d, Saturday: %d\n", Sunday, Saturday)
fmt.Printf("KB: %d, MB: %d, GB: %d, TB: %d\n", KB, MB, GB, TB)
// 权限组合(位运算)
var perm Permission = Read | Write // 011 = 3
fmt.Printf("权限: %d, 可读: %v, 可写: %v, 可执行: %v\n",
perm, perm&Read != 0, perm&Write != 0, perm&Execute != 0)
// 无类型常量可以隐式转换为兼容类型
var x int = UntypedInt // int
var y float64 = UntypedFloat // float64
var z complex128 = UntypedInt // complex128
fmt.Printf("x: %d, y: %f, z: %v\n", x, y, z)
}
5. 控制流语句
5.1 条件语句
package main
import (
"fmt"
"math/rand"
)
func main() {
// 基本if语句
score := 85
if score >= 90 {
fmt.Println("优秀")
} else if score >= 80 {
fmt.Println("良好")
} else if score >= 60 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}
// if带初始化语句
if num := rand.Intn(100); num > 50 {
fmt.Printf("随机数 %d 大于50\n", num)
} else {
fmt.Printf("随机数 %d 小于等于50\n", num)
}
// switch语句
day := "Wednesday"
switch day {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("工作日")
case "Saturday", "Sunday":
fmt.Println("周末")
default:
fmt.Println("未知")
}
// 无条件switch(替代长if-else链)
temperature := 35
switch {
case temperature > 35:
fmt.Println("酷热")
case temperature > 25:
fmt.Println("温暖")
case temperature > 10:
fmt.Println("凉爽")
default:
fmt.Println("寒冷")
}
// 类型switch
var i interface{} = "hello"
switch v := i.(type) {
case int:
fmt.Printf("整数: %d\n", v)
case string:
fmt.Printf("字符串: %s\n", v)
case bool:
fmt.Printf("布尔: %v\n", v)
default:
fmt.Printf("未知类型: %T\n", v)
}
}
5.2 循环语句
Go语言只有for一种循环语句,但它可以模拟所有循环模式:
package main
import "fmt"
func main() {
// 标准for循环
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}
fmt.Println("1到100的和:", sum)
// while风格
n := 1
for n < 1000 {
n *= 2
}
fmt.Println("最小的>=1000的2的幂:", n)
// 无限循环
count := 0
for {
count++
if count > 5 {
break
}
if count%2 == 0 {
continue // 跳过偶数
}
fmt.Println("奇数:", count)
}
// range遍历
// 遍历字符串(按rune)
for i, ch := range "Hello, 世界" {
fmt.Printf("索引: %d, 字符: %c, 码点: %d\n", i, ch, ch)
}
// 遍历切片
fruits := []string{"苹果", "香蕉", "橙子", "葡萄"}
for index, value := range fruits {
fmt.Printf("索引: %d, 值: %s\n", index, value)
}
// 只需要索引
for i := range fruits {
fmt.Println("索引:", i)
}
// 只需要值
for _, fruit := range fruits {
fmt.Println("水果:", fruit)
}
// 遍历映射
scores := map[string]int{
"数学": 95,
"英语": 88,
"物理": 92,
}
for subject, score := range scores {
fmt.Printf("%s: %d分\n", subject, score)
}
// goto语句(谨慎使用)
x := 0
Retry:
x++
if x < 3 {
fmt.Printf("重试第%d次\n", x)
goto Retry
}
fmt.Println("完成")
}
6. 函数与方法
6.1 函数基础
package main
import (
"errors"
"fmt"
)
// 基本函数
func add(a, b int) int {
return a + b
}
// 多参数类型相同可以简写
func multiply(a, b int) int {
return a * b
}
// 多返回值
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
// 命名返回值
func rectangle(length, width float64) (area, perimeter float64) {
area = length * width
perimeter = 2 * (length + width)
return // 裸返回,自动返回命名的返回值
}
// 可变参数函数
func sum(numbers ...int) int {
total := 0
for _, n := range numbers {
total += n
}
return total
}
// 函数作为参数(高阶函数)
func apply(fn func(int, int) int, a, b int) int {
return fn(a, b)
}
// 函数作为返回值
func makeMultiplier(factor int) func(int) int {
return func(x int) int {
return x * factor
}
}
// 闭包
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
// 递归
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
// defer语句
func readFile(filename string) {
fmt.Printf("打开文件: %s\n", filename)
defer fmt.Printf("关闭文件: %s\n", filename) // 最后执行
defer fmt.Println("清理资源...") // 后进先出(LIFO)
fmt.Printf("读取文件: %s\n", filename)
}
// panic与recover
func safeDivide(a, b int) (result int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("捕获到异常: %v", r)
}
}()
if b == 0 {
panic("除以零")
}
return a / b, nil
}
func main() {
fmt.Println("加法:", add(3, 5))
fmt.Println("乘法:", multiply(4, 6))
// 多返回值处理
if result, err := divide(10, 3); err != nil {
fmt.Println("错误:", err)
} else {
fmt.Printf("10 / 3 = %.4f\n", result)
}
// 命名返回值
area, perimeter := rectangle(5, 3)
fmt.Printf("面积: %.2f, 周长: %.2f\n", area, perimeter)
// 可变参数
fmt.Println("求和:", sum(1, 2, 3, 4, 5))
// 切片展开
nums := []int{10, 20, 30}
fmt.Println("切片求和:", sum(nums...))
// 高阶函数
fmt.Println("apply add:", apply(add, 3, 5))
// 闭包
double := makeMultiplier(2)
triple := makeMultiplier(3)
fmt.Println("double 5:", double(5))
fmt.Println("triple 5:", triple(5))
// 计数器闭包
next := counter()
fmt.Println(next()) // 1
fmt.Println(next()) // 2
fmt.Println(next()) // 3
// 递归
fmt.Println("fibonacci(10):", fibonacci(10))
// defer
readFile("test.txt")
// panic/recover
r, err := safeDivide(10, 0)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("结果:", r)
}
}
6.2 方法
package main
import (
"fmt"
"math"
)
// 定义结构体
type Point struct {
X, Y float64
}
// 值接收者方法
func (p Point) Distance(q Point) float64 {
return math.Sqrt(math.Pow(p.X-q.X, 2) + math.Pow(p.Y-q.Y, 2))
}
// 指针接收者方法(可以修改结构体字段)
func (p *Point) Translate(dx, dy float64) {
p.X += dx
p.Y += dy
}
// 指针接收者方法
func (p *Point) Scale(factor float64) {
p.X *= factor
p.Y *= factor
}
func (p Point) String() string {
return fmt.Sprintf("(%.2f, %.2f)", p.X, p.Y)
}
// 自定义类型的方法
type Celsius float64
type Fahrenheit float64
func (c Celsius) ToFahrenheit() Fahrenheit {
return Fahrenheit(c*9/5 + 32)
}
func (f Fahrenheit) ToCelsius() Celsius {
return Celsius((f - 32) * 5 / 9)
}
func main() {
p1 := Point{3, 4}
p2 := Point{0, 0}
fmt.Printf("p1到p2的距离: %.2f\n", p1.Distance(p2))
p1.Translate(1, 1)
fmt.Println("平移后的p1:", p1)
p1.Scale(2)
fmt.Println("缩放后的p1:", p1)
// 温度转换
c := Celsius(100)
f := c.ToFahrenheit()
fmt.Printf("%.1f°C = %.1f°F\n", c, f)
f2 := Fahrenheit(212)
c2 := f2.ToCelsius()
fmt.Printf("%.1f°F = %.1f°C\n", f2, c2)
}
7. 数组、切片与映射
7.1 数组
package main
import "fmt"
func main() {
// 数组声明
var arr1 [5]int // 零值数组
arr2 := [5]int{1, 2, 3, 4, 5} // 字面量初始化
arr3 := [...]int{10, 20, 30} // 编译器自动计算长度
arr4 := [3]string{"Go", "Rust", "Python"}
fmt.Println("arr1:", arr1)
fmt.Println("arr2:", arr2)
fmt.Println("arr3:", arr3)
fmt.Println("arr4:", arr4)
// 数组访问与修改
arr2[0] = 100
fmt.Println("修改后:", arr2)
fmt.Println("长度:", len(arr2))
// 数组是值类型
a := [3]int{1, 2, 3}
b := a // 复制整个数组
b[0] = 100
fmt.Println("a:", a) // [1 2 3] 不受影响
fmt.Println("b:", b) // [100 2 3]
// 二维数组
matrix := [3][3]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
for _, row := range matrix {
fmt.Println(row)
}
}
7.2 切片
切片是Go语言中最重要的数据结构之一,它是一个动态大小的序列:
package main
import "fmt"
func main() {
// 创建切片
s1 := []int{1, 2, 3, 4, 5} // 字面量
s2 := make([]int, 5) // 长度5,容量5
s3 := make([]int, 3, 10) // 长度3,容量10
var s4 []int // nil切片
fmt.Println("s1:", s1, "len:", len(s1), "cap:", cap(s1))
fmt.Println("s2:", s2, "len:", len(s2), "cap:", cap(s2))
fmt.Println("s3:", s3, "len:", len(s3), "cap:", cap(s3))
fmt.Println("s4:", s4, "nil:", s4 == nil)
// 切片操作
fmt.Println("s1[1:3]:", s1[1:3]) // [2 3]
fmt.Println("s1[:3]:", s1[:3]) // [1 2 3]
fmt.Println("s1[2:]:", s1[2:]) // [3 4 5]
fmt.Println("s1[:]:", s1[:]) // [1 2 3 4 5]
// append追加元素
s1 = append(s1, 6)
fmt.Println("追加后:", s1)
// append追加多个元素
s1 = append(s1, 7, 8, 9)
fmt.Println("再追加:", s1)
// append合并切片
s5 := []int{10, 20, 30}
s1 = append(s1, s5...)
fmt.Println("合并后:", s1)
// copy复制切片
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 3)
n := copy(dst, src) // 复制前3个元素
fmt.Printf("复制了%d个元素: %v\n", n, dst)
// 删除切片元素(保持顺序)
s := []int{1, 2, 3, 4, 5}
index := 2
s = append(s[:index], s[index+1:]...) // 删除索引2的元素
fmt.Println("删除后:", s) // [1 2 4 5]
// 删除切片元素(不保持顺序,更快)
s2 = []int{1, 2, 3, 4, 5}
s2[index] = s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println("快速删除:", s2) // [1 2 5 4]
// 切片的内部结构:指针、长度、容量
original := []int{1, 2, 3, 4, 5}
sub := original[1:3]
fmt.Printf("sub: %v, len: %d, cap: %d\n", sub, len(sub), cap(sub))
// sub共享底层数组
sub[0] = 200
fmt.Println("修改sub后original:", original) // [1 200 3 4 5]
// 使用copy创建独立副本
independent := make([]int, len(original))
copy(independent, original)
independent[0] = 999
fmt.Println("independent:", independent)
fmt.Println("original不受影响:", original)
// 预分配容量避免频繁扩容
result := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {
result = append(result, i*i)
}
fmt.Println("前10个:", result[:10])
}
7.3 映射(Map)
package main
import (
"fmt"
"sort"
)
func main() {
// 创建映射
m1 := map[string]int{
"数学": 95,
"英语": 88,
"物理": 92,
}
m2 := make(map[string]int) // 空映射
var m3 map[string]int // nil映射
fmt.Println("m1:", m1)
fmt.Println("m2:", m2)
fmt.Println("m3:", m3, "nil:", m3 == nil)
// 基本操作
m2["Go"] = 100 // 添加/修改
m2["Rust"] = 98
m2["Python"] = 90
fmt.Println("m2:", m2)
// 读取
score := m2["Go"]
fmt.Println("Go成绩:", score)
// 检查键是否存在
if score, ok := m2["Java"]; ok {
fmt.Println("Java成绩:", score)
} else {
fmt.Println("Java不在映射中")
}
// 删除
delete(m2, "Python")
fmt.Println("删除Python后:", m2)
// 遍历映射(顺序不确定)
for subject, score := range m1 {
fmt.Printf("%s: %d\n", subject, score)
}
// 有序遍历映射
keys := make([]string, 0, len(m1))
for k := range m1 {
keys = append(keys, k)
}
sort.Strings(keys)
fmt.Println("\n有序遍历:")
for _, k := range keys {
fmt.Printf("%s: %d\n", k, m1[k])
}
// 映射作为集合
set := make(map[string]bool)
fruits := []string{"苹果", "香蕉", "苹果", "橙子", "香蕉", "葡萄"}
for _, f := range fruits {
set[f] = true
}
fmt.Println("去重后的水果:")
for f := range set {
fmt.Println(" ", f)
}
// 映射的值是切片
classScores := make(map[string][]int)
classScores["一班"] = append(classScores["一班"], 90, 85, 92)
classScores["二班"] = append(classScores["二班"], 88, 76, 95)
fmt.Println("班级成绩:", classScores)
// 嵌套映射
students := map[string]map[string]int{
"张三": {"数学": 95, "英语": 88},
"李四": {"数学": 82, "英语": 90},
}
for name, scores := range students {
fmt.Printf("%s的成绩: %v\n", name, scores)
}
}
8. 结构体与接口
8.1 结构体
package main
import (
"fmt"
"time"
)
// 定义结构体
type User struct {
ID int
Name string
Email string
Age int
CreatedAt time.Time
}
// 结构体方法
func (u User) String() string {
return fmt.Sprintf("User{id=%d, name=%s, email=%s, age=%d}",
u.ID, u.Name, u.Email, u.Age)
}
// 指针接收者方法
func (u *User) UpdateEmail(email string) {
u.Email = email
}
// 构造函数(Go语言惯例:NewXxx)
func NewUser(id int, name, email string, age int) *User {
return &User{
ID: id,
Name: name,
Email: email,
Age: age,
CreatedAt: time.Now(),
}
}
// 嵌入结构体(组合)
type Address struct {
City string
Country string
}
type Employee struct {
User // 嵌入User,继承其字段和方法
Company string
Position string
Address Address // 命名嵌入
}
func (e Employee) FullInfo() string {
return fmt.Sprintf("%s - %s @ %s (%s, %s)",
e.Name, e.Position, e.Company, e.Address.City, e.Address.Country)
}
func main() {
// 创建结构体实例
u1 := User{
ID: 1,
Name: "张三",
Email: "zhangsan@example.com",
Age: 25,
}
u2 := NewUser(2, "李四", "lisi@example.com", 30)
fmt.Println(u1)
fmt.Println(u2)
// 访问和修改字段
u1.Age = 26
u1.UpdateEmail("zhangsan_new@example.com")
fmt.Println("更新后:", u1)
// 结构体切片
users := []User{u1, *u2}
for _, u := range users {
fmt.Println(u)
}
// 匿名结构体
point := struct {
X, Y int
}{10, 20}
fmt.Println("匿名结构体:", point)
// 嵌入结构体
emp := Employee{
User: User{
ID: 3,
Name: "王五",
Email: "wangwu@example.com",
Age: 28,
},
Company: "Google",
Position: "软件工程师",
Address: Address{
City: "北京",
Country: "中国",
},
}
// 直接访问嵌入结构体的字段
fmt.Println("姓名:", emp.Name) // 等同于 emp.User.Name
fmt.Println("公司:", emp.Company)
fmt.Println(emp.FullInfo())
}
8.2 接口
package main
import (
"fmt"
"math"
)
// 定义接口
type Shape interface {
Area() float64
Perimeter() float64
}
// 圆形
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
func (c Circle) String() string {
return fmt.Sprintf("圆形(半径=%.2f)", c.Radius)
}
// 矩形
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
func (r Rectangle) String() string {
return fmt.Sprintf("矩形(宽=%.2f, 高=%.2f)", r.Width, r.Height)
}
// 三角形
type Triangle struct {
A, B, C float64 // 三条边
}
func (t Triangle) Area() float64 {
s := (t.A + t.B + t.C) / 2 // 半周长
return math.Sqrt(s * (s - t.A) * (s - t.B) * (s - t.C))
}
func (t Triangle) Perimeter() float64 {
return t.A + t.B + t.C
}
// 多态函数
func PrintShapeInfo(s Shape) {
fmt.Printf("形状: %v\n", s)
fmt.Printf(" 面积: %.4f\n", s.Area())
fmt.Printf(" 周长: %.4f\n", s.Perimeter())
}
// 空接口
func describe(i interface{}) {
fmt.Printf("类型: %T, 值: %v\n", i, i)
}
// 接口组合
type ReadWriter interface {
Reader
Writer
}
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
func main() {
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 4, Height: 6},
Triangle{A: 3, B: 4, C: 5},
}
for _, s := range shapes {
PrintShapeInfo(s)
fmt.Println()
}
// 类型断言
var s Shape = Circle{Radius: 3}
if c, ok := s.(Circle); ok {
fmt.Printf("是圆形,半径: %.2f\n", c.Radius)
}
// 类型switch
for _, shape := range shapes {
switch v := shape.(type) {
case Circle:
fmt.Printf("圆形: 半径=%.2f\n", v.Radius)
case Rectangle:
fmt.Printf("矩形: 宽=%.2f, 高=%.2f\n", v.Width, v.Height)
case Triangle:
fmt.Printf("三角形: 三边=%.2f, %.2f, %.2f\n", v.A, v.B, v.C)
}
}
// 空接口
describe(42)
describe("hello")
describe(true)
describe([]int{1, 2, 3})
// 接口的零值
var nilShape Shape
fmt.Println("nil接口:", nilShape == nil) // true
// 注意:持有nil指针的接口不等于nil
var nilCircle *Circle
var iface Shape = nilCircle
fmt.Println("持有nil指针的接口:", iface == nil) // false!
}
9. 并发编程:goroutine与channel
Go语言的并发模型基于CSP(Communicating Sequential Processes)理论,核心概念是goroutine和channel。
9.1 Goroutine
package main
import (
"fmt"
"sync"
"time"
)
func sayHello(name string) {
for i := 0; i < 3; i++ {
fmt.Printf("[%s] 你好!(%d)\n", name, i+1)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
// 启动goroutine
go sayHello("goroutine-1")
go sayHello("goroutine-2")
// 使用匿名函数启动goroutine
go func() {
fmt.Println("匿名goroutine执行")
}()
// 等待goroutine完成(使用WaitGroup)
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
fmt.Printf("Worker %d 开始工作\n", id)
time.Sleep(time.Duration(id*100) * time.Millisecond)
fmt.Printf("Worker %d 完成工作\n", id)
}(i)
}
wg.Wait()
fmt.Println("所有worker已完成")
}
9.2 Channel
package main
import (
"fmt"
"time"
)
func main() {
// 无缓冲channel(同步通信)
ch := make(chan string)
go func() {
ch <- "Hello from goroutine!"
}()
msg := <-ch
fmt.Println(msg)
// 有缓冲channel(异步通信)
bufferedCh := make(chan int, 3)
bufferedCh <- 1
bufferedCh <- 2
bufferedCh <- 3
// bufferedCh <- 4 // 会阻塞,因为缓冲区已满
fmt.Println(<-bufferedCh) // 1
fmt.Println(<-bufferedCh) // 2
fmt.Println(<-bufferedCh) // 3
// channel方向(只读/只写)
// 只写channel: chan<- int
// 只读channel: <-chan int
// 使用channel实现生产者-消费者模式
producer := func(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
fmt.Printf("生产: %d\n", i)
}
close(ch) // 关闭channel
}
consumer := func(ch <-chan int) {
for v := range ch { // 自动检测channel关闭
fmt.Printf("消费: %d\n", v)
}
}
ch2 := make(chan int, 3)
go producer(ch2)
consumer(ch2)
}
9.3 Select语句
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch1 <- "来自channel 1"
}()
go func() {
time.Sleep(1 * time.Second)
ch2 <- "来自channel 2"
}()
// select等待多个channel
for i := 0; i < 2; i++ {
select {
case msg := <-ch1:
fmt.Println("收到:", msg)
case msg := <-ch2:
fmt.Println("收到:", msg)
}
}
// select超时控制
ch := make(chan string)
go func() {
time.Sleep(3 * time.Second)
ch <- "结果"
}()
select {
case result := <-ch:
fmt.Println("获取结果:", result)
case <-time.After(1 * time.Second):
fmt.Println("超时!")
}
// select非阻塞操作
ch3 := make(chan int, 1)
select {
case v := <-ch3:
fmt.Println("收到:", v)
default:
fmt.Println("没有数据可读")
}
// 使用done channel实现优雅退出
done := make(chan struct{})
go func() {
for {
select {
case <-done:
fmt.Println("收到退出信号")
return
default:
fmt.Println("工作中...")
time.Sleep(500 * time.Millisecond)
}
}
}()
time.Sleep(2 * time.Second)
close(done) // 发送退出信号
time.Sleep(100 * time.Millisecond)
fmt.Println("程序退出")
}
9.4 并发模式
package main
import (
"context"
"fmt"
"sync"
"time"
)
// Fan-out/Fan-in模式
func fanOutFanIn() {
input := make(chan int)
// 多个worker并发处理
numWorkers := 3
var wg sync.WaitGroup
// Fan-out: 一个输入分发给多个worker
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for v := range input {
fmt.Printf("Worker %d 处理: %d -> %d\n", id, v, v*v)
time.Sleep(100 * time.Millisecond)
}
}(i)
}
// 发送数据
for i := 1; i <= 10; i++ {
input <- i
}
close(input)
wg.Wait()
}
// Context控制goroutine生命周期
func contextDemo() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("Context取消:", ctx.Err())
return
default:
fmt.Println("处理中...")
time.Sleep(500 * time.Millisecond)
}
}
}(ctx)
time.Sleep(3 * time.Second)
fmt.Println("主函数结束")
}
func main() {
fmt.Println("=== Fan-out/Fan-in ===")
fanOutFanIn()
fmt.Println("\n=== Context控制 ===")
contextDemo()
}
9.5 sync包常用工具
package main
import (
"fmt"
"sync"
"sync/atomic"
)
// Mutex互斥锁
type SafeCounter struct {
mu sync.Mutex
count int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
// RWMutex读写锁
type SafeMap struct {
mu sync.RWMutex
data map[string]string
}
func (m *SafeMap) Get(key string) (string, bool) {
m.mu.RLock() // 读锁:允许并发读
defer m.mu.RUnlock()
v, ok := m.data[key]
return v, ok
}
func (m *SafeMap) Set(key, value string) {
m.mu.Lock() // 写锁:独占
defer m.mu.Unlock()
m.data[key] = value
}
func main() {
// 互斥锁示例
counter := &SafeCounter{}
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Increment()
}()
}
wg.Wait()
fmt.Println("最终计数:", counter.Value()) // 1000
// 读写锁示例
sm := &SafeMap{data: make(map[string]string)}
sm.Set("name", "Go")
sm.Set("version", "1.22")
if v, ok := sm.Get("name"); ok {
fmt.Println("name:", v)
}
// 原子操作(无锁,更高性能)
var atomicCounter int64
var wg2 sync.WaitGroup
for i := 0; i < 1000; i++ {
wg2.Add(1)
go func() {
defer wg2.Done()
atomic.AddInt64(&atomicCounter, 1)
}()
}
wg2.Wait()
fmt.Println("原子计数:", atomic.LoadInt64(&atomicCounter))
// Once只执行一次
var once sync.Once
init := func() {
fmt.Println("初始化操作(只执行一次)")
}
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
once.Do(init)
}()
}
wg.Wait()
// Pool对象池
pool := &sync.Pool{
New: func() interface{} {
fmt.Println("创建新对象")
return make([]byte, 1024)
},
}
// 获取对象(可能新建)
buf := pool.Get().([]byte)
fmt.Printf("获取缓冲区: len=%d, cap=%d\n", len(buf), cap(buf))
// 归还对象
pool.Put(buf)
// 再次获取(可能复用之前的)
buf2 := pool.Get().([]byte)
fmt.Printf("再次获取: %v\n", &buf == &buf2) // 可能是同一个
}
10. 包管理与模块
10.1 创建和使用包
项目结构:
myproject/
├── go.mod
├── main.go
├── mathutil/
│ ├── basic.go
│ └── advanced.go
└── stringutil/
└── convert.go
// mathutil/basic.go
package mathutil
// Add 加法(导出函数,首字母大写)
func Add(a, b int) int {
return a + b
}
// subtract 减法(未导出函数,首字母小写)
func subtract(a, b int) int {
return a - b
}
// SubtractSubtractionResult 使用未导出函数
func SubtractResult(a, b int) int {
return subtract(a, b)
}
// mathutil/advanced.go
package mathutil
import "math"
// Max 返回两个数中的较大值
func Max(a, b float64) float64 {
return math.Max(a, b)
}
// Min 返回两个数中的较小值
func Min(a, b float64) float64 {
return math.Min(a, b)
}
// stringutil/convert.go
package stringutil
import "strings"
// Reverse 反转字符串
func Reverse(s string) string {
runes := []rune(s)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes)
}
// ToTitleCase 首字母大写
func ToTitleCase(s string) string {
return strings.Title(s)
}
// main.go
package main
import (
"fmt"
"myproject/mathutil"
"myproject/stringutil"
)
func main() {
fmt.Println("加法:", mathutil.Add(3, 5))
fmt.Println("减法:", mathutil.SubtractResult(10, 4))
fmt.Println("最大值:", mathutil.Max(3.14, 2.72))
fmt.Println("反转:", stringutil.Reverse("Hello, Go!"))
}
10.2 Go Modules
# 初始化模块
go mod init github.com/username/myproject
# 添加依赖
go get github.com/gin-gonic/gin@latest
go get github.com/go-sql-driver/mysql@v1.7.0
# 整理依赖(添加缺少的,删除未使用的)
go mod tidy
# 下载依赖到本地缓存
go mod download
# 查看依赖图
go mod graph
# 验证依赖
go mod verify
# 将依赖复制到vendor目录
go mod vendor
go.mod文件示例:
module github.com/username/myproject
go 1.22
require (
github.com/gin-gonic/gin v1.9.1
github.com/go-sql-driver/mysql v1.7.0
)
require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abb8f00 // indirect
// ... 更多间接依赖
)
11. 错误处理
11.1 基本错误处理
package main
import (
"errors"
"fmt"
"os"
"strconv"
)
// 自定义错误
var (
ErrNotFound = errors.New("资源未找到")
ErrUnauthorized = errors.New("未授权访问")
ErrInvalidInput = errors.New("无效输入")
)
// 返回错误的函数
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
// 带上下文信息的错误
func parseConfig(filename string) (map[string]string, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("读取配置文件失败: %w", err)
}
// 解析配置...
_ = data
return map[string]string{"key": "value"}, nil
}
// 错误链(Go 1.13+)
func fetchUser(id int) (string, error) {
if id <= 0 {
return "", fmt.Errorf("用户ID无效: %w", ErrInvalidInput)
}
if id > 1000 {
return "", fmt.Errorf("用户不存在: %w", ErrNotFound)
}
return "用户" + strconv.Itoa(id), nil
}
func main() {
// 基本错误处理
result, err := divide(10, 3)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Printf("10 / 3 = %.4f\n", result)
}
// 错误链检查
_, err = fetchUser(-1)
if errors.Is(err, ErrInvalidInput) {
fmt.Println("输入验证错误:", err)
}
_, err = fetchUser(2000)
if errors.Is(err, ErrNotFound) {
fmt.Println("资源未找到:", err)
}
// errors.As提取具体错误类型
_, err = parseConfig("nonexistent.json")
if err != nil {
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Printf("路径错误: %s\n", pathErr.Path)
} else {
fmt.Println("其他错误:", err)
}
}
}
11.2 自定义错误类型
package main
import (
"fmt"
"time"
)
// 自定义错误结构体
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("验证错误 [%s]: %s", e.Field, e.Message)
}
// 多错误聚合
type MultiError struct {
Errors []error
}
func (me *MultiError) Error() string {
if len(me.Errors) == 0 {
return "无错误"
}
msg := fmt.Sprintf("共%d个错误:\n", len(me.Errors))
for i, err := range me.Errors {
msg += fmt.Sprintf(" %d. %s\n", i+1, err.Error())
}
return msg
}
func (me *MultiError) Add(err error) {
me.Errors = append(me.Errors, err)
}
func (me *MultiError) HasErrors() bool {
return len(me.Errors) > 0
}
// 带时间戳的错误
type TimedError struct {
Err error
Timestamp time.Time
}
func (e *TimedError) Error() string {
return fmt.Sprintf("[%s] %s", e.Timestamp.Format("15:04:05"), e.Err.Error())
}
func (e *TimedError) Unwrap() error {
return e.Err
}
// 错误处理最佳实践
func validateUser(name, email string, age int) error {
me := &MultiError{}
if name == "" {
me.Add(&ValidationError{Field: "name", Message: "不能为空"})
}
if email == "" {
me.Add(&ValidationError{Field: "email", Message: "不能为空"})
}
if age < 0 || age > 150 {
me.Add(&ValidationError{Field: "age", Message: "必须在0-150之间"})
}
if me.HasErrors() {
return me
}
return nil
}
func main() {
// 使用自定义错误
if err := validateUser("", "", -1); err != nil {
fmt.Println(err)
}
// 带时间戳的错误
te := &TimedError{
Err: errors.New("数据库连接失败"),
Timestamp: time.Now(),
}
fmt.Println(te)
}
// 需要导入 errors 包
import "errors"
12. 文件I/O
12.1 基本文件操作
package main
import (
"bufio"
"fmt"
"io"
"os"
"path/filepath"
)
func main() {
// 写入文件
data := []byte("Hello, Go语言文件操作!\n第二行内容。\n")
err := os.WriteFile("test.txt", data, 0644)
if err != nil {
fmt.Println("写入失败:", err)
return
}
fmt.Println("文件写入成功")
// 读取整个文件
content, err := os.ReadFile("test.txt")
if err != nil {
fmt.Println("读取失败:", err)
return
}
fmt.Printf("文件内容:\n%s", content)
// 使用os.File进行更精细的控制
file, err := os.Create("output.txt")
if err != nil {
fmt.Println("创建文件失败:", err)
return
}
defer file.Close()
// 写入字符串
file.WriteString("写入字符串内容\n")
file.Write([]byte("写入字节内容\n"))
// 读取文件
file2, err := os.Open("test.txt")
if err != nil {
fmt.Println("打开文件失败:", err)
return
}
defer file2.Close()
// 逐行读取
scanner := bufio.NewScanner(file2)
lineNum := 0
for scanner.Scan() {
lineNum++
fmt.Printf("第%d行: %s\n", lineNum, scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println("扫描错误:", err)
}
// 使用缓冲写入
file3, _ := os.Create("buffered.txt")
writer := bufio.NewWriter(file3)
for i := 0; i < 10; i++ {
writer.WriteString(fmt.Sprintf("第%d行\n", i))
}
writer.Flush() // 必须刷新缓冲区
file3.Close()
// 追加模式
file4, _ := os.OpenFile("test.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
file4.WriteString("追加的内容\n")
file4.Close()
// 使用io.Copy复制文件
src, _ := os.Open("test.txt")
dst, _ := os.Create("copy.txt")
bytesCopied, _ := io.Copy(dst, src)
fmt.Printf("复制了 %d 字节\n", bytesCopied)
src.Close()
dst.Close()
// 临时文件
tmpFile, _ := os.CreateTemp("", "example-*.txt")
tmpFile.WriteString("临时文件内容")
fmt.Println("临时文件:", tmpFile.Name())
tmpFile.Close()
os.Remove(tmpFile.Name()) // 使用后删除
// 清理
os.Remove("test.txt")
os.Remove("output.txt")
os.Remove("buffered.txt")
os.Remove("copy.txt")
}
12.2 目录操作
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
// 创建目录
os.MkdirAll("testdir/subdir/deep", 0755)
// 遍历目录
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
typ := "文件"
if info.IsDir() {
typ = "目录"
}
fmt.Printf("[%s] %s (%d bytes)\n", typ, path, info.Size())
return nil
})
// 读取目录内容
entries, _ := os.ReadDir(".")
for _, entry := range entries {
info, _ := entry.Info()
fmt.Printf("名称: %s, 是目录: %v, 大小: %d\n",
entry.Name(), entry.IsDir(), info.Size())
}
// 文件路径操作
path := "/home/user/documents/report.pdf"
fmt.Println("目录:", filepath.Dir(path))
fmt.Println("文件名:", filepath.Base(path))
fmt.Println("扩展名:", filepath.Ext(path))
fmt.Println("绝对路径:", filepath.Abs(path))
// 路径拼接
joined := filepath.Join("home", "user", "documents", "file.txt")
fmt.Println("拼接路径:", joined)
// 清理
os.RemoveAll("testdir")
}
12.3 文件信息与权限
package main
import (
"fmt"
"os"
)
func main() {
// 获取文件信息
info, err := os.Stat("main.go")
if err != nil {
if os.IsNotExist(err) {
fmt.Println("文件不存在")
}
return
}
fmt.Println("文件名:", info.Name())
fmt.Println("大小:", info.Size(), "字节")
fmt.Println("权限:", info.Mode())
fmt.Println("修改时间:", info.ModTime())
fmt.Println("是目录:", info.IsDir())
// 检查文件是否存在
if _, err := os.Stat("nonexistent.txt"); os.IsNotExist(err) {
fmt.Println("文件不存在")
}
// 修改权限
os.Chmod("test.txt", 0755)
// 创建符号链接
os.Symlink("target.txt", "link.txt")
os.Remove("link.txt")
}
13. HTTP服务开发
13.1 基本HTTP服务器
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
)
// 基本处理器
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "你好,世界!请求路径: %s\n", r.URL.Path)
}
// JSON响应
type Response struct {
Message string `json:"message"`
Data interface{} `json:"data"`
Time string `json:"time"`
}
func apiHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
resp := Response{
Message: "成功",
Data: map[string]string{"name": "Go", "version": "1.22"},
Time: time.Now().Format(time.RFC3339),
}
json.NewEncoder(w).Encode(resp)
}
// 处理不同HTTP方法
func methodHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
fmt.Fprintln(w, "GET请求")
case http.MethodPost:
fmt.Fprintln(w, "POST请求")
case http.MethodPut:
fmt.Fprintln(w, "PUT请求")
case http.MethodDelete:
fmt.Fprintln(w, "DELETE请求")
default:
http.Error(w, "不支持的方法", http.StatusMethodNotAllowed)
}
}
// 中间件
func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next(w, r)
log.Printf("[%s] %s %s 耗时: %v",
r.Method, r.URL.Path, r.RemoteAddr, time.Since(start))
}
}
func corsMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
next(w, r)
}
}
func main() {
// 使用默认路由器
http.HandleFunc("/", helloHandler)
http.HandleFunc("/api", apiHandler)
http.HandleFunc("/methods", methodHandler)
// 使用中间件
http.HandleFunc("/logged", corsMiddleware(loggingMiddleware(helloHandler)))
fmt.Println("服务器启动在 :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
13.2 HTTP客户端
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
func main() {
// 创建自定义客户端(带超时)
client := &http.Client{
Timeout: 10 * time.Second,
}
// GET请求
resp, err := client.Get("https://httpbin.org/get")
if err != nil {
fmt.Println("GET请求失败:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("状态码: %d\n", resp.StatusCode)
fmt.Printf("响应: %s\n", body[:200])
// POST请求(JSON)
data := map[string]string{
"name": "张三",
"email": "zhangsan@example.com",
}
jsonData, _ := json.Marshal(data)
resp2, err := client.Post(
"https://httpbin.org/post",
"application/json",
bytes.NewBuffer(jsonData),
)
if err != nil {
fmt.Println("POST请求失败:", err)
return
}
defer resp2.Body.Close()
body2, _ := io.ReadAll(resp2.Body)
fmt.Printf("\nPOST响应: %s\n", body2[:200])
// 使用自定义Request
req, _ := http.NewRequest("PUT", "https://httpbin.org/put", bytes.NewBuffer(jsonData))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer token123")
resp3, err := client.Do(req)
if err != nil {
fmt.Println("PUT请求失败:", err)
return
}
defer resp3.Body.Close()
body3, _ := io.ReadAll(resp3.Body)
fmt.Printf("\nPUT响应: %s\n", body3[:200])
}
14. 数据库操作
14.1 使用database/sql
package main
import (
"database/sql"
"fmt"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
// User模型
type User struct {
ID int
Name string
Email string
Age int
CreatedAt time.Time
}
func main() {
// 连接数据库
dsn := "user:password@tcp(localhost:3306)/mydb?charset=utf8mb4&parseTime=true"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal("连接数据库失败:", err)
}
defer db.Close()
// 配置连接池
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(5 * time.Minute)
// 测试连接
if err := db.Ping(); err != nil {
log.Fatal("数据库Ping失败:", err)
}
fmt.Println("数据库连接成功")
// 创建表
createTable := `
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
age INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`
_, err = db.Exec(createTable)
if err != nil {
log.Fatal("创建表失败:", err)
}
// 插入数据
result, err := db.Exec(
"INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
"张三", "zhangsan@example.com", 25,
)
if err != nil {
log.Fatal("插入失败:", err)
}
id, _ := result.LastInsertId()
fmt.Printf("插入成功,ID: %d\n", id)
// 查询单行
var user User
err = db.QueryRow(
"SELECT id, name, email, age, created_at FROM users WHERE id = ?", id,
).Scan(&user.ID, &user.Name, &user.Email, &user.Age, &user.CreatedAt)
if err != nil {
if err == sql.ErrNoRows {
fmt.Println("未找到用户")
} else {
log.Fatal("查询失败:", err)
}
}
fmt.Printf("查询结果: %+v\n", user)
// 查询多行
rows, err := db.Query("SELECT id, name, email, age FROM users WHERE age > ?", 18)
if err != nil {
log.Fatal("查询失败:", err)
}
defer rows.Close()
var users []User
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Name, &u.Email, &u.Age); err != nil {
log.Fatal("扫描失败:", err)
}
users = append(users, u)
}
if err := rows.Err(); err != nil {
log.Fatal("遍历失败:", err)
}
fmt.Printf("查询到 %d 个用户\n", len(users))
// 事务
tx, err := db.Begin()
if err != nil {
log.Fatal("开启事务失败:", err)
}
_, err = tx.Exec("UPDATE users SET age = age + 1 WHERE name = ?", "张三")
if err != nil {
tx.Rollback()
log.Fatal("更新失败:", err)
}
_, err = tx.Exec("INSERT INTO users (name, email, age) VALUES (?, ?, ?)",
"李四", "lisi@example.com", 30)
if err != nil {
tx.Rollback()
log.Fatal("插入失败:", err)
}
if err := tx.Commit(); err != nil {
log.Fatal("提交事务失败:", err)
}
fmt.Println("事务提交成功")
// 预编译语句(防止SQL注入)
stmt, err := db.Prepare("SELECT id, name, email FROM users WHERE age > ?")
if err != nil {
log.Fatal("预编译失败:", err)
}
defer stmt.Close()
rows2, _ := stmt.Query(20)
defer rows2.Close()
for rows2.Next() {
var id, age int
var name, email string
rows2.Scan(&id, &name, &email)
fmt.Printf("ID: %d, Name: %s, Email: %s\n", id, name, email)
}
}
15. JSON处理
15.1 基本JSON操作
package main
import (
"encoding/json"
"fmt"
"os"
"time"
)
// 结构体标签
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age,omitempty"` // 零值时省略
Password string `json:"-"` // 永不序列化
CreatedAt time.Time `json:"created_at"`
Tags []string `json:"tags,omitempty"`
Address *Address `json:"address,omitempty"` // 指针类型,nil时省略
}
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
func main() {
// 序列化(结构体 -> JSON)
user := User{
ID: 1,
Name: "张三",
Email: "zhangsan@example.com",
Age: 25,
Password: "secret123", // 不会被序列化
CreatedAt: time.Now(),
Tags: []string{"golang", "backend"},
Address: &Address{
City: "北京",
Country: "中国",
},
}
// 格式化输出
jsonData, err := json.MarshalIndent(user, "", " ")
if err != nil {
fmt.Println("序列化失败:", err)
return
}
fmt.Println("序列化结果:")
fmt.Println(string(jsonData))
// 反序列化(JSON -> 结构体)
jsonStr := `{
"id": 2,
"name": "李四",
"email": "lisi@example.com",
"age": 30,
"created_at": "2024-01-15T10:30:00Z",
"tags": ["python", "data"]
}`
var user2 User
err = json.Unmarshal([]byte(jsonStr), &user2)
if err != nil {
fmt.Println("反序列化失败:", err)
return
}
fmt.Printf("\n反序列化结果: %+v\n", user2)
// 未知结构的JSON(使用map)
var unknown map[string]interface{}
json.Unmarshal([]byte(jsonStr), &unknown)
fmt.Println("\n未知结构:")
for k, v := range unknown {
fmt.Printf(" %s: %v (类型: %T)\n", k, v, v)
}
// JSON数组
users := []User{
{ID: 1, Name: "张三", Email: "zhangsan@example.com", Age: 25},
{ID: 2, Name: "李四", Email: "lisi@example.com", Age: 30},
{ID: 3, Name: "王五", Email: "wangwu@example.com", Age: 28},
}
jsonArray, _ := json.MarshalIndent(users, "", " ")
fmt.Println("\nJSON数组:")
fmt.Println(string(jsonArray))
// 流式编码/解码
// 写入文件
file, _ := os.Create("users.json")
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
encoder.Encode(users)
file.Close()
// 从文件读取
file2, _ := os.Open("users.json")
var loadedUsers []User
decoder := json.NewDecoder(file2)
decoder.Decode(&loadedUsers)
file2.Close()
fmt.Printf("\n从文件加载了 %d 个用户\n", len(loadedUsers))
// 自定义JSON序列化
type Color struct {
R, G, B uint8
}
// 自定义序列化
func (c Color) MarshalJSON() ([]byte, error) {
return json.Marshal(fmt.Sprintf("#%02x%02x%02x", c.R, c.G, c.B))
}
// 自定义反序列化
func (c *Color) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
_, err := fmt.Sscanf(s, "#%02x%02x%02x", &c.R, &c.G, &c.B)
return err
}
red := Color{R: 255, G: 0, B: 0}
colorJSON, _ := json.Marshal(red)
fmt.Printf("\n自定义序列化: %s\n", colorJSON)
os.Remove("users.json")
}
16. 测试与基准测试
16.1 单元测试
// calculator/calculator.go
package calculator
import (
"errors"
"math"
)
func Add(a, b float64) float64 {
return a + b
}
func Subtract(a, b float64) float64 {
return a - b
}
func Multiply(a, b float64) float64 {
return a * b
}
func Divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
func Sqrt(x float64) (float64, error) {
if x < 0 {
return 0, errors.New("square root of negative number")
}
return math.Sqrt(x), nil
}
// calculator/calculator_test.go
package calculator
import (
"math"
"testing"
)
// 基本测试
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5.0
if result != expected {
t.Errorf("Add(2, 3) = %f; want %f", result, expected)
}
}
// 表驱动测试
func TestDivide(t *testing.T) {
tests := []struct {
name string
a, b float64
expected float64
wantErr bool
}{
{"正常除法", 10, 3, 3.333333, false},
{"除以零", 10, 0, 0, true},
{"负数除法", -10, 2, -5, false},
{"零除以数", 0, 5, 0, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := Divide(tt.a, tt.b)
if (err != nil) != tt.wantErr {
t.Errorf("Divide(%f, %f) error = %v, wantErr %v",
tt.a, tt.b, err, tt.wantErr)
return
}
if !tt.wantErr && math.Abs(result-tt.expected) > 0.0001 {
t.Errorf("Divide(%f, %f) = %f; want %f",
tt.a, tt.b, result, tt.expected)
}
})
}
}
// 测试Sqrt
func TestSqrt(t *testing.T) {
tests := []struct {
input float64
want float64
wantErr bool
}{
{4, 2, false},
{9, 3, false},
{-1, 0, true},
{0, 0, false},
}
for _, tt := range tests {
got, err := Sqrt(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("Sqrt(%v) error = %v, wantErr %v", tt.input, err, tt.wantErr)
continue
}
if !tt.wantErr && math.Abs(got-tt.want) > 1e-10 {
t.Errorf("Sqrt(%v) = %v, want %v", tt.input, got, tt.want)
}
}
}
// 基准测试
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2.5, 3.7)
}
}
func BenchmarkDivide(b *testing.B) {
for i := 0; i < b.N; i++ {
Divide(100, 7)
}
}
// 子基准测试
func BenchmarkDivide_Precision(b *testing.B) {
benchmarks := []struct {
name string
a, b float64
}{
{"整数", 100, 7},
{"小数", 3.14159, 2.71828},
{"大数", 1e15, 3},
}
for _, bm := range benchmarks {
b.Run(bm.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
Divide(bm.a, bm.b)
}
})
}
}
运行测试:
# 运行所有测试
go test ./...
# 运行特定包的测试
go test ./calculator/
# 运行特定测试
go test -run TestDivide ./calculator/
# 显示详细输出
go test -v ./calculator/
# 运行基准测试
go test -bench=. ./calculator/
# 生成测试覆盖率
go test -cover ./calculator/
go test -coverprofile=coverage.out ./calculator/
go tool cover -html=coverage.out # 浏览器查看覆盖率
# 模糊测试(Go 1.18+)
func FuzzDivide(f *testing.F) {
f.Add(10.0, 3.0)
f.Add(0.0, 1.0)
f.Fuzz(func(t *testing.T, a, b float64) {
result, err := Divide(a, b)
if err != nil && b != 0 {
t.Errorf("Divide(%f, %f) unexpected error: %v", a, b, err)
}
if err == nil && b != 0 {
expected := a / b
if math.Abs(result-expected) > 1e-10 {
t.Errorf("Divide(%f, %f) = %f, want %f", a, b, result, expected)
}
}
})
}
17. 依赖管理
17.1 Go Modules详解
# 初始化新项目
mkdir myproject && cd myproject
go mod init github.com/username/myproject
# 查看go.mod
cat go.mod
# module github.com/username/myproject
# go 1.22
# 添加依赖
go get github.com/gin-gonic/gin
go get github.com/sirupsen/logrus@v1.9.3
# 添加特定版本
go get github.com/gin-gonic/gin@v1.9.1
# 更新到最新版本
go get -u github.com/gin-gonic/gin
# 更新所有依赖
go get -u ./...
# 整理依赖
go mod tidy
# 查看依赖图
go mod graph
# 验证依赖完整性
go mod verify
# 生成vendor目录
go mod vendor
# 使用vendor构建
go build -mod=vendor ./...
# 查看可用版本
go list -m -versions github.com/gin-gonic/gin
17.2 go.sum文件
go.sum文件记录了每个依赖的加密哈希值,确保依赖的完整性和安全性:
github.com/gin-gonic/gin v1.9.1 h1:4idEAnCLz/...
github.com/gin-gonic/gin v1.9.1/go.mod h1:...
17.3 私有模块
# 配置私有模块
go env -w GOPRIVATE=github.com/mycompany/*
# 或者分别配置
go env -w GONOSUMCHECK=github.com/mycompany/*
go env -w GONOSUMDB=github.com/mycompany/*
# 使用私有仓库(需要配置git认证)
go get github.com/mycompany/private-repo
17.4 版本选择策略
// go.mod中的版本约束
require (
// 精确版本
github.com/pkg/errors v0.9.1
// 语义化版本
github.com/gin-gonic/gin v1.9.1
// 主版本号(需要在import路径中包含)
github.com/go-yaml/yaml/v3 v3.0.1
)
// replace替换(用于本地开发或fork)
replace (
github.com/old/package => github.com/new/package v1.0.0
github.com/local/package => ../local/package
)
// exclude排除特定版本
exclude github.com/broken/package v1.2.3
18. 常用标准库
18.1 fmt格式化
package main
import "fmt"
func main() {
name := "张三"
age := 25
height := 175.5
// Print系列
fmt.Println("换行打印")
fmt.Print("不换行")
fmt.Printf("格式化: %s, %d岁, %.1fcm\n", name, age, height)
// 格式化动词
fmt.Printf("字符串: %s\n", name) // 字符串
fmt.Printf("整数: %d\n", age) // 十进制整数
fmt.Printf("二进制: %b\n", age) // 二进制
fmt.Printf("八进制: %o\n", age) // 八进制
fmt.Printf("十六进制: %x\n", age) // 十六进制(小写)
fmt.Printf("浮点数: %f\n", height) // 浮点数
fmt.Printf("科学计数: %e\n", height) // 科学计数法
fmt.Printf("布尔: %t\n", true) // 布尔
fmt.Printf("类型: %T\n", name) // 类型
fmt.Printf("值: %v\n", name) // 值(默认格式)
fmt.Printf("详细: %+v\n", struct{X int}{1}) // 包含字段名
fmt.Printf("Go语法: %#v\n", name) // Go语法表示
fmt.Printf("指针: %p\n", &name) // 指针地址
// 宽度和精度
fmt.Printf("[%10s]\n", "hello") // 右对齐,宽度10
fmt.Printf("[%-10s]\n", "hello") // 左对齐,宽度10
fmt.Printf("[%010d]\n", 42) // 零填充,宽度10
fmt.Printf("[%.3f]\n", 3.14159) // 精度3位小数
fmt.Printf("[%10.3f]\n", 3.14159) // 宽度10,精度3
// Sprintf返回字符串
s := fmt.Sprintf("姓名: %s, 年龄: %d", name, age)
fmt.Println(s)
// Errorf创建错误
err := fmt.Errorf("用户 %s 不存在 (ID: %d)", name, 42)
fmt.Println(err)
}
18.2 strings字符串处理
package main
import (
"fmt"
"strings"
)
func main() {
s := "Hello, Go语言!Hello, World!"
// 查找
fmt.Println("包含Go:", strings.Contains(s, "Go"))
fmt.Println("包含Rust:", strings.Contains(s, "Rust"))
fmt.Println("前缀Hello:", strings.HasPrefix(s, "Hello"))
fmt.Println("后缀World!:", strings.HasSuffix(s, "World!"))
fmt.Println("第一次出现Hello:", strings.Index(s, "Hello"))
fmt.Println("最后一次出现Hello:", strings.LastIndex(s, "Hello"))
// 转换
fmt.Println("大写:", strings.ToUpper("hello"))
fmt.Println("小写:", strings.ToUpper("HELLO"))
fmt.Println("标题:", strings.Title("hello world"))
// 替换
fmt.Println("替换:", strings.Replace("aabbaa", "aa", "xx", 1))
fmt.Println("全部替换:", strings.ReplaceAll("aabbaa", "aa", "xx"))
// 分割与连接
parts := strings.Split("a,b,c,d", ",")
fmt.Println("分割:", parts)
joined := strings.Join(parts, " | ")
fmt.Println("连接:", joined)
// 修剪
fmt.Println("修剪空格:", strings.TrimSpace(" hello "))
fmt.Println("修剪字符:", strings.Trim("***hello***", "*"))
fmt.Println("修剪左侧:", strings.TrimLeft("***hello***", "*"))
fmt.Println("修剪右侧:", strings.TrimRight("***hello***", "*"))
// 重复和比较
fmt.Println("重复:", strings.Repeat("Go!", 3))
fmt.Println("比较:", strings.Compare("abc", "abd")) // -1
fmt.Println("相等:", strings.EqualFold("Go", "go")) // true(忽略大小写)
// Reader
reader := strings.NewReader("Hello, Reader!")
buf := make([]byte, 5)
n, _ := reader.Read(buf)
fmt.Printf("读取了%d字节: %s\n", n, buf)
}
18.3 time时间处理
package main
import (
"fmt"
"time"
)
func main() {
// 当前时间
now := time.Now()
fmt.Println("当前时间:", now)
fmt.Println("年:", now.Year())
fmt.Println("月:", now.Month())
fmt.Println("日:", now.Day())
fmt.Println("时:", now.Hour())
fmt.Println("分:", now.Minute())
fmt.Println("秒:", now.Second())
fmt.Println("星期:", now.Weekday())
// 时间格式化(Go使用特定时间 2006-01-02 15:04:05)
fmt.Println("日期:", now.Format("2006-01-02"))
fmt.Println("时间:", now.Format("15:04:05"))
fmt.Println("完整:", now.Format("2006-01-02 15:04:05"))
fmt.Println("RFC3339:", now.Format(time.RFC3339))
// 解析时间字符串
t, err := time.Parse("2006-01-02", "2024-01-15")
if err != nil {
fmt.Println("解析失败:", err)
} else {
fmt.Println("解析结果:", t)
}
// 时间运算
tomorrow := now.Add(24 * time.Hour)
fmt.Println("明天:", tomorrow.Format("2006-01-02"))
lastWeek := now.AddDate(0, 0, -7)
fmt.Println("上周:", lastWeek.Format("2006-01-02"))
// 时间差
diff := tomorrow.Sub(now)
fmt.Println("时间差:", diff)
fmt.Println("小时:", diff.Hours())
fmt.Println("分钟:", diff.Minutes())
// 时间比较
fmt.Println("在明天之前:", now.Before(tomorrow))
fmt.Println("在明天之后:", now.After(tomorrow))
fmt.Println("相等:", now.Equal(now))
// 定时器
timer := time.NewTimer(2 * time.Second)
fmt.Println("等待2秒...")
<-timer.C
fmt.Println("2秒已过")
// 周期性定时器
ticker := time.NewTicker(500 * time.Millisecond)
go func() {
for t := range ticker.C {
fmt.Println("Tick:", t.Format("15:04:05"))
}
}()
time.Sleep(2 * time.Second)
ticker.Stop()
fmt.Println("Ticker停止")
}
18.4 strconv类型转换
package main
import (
"fmt"
"strconv"
)
func main() {
// 整数转字符串
fmt.Println("Itoa:", strconv.Itoa(42))
fmt.Println("FormatInt:", strconv.FormatInt(255, 16)) // 十六进制
// 字符串转整数
n, _ := strconv.Atoi("123")
fmt.Println("Atoi:", n)
n2, _ := strconv.ParseInt("ff", 16, 64)
fmt.Println("ParseInt(十六进制):", n2)
// 浮点数
fmt.Println("Float:", strconv.FormatFloat(3.14159, 'f', 2, 64))
f, _ := strconv.ParseFloat("3.14", 64)
fmt.Println("ParseFloat:", f)
// 布尔值
fmt.Println("Bool:", strconv.FormatBool(true))
b, _ := strconv.ParseBool("true")
fmt.Println("ParseBool:", b)
// 错误处理
_, err := strconv.Atoi("abc")
if err != nil {
fmt.Println("转换错误:", err)
}
}
18.5 sort排序
package main
import (
"fmt"
"sort"
)
func main() {
// 基本排序
ints := []int{5, 2, 6, 3, 1, 4}
sort.Ints(ints)
fmt.Println("整数排序:", ints)
strings2 := []string{"banana", "apple", "cherry"}
sort.Strings(strings2)
fmt.Println("字符串排序:", strings2)
// 自定义排序
people := []struct {
Name string
Age int
}{
{"张三", 25},
{"李四", 30},
{"王五", 20},
{"赵六", 28},
}
// 按年龄排序
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println("按年龄排序:", people)
// 使用sort.Interface
type PersonSlice []struct {
Name string
Age int
}
func (p PersonSlice) Len() int { return len(p) }
func (p PersonSlice) Less(i, j int) bool { return p[i].Name < p[j].Name }
func (p PersonSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
sort.Sort(PersonSlice(people))
fmt.Println("按姓名排序:", people)
// 二分查找(需要先排序)
sort.Ints(ints)
idx := sort.SearchInts(ints, 3)
fmt.Println("查找3的索引:", idx)
// 反转
sort.Sort(sort.Reverse(sort.IntSlice(ints)))
fmt.Println("降序排序:", ints)
}
19. 实战:RESTful API服务
以下是一个完整的RESTful API服务实现,包含用户管理的增删改查功能:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"strings"
"sync"
"time"
)
// ===== 数据模型 =====
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type APIResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// ===== 数据存储(内存模拟)=====
type UserStore struct {
mu sync.RWMutex
users map[int]*User
nextID int
}
func NewUserStore() *UserStore {
return &UserStore{
users: make(map[int]*User),
nextID: 1,
}
}
func (s *UserStore) Create(user *User) *User {
s.mu.Lock()
defer s.mu.Unlock()
user.ID = s.nextID
s.nextID++
user.CreatedAt = time.Now()
user.UpdatedAt = time.Now()
s.users[user.ID] = user
return user
}
func (s *UserStore) GetByID(id int) *User {
s.mu.RLock()
defer s.mu.RUnlock()
return s.users[id]
}
func (s *UserStore) GetAll() []*User {
s.mu.RLock()
defer s.mu.RUnlock()
users := make([]*User, 0, len(s.users))
for _, u := range s.users {
users = append(users, u)
}
return users
}
func (s *UserStore) Update(id int, update *User) *User {
s.mu.Lock()
defer s.mu.Unlock()
existing, ok := s.users[id]
if !ok {
return nil
}
if update.Name != "" {
existing.Name = update.Name
}
if update.Email != "" {
existing.Email = update.Email
}
if update.Age > 0 {
existing.Age = update.Age
}
existing.UpdatedAt = time.Now()
return existing
}
func (s *UserStore) Delete(id int) bool {
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.users[id]; !ok {
return false
}
delete(s.users, id)
return true
}
// ===== HTTP处理器 =====
type UserHandler struct {
store *UserStore
}
func NewUserHandler(store *UserStore) *UserHandler {
return &UserHandler{store: store}
}
func (h *UserHandler) writeJSON(w http.ResponseWriter, code int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
json.NewEncoder(w).Encode(data)
}
func (h *UserHandler) HandleUsers(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
h.listUsers(w, r)
case http.MethodPost:
h.createUser(w, r)
default:
h.writeJSON(w, http.StatusMethodNotAllowed, APIResponse{
Code: 405, Message: "不支持的HTTP方法",
})
}
}
func (h *UserHandler) HandleUser(w http.ResponseWriter, r *http.Request) {
// 从URL提取ID: /api/users/123
parts := strings.Split(r.URL.Path, "/")
if len(parts) < 4 {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 400, Message: "无效的URL",
})
return
}
id, err := strconv.Atoi(parts[3])
if err != nil {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 400, Message: "无效的用户ID",
})
return
}
switch r.Method {
case http.MethodGet:
h.getUser(w, r, id)
case http.MethodPut:
h.updateUser(w, r, id)
case http.MethodDelete:
h.deleteUser(w, r, id)
default:
h.writeJSON(w, http.StatusMethodNotAllowed, APIResponse{
Code: 405, Message: "不支持的HTTP方法",
})
}
}
func (h *UserHandler) listUsers(w http.ResponseWriter, r *http.Request) {
users := h.store.GetAll()
h.writeJSON(w, http.StatusOK, APIResponse{
Code: 200, Message: "成功", Data: users,
})
}
func (h *UserHandler) createUser(w http.ResponseWriter, r *http.Request) {
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 400, Message: "无效的请求体",
})
return
}
// 验证
if user.Name == "" || user.Email == "" {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 400, Message: "姓名和邮箱不能为空",
})
return
}
created := h.store.Create(&user)
h.writeJSON(w, http.StatusCreated, APIResponse{
Code: 201, Message: "创建成功", Data: created,
})
}
func (h *UserHandler) getUser(w http.ResponseWriter, r *http.Request, id int) {
user := h.store.GetByID(id)
if user == nil {
h.writeJSON(w, http.StatusNotFound, APIResponse{
Code: 404, Message: "用户不存在",
})
return
}
h.writeJSON(w, http.StatusOK, APIResponse{
Code: 200, Message: "成功", Data: user,
})
}
func (h *UserHandler) updateUser(w http.ResponseWriter, r *http.Request, id int) {
var update User
if err := json.NewDecoder(r.Body).Decode(&update); err != nil {
h.writeJSON(w, http.StatusBadRequest, APIResponse{
Code: 400, Message: "无效的请求体",
})
return
}
user := h.store.Update(id, &update)
if user == nil {
h.writeJSON(w, http.StatusNotFound, APIResponse{
Code: 404, Message: "用户不存在",
})
return
}
h.writeJSON(w, http.StatusOK, APIResponse{
Code: 200, Message: "更新成功", Data: user,
})
}
func (h *UserHandler) deleteUser(w http.ResponseWriter, r *http.Request, id int) {
if !h.store.Delete(id) {
h.writeJSON(w, http.StatusNotFound, APIResponse{
Code: 404, Message: "用户不存在",
})
return
}
h.writeJSON(w, http.StatusOK, APIResponse{
Code: 200, Message: "删除成功",
})
}
// ===== 中间件 =====
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("[%s] %s %s - %v", r.Method, r.URL.Path, r.RemoteAddr, time.Since(start))
})
}
func recoveryMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("Panic恢复: %v", err)
http.Error(w, "服务器内部错误", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
// ===== 路由器 =====
type Router struct {
userHandler *UserHandler
}
func NewRouter(userHandler *UserHandler) *Router {
return &Router{userHandler: userHandler}
}
func (rt *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
switch {
case path == "/api/users":
rt.userHandler.HandleUsers(w, r)
case strings.HasPrefix(path, "/api/users/"):
rt.userHandler.HandleUser(w, r)
case path == "/health":
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
default:
http.NotFound(w, r)
}
}
// ===== 主函数 =====
func main() {
store := NewUserStore()
handler := NewUserHandler(store)
router := NewRouter(handler)
// 添加中间件链
var httpHandler http.Handler = router
httpHandler = corsMiddleware(httpHandler)
httpHandler = loggingMiddleware(httpHandler)
httpHandler = recoveryMiddleware(httpHandler)
// 创建服务器
server := &http.Server{
Addr: ":8080",
Handler: httpHandler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
fmt.Println("RESTful API服务启动在 http://localhost:8080")
fmt.Println("API端点:")
fmt.Println(" GET /api/users - 获取所有用户")
fmt.Println(" POST /api/users - 创建用户")
fmt.Println(" GET /api/users/:id - 获取单个用户")
fmt.Println(" PUT /api/users/:id - 更新用户")
fmt.Println(" DELETE /api/users/:id - 删除用户")
fmt.Println(" GET /health - 健康检查")
log.Fatal(server.ListenAndServe())
}
测试API:
# 获取所有用户
curl http://localhost:8080/api/users
# 创建用户
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name":"张三","email":"zhangsan@example.com","age":25}'
# 获取单个用户
curl http://localhost:8080/api/users/1
# 更新用户
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{"name":"张三丰","age":30}'
# 删除用户
curl -X DELETE http://localhost:8080/api/users/1
# 健康检查
curl http://localhost:8080/health
20. 实战:并发爬虫
以下是一个完整的并发网页爬虫实现:
package main
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"regexp"
"strings"
"sync"
"time"
)
// ===== 数据结构 =====
type CrawlResult struct {
URL string
Title string
StatusCode int
Links []string
Error error
Duration time.Duration
}
type Crawler struct {
maxWorkers int
maxDepth int
timeout time.Duration
visited map[string]bool
mu sync.Mutex
wg sync.WaitGroup
results []CrawlResult
resultsMu sync.Mutex
urlCh chan crawlTask
httpClient *http.Client
allowedDomain string
}
type crawlTask struct {
url string
depth int
}
// ===== 爬虫核心 =====
func NewCrawler(maxWorkers, maxDepth int, timeout time.Duration) *Crawler {
return &Crawler{
maxWorkers: maxWorkers,
maxDepth: maxDepth,
timeout: timeout,
visited: make(map[string]bool),
urlCh: make(chan crawlTask, maxWorkers*2),
httpClient: &http.Client{
Timeout: timeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) >= 5 {
return fmt.Errorf("重定向次数过多")
}
return nil
},
},
}
}
func (c *Crawler) isVisited(urlStr string) bool {
c.mu.Lock()
defer c.mu.Unlock()
return c.visited[urlStr]
}
func (c *Crawler) markVisited(urlStr string) {
c.mu.Lock()
defer c.mu.Unlock()
c.visited[urlStr] = true
}
func (c *Crawler) addResult(result CrawlResult) {
c.resultsMu.Lock()
defer c.resultsMu.Unlock()
c.results = append(c.results, result)
}
func (c *Crawler) fetch(ctx context.Context, urlStr string) CrawlResult {
start := time.Now()
result := CrawlResult{URL: urlStr}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, urlStr, nil)
if err != nil {
result.Error = fmt.Errorf("创建请求失败: %w", err)
result.Duration = time.Since(start)
return result
}
req.Header.Set("User-Agent", "GoCrawler/1.0")
resp, err := c.httpClient.Do(req)
if err != nil {
result.Error = fmt.Errorf("请求失败: %w", err)
result.Duration = time.Since(start)
return result
}
defer resp.Body.Close()
result.StatusCode = resp.StatusCode
result.Duration = time.Since(start)
// 只处理HTML响应
contentType := resp.Header.Get("Content-Type")
if !strings.Contains(contentType, "text/html") {
return result
}
body, err := io.ReadAll(io.LimitReader(resp.Body, 10*1024*1024)) // 限制10MB
if err != nil {
result.Error = fmt.Errorf("读取响应失败: %w", err)
return result
}
html := string(body)
// 提取标题
titleRe := regexp.MustCompile(`(?i)<title>(.*?)</title>`)
if matches := titleRe.FindStringSubmatch(html); len(matches) > 1 {
result.Title = strings.TrimSpace(matches[1])
}
// 提取链接
linkRe := regexp.MustCompile(`(?i)href=["'](.*?)["']`)
matches := linkRe.FindAllStringSubmatch(html, -1)
seen := make(map[string]bool)
for _, match := range matches {
if len(match) > 1 {
link := match[1]
// 解析相对URL
parsedBase, _ := url.Parse(urlStr)
parsedLink, _ := url.Parse(link)
resolved := parsedBase.ResolveReference(parsedLink)
resolved.Fragment = "" // 去除锚点
resolvedStr := resolved.String()
if !seen[resolvedStr] && (resolved.Scheme == "http" || resolved.Scheme == "https") {
seen[resolvedStr] = true
result.Links = append(result.Links, resolvedStr)
}
}
}
return result
}
func (c *Crawler) worker(ctx context.Context) {
defer c.wg.Done()
for task := range c.urlCh {
select {
case <-ctx.Done():
return
default:
}
if c.isVisited(task.url) {
continue
}
c.markVisited(task.url)
fmt.Printf("[深度%d] 正在爬取: %s\n", task.depth, task.url)
result := c.fetch(ctx, task.url)
c.addResult(result)
if result.Error != nil {
fmt.Printf(" ✗ 错误: %v\n", result.Error)
continue
}
fmt.Printf(" ✓ 状态: %d, 标题: %s, 耗时: %v, 链接数: %d\n",
result.StatusCode, result.Title, result.Duration, len(result.Links))
// 如果未达到最大深度,继续爬取子链接
if task.depth < c.maxDepth {
for _, link := range result.Links {
if !c.isVisited(link) {
c.wg.Add(1)
go func(l string) {
defer c.wg.Done()
select {
case c.urlCh <- crawlTask{url: l, depth: task.depth + 1}:
case <-ctx.Done():
}
}(link)
}
}
}
}
}
func (c *Crawler) Crawl(ctx context.Context, startURL string) []CrawlResult {
// 解析起始URL的域名
parsed, err := url.Parse(startURL)
if err != nil {
return nil
}
c.allowedDomain = parsed.Host
// 启动worker
for i := 0; i < c.maxWorkers; i++ {
c.wg.Add(1)
go c.worker(ctx)
}
// 发送起始URL
c.wg.Add(1)
go func() {
defer c.wg.Done()
c.urlCh <- crawlTask{url: startURL, depth: 0}
}()
// 等待所有任务完成
c.wg.Wait()
close(c.urlCh)
return c.results
}
// ===== 报告生成 =====
func generateReport(results []CrawlResult) string {
var sb strings.Builder
sb.WriteString("=== 爬虫报告 ===\n\n")
sb.WriteString(fmt.Sprintf("总计爬取: %d 个页面\n", len(results)))
successCount := 0
errorCount := 0
var totalDuration time.Duration
for _, r := range results {
if r.Error != nil {
errorCount++
} else {
successCount++
}
totalDuration += r.Duration
}
sb.WriteString(fmt.Sprintf("成功: %d, 失败: %d\n", successCount, errorCount))
sb.WriteString(fmt.Sprintf("总耗时: %v\n\n", totalDuration))
sb.WriteString("=== 详细结果 ===\n\n")
for i, r := range results {
sb.WriteString(fmt.Sprintf("%d. %s\n", i+1, r.URL))
if r.Title != "" {
sb.WriteString(fmt.Sprintf(" 标题: %s\n", r.Title))
}
sb.WriteString(fmt.Sprintf(" 状态: %d\n", r.StatusCode))
sb.WriteString(fmt.Sprintf(" 耗时: %v\n", r.Duration))
if r.Error != nil {
sb.WriteString(fmt.Sprintf(" 错误: %v\n", r.Error))
}
if len(r.Links) > 0 {
sb.WriteString(fmt.Sprintf(" 发现链接: %d 个\n", len(r.Links)))
}
sb.WriteString("\n")
}
return sb.String()
}
// ===== 主函数 =====
func main() {
if len(os.Args) < 2 {
fmt.Println("用法: go run crawler.go <URL> [最大深度] [并发数]")
fmt.Println("示例: go run crawler.go https://example.com 2 5")
return
}
startURL := os.Args[1]
maxDepth := 1
maxWorkers := 5
if len(os.Args) > 2 {
fmt.Sscanf(os.Args[2], "%d", &maxDepth)
}
if len(os.Args) > 3 {
fmt.Sscanf(os.Args[3], "%d", &maxWorkers)
}
fmt.Printf("开始爬取: %s\n", startURL)
fmt.Printf("最大深度: %d, 并发数: %d\n\n", maxDepth, maxWorkers)
// 创建带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// 创建爬虫并开始爬取
crawler := NewCrawler(maxWorkers, maxDepth, 10*time.Second)
results := crawler.Crawl(ctx, startURL)
// 生成报告
report := generateReport(results)
fmt.Println(report)
// 保存报告到文件
reportFile := "crawl_report.txt"
os.WriteFile(reportFile, []byte(report), 0644)
fmt.Printf("报告已保存到: %s\n", reportFile)
}
运行爬虫:
# 基本用法
go run crawler.go https://example.com
# 指定深度和并发数
go run crawler.go https://example.com 3 10
# 编译后运行
go build -o crawler crawler.go
./crawler https://example.com 2 5
21. 部署与优化
21.1 交叉编译
# 查看支持的目标平台
go tool dist list
# 编译为Linux二进制(在macOS上)
GOOS=linux GOARCH=amd64 go build -o myapp-linux main.go
# 编译为Windows二进制
GOOS=windows GOARCH=amd64 go build -o myapp.exe main.go
# 编译为ARM架构(如树莓派)
GOOS=linux GOARCH=arm64 go build -o myapp-arm64 main.go
# 静态编译(无CGO依赖)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags '-s -w' -o myapp-static main.go
21.2 优化编译
# 去除调试信息,减小二进制体积
go build -ldflags '-s -w' -o myapp main.go
# -s: 去除符号表
# -w: 去除DWARF调试信息
# 查看二进制大小
ls -lh myapp
# 使用upx压缩(可选)
# upx --best myapp
21.3 Docker部署
# 多阶段构建
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-s -w' -o main .
# 最终镜像
FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
ENV TZ=Asia/Shanghai
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
构建和运行:
# 构建镜像
docker build -t myapp:latest .
# 运行容器
docker run -d -p 8080:8080 --name myapp myapp:latest
# 查看日志
docker logs -f myapp
21.4 性能优化
1. 内存优化
// 预分配切片容量
// 不好的写法
var items []Item
for _, data := range rawData {
items = append(items, process(data))
}
// 好的写法
items := make([]Item, 0, len(rawData))
for _, data := range rawData {
items = append(items, process(data))
}
// 使用sync.Pool复用对象
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 4096)
},
}
func processRequest(data []byte) {
buf := bufferPool.Get().([]byte)
defer bufferPool.Put(buf)
// 使用buf处理数据...
}
// 避免不必要的内存分配
// 不好的写法:每次循环创建新字符串
for i := 0; i < 1000; i++ {
s := fmt.Sprintf("item-%d", i)
process(s)
}
// 好的写法:使用strings.Builder
var builder strings.Builder
for i := 0; i < 1000; i++ {
builder.Reset()
builder.WriteString("item-")
builder.WriteString(strconv.Itoa(i))
process(builder.String())
}
2. 并发优化
// 控制goroutine数量
func processWithLimit(items []Item, maxWorkers int) []Result {
results := make([]Result, len(items))
sem := make(chan struct{}, maxWorkers)
var wg sync.WaitGroup
for i, item := range items {
wg.Add(1)
sem <- struct{}{} // 获取信号量
go func(idx int, it Item) {
defer wg.Done()
defer func() { <-sem }() // 释放信号量
results[idx] = process(it)
}(i, item)
}
wg.Wait()
return results
}
// 使用errgroup管理并发错误
import "golang.org/x/sync/errgroup"
func fetchAll(urls []string) ([]string, error) {
results := make([]string, len(urls))
g, ctx := errgroup.WithContext(context.Background())
for i, u := range urls {
i, u := i, u
g.Go(func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
resp, err := http.Get(u)
if err != nil {
return err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
results[i] = string(body)
return nil
})
}
if err := g.Wait(); err != nil {
return nil, err
}
return results, nil
}
3. 使用pprof进行性能分析
package main
import (
"net/http"
_ "net/http/pprof"
"log"
)
func main() {
// 在单独的端口启动pprof
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
// 你的应用代码...
select {}
}
# CPU分析
go tool pprof http://localhost:60/debug/pprof/profile?seconds=30
# 内存分析
go tool pprof http://localhost:60/debug/pprof/heap
# goroutine分析
go tool pprof http://localhost:60/debug/pprof/goroutine
# 在交互模式中
(pprof) top
(pprof) list functionName
(pprof) web # 生成SVG图(需要安装graphviz)
4. 编译优化技巧
# 使用编译器优化
go build -gcflags='-m -l' main.go # 查看逃逸分析
go build -gcflags='-B' main.go # 禁用边界检查(谨慎使用)
# 分析二进制
go tool nm myapp | grep main # 查看符号
go tool objdump -S myapp # 反汇编
21.5 监控与日志
package main
import (
"log/slog"
"os"
"time"
)
func main() {
// 使用slog结构化日志(Go 1.21+)
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
logger.Info("服务器启动",
"port", 8080,
"env", "production",
)
logger.Error("数据库连接失败",
"error", "connection refused",
"retry_in", 5*time.Second,
)
// 带上下文的logger
requestLogger := logger.With(
"request_id", "abc-123",
"user_id", 42,
)
requestLogger.Info("处理请求", "path", "/api/users")
}
21.6 优雅关闭
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second) // 模拟处理
fmt.Fprintln(w, "Hello!")
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
// 启动服务器
go func() {
log.Println("服务器启动在 :8080")
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal("服务器异常:", err)
}
}()
// 监听系统信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("收到关闭信号,正在优雅关闭...")
// 给现有请求30秒的完成时间
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatal("关闭失败:", err)
}
log.Println("服务器已安全关闭")
}
总结
本教程从Go语言的历史背景出发,系统性地介绍了Go语言的核心知识体系:
- 基础语法:数据类型、变量常量、控制流、函数方法等构成Go语言的基石。
- 数据结构:数组、切片、映射、结构体和接口是Go程序中数据组织的核心方式。
- 并发编程:goroutine和channel是Go语言最强大的特性,使得并发编程简单而高效。
- 工程实践:包管理、错误处理、测试、依赖管理等是构建大型Go项目的关键能力。
- 应用开发:HTTP服务、数据库操作、JSON处理等是Web开发的日常需求。
- 实战项目:RESTful API服务和并发爬虫展示了Go语言在真实场景中的应用。
- 部署优化:交叉编译、Docker部署、性能分析等帮助你将Go应用推向生产环境。
Go语言的学习曲线相对平缓,但它的并发模型和工程哲学值得深入理解。建议读者在学习过程中多动手实践,通过实际项目加深对Go语言的理解。随着Go语言生态的不断壮大,掌握Go语言将为你的职业生涯增添一项重要技能。
参考资料
本教程由AI生成,内容原创,可自由使用和分享。