Go语言入门到精通教程
目录
1. Go语言简介
1.1 什么是Go语言
Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
特点:
- 简洁、快速、安全
- 并行、有趣、开源
- 内存管理、数组安全、编译迅速
1.2 Go语言的优势
- 简单易学:语法简洁,关键字少(25个)
- 高性能:接近C语言的性能
- 并发支持:原生支持并发编程(goroutine)
- 快速编译:编译速度极快
- 跨平台:支持多平台交叉编译
- 丰富的标准库:功能强大的标准库
- 垃圾回收:自动内存管理
1.3 Go语言应用场景
- 服务器编程
- 分布式系统
- 网络编程
- 云计算
- 容器技术(Docker、Kubernetes)
- 区块链
- 微服务
2. 环境搭建
2.1 安装Go
Windows安装
# 1. 下载安装包
# 访问 https://golang.org/dl/ 下载Windows安装包
# 2. 运行安装程序,默认安装到 C:\Go
# 3. 验证安装
go version
Linux安装
# 1. 下载
wget https://golang.org/dl/go1.21.5.linux-amd64.tar.gz
# 2. 解压
sudo tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
# 3. 配置环境变量
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
# 4. 验证
go version
macOS安装
# 使用Homebrew
brew install go
# 验证
go version
2.2 配置环境变量
# GOROOT:Go安装目录
export GOROOT=/usr/local/go
# GOPATH:工作目录
export GOPATH=$HOME/go
# PATH:添加Go二进制文件路径
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
# GOPROXY:配置代理(中国用户推荐)
export GOPROXY=https://goproxy.cn,direct
2.3 IDE选择
推荐IDE:
- Visual Studio Code + Go插件
- GoLand(JetBrains出品)
- Vim/Neovim + vim-go
- Sublime Text + GoSublime
2.4 第一个Go程序
// hello.go
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
运行程序:
# 方式1:直接运行
go run hello.go
# 方式2:编译后运行
go build hello.go
./hello
# 方式3:安装到GOPATH/bin
go install hello.go
3. 基础语法
3.1 包声明
package main // 声明包名,main包是程序入口
3.2 导入包
// 单个导入
import "fmt"
// 多个导入
import (
"fmt"
"math"
"strings"
)
// 别名导入
import (
f "fmt"
m "math"
)
// 点导入(不推荐)
import . "fmt"
// 匿名导入(只执行init函数)
import _ "database/sql"
3.3 变量声明
package main
import "fmt"
func main() {
// 方式1:标准声明
var name string = "Go"
// 方式2:类型推断
var age = 10
// 方式3:短变量声明(只能在函数内使用)
version := 1.21
// 方式4:批量声明
var (
a int = 1
b string = "hello"
c bool = true
)
// 方式5:多变量声明
var x, y, z int = 1, 2, 3
m, n := 100, 200
fmt.Println(name, age, version, a, b, c, x, y, z, m, n)
}
3.4 常量
package main
import "fmt"
func main() {
// 普通常量
const PI = 3.14159
const URL = "https://golang.org"
// 批量声明
const (
StatusOK = 200
StatusNotFound = 404
StatusError = 500
)
// iota枚举
const (
Sunday = 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
)
fmt.Println(PI, StatusOK, Monday, KB, MB)
}
3.5 注释
// 单行注释
/*
多行注释
可以跨越多行
*/
// 文档注释(用于生成文档)
// Package main 提供程序入口
package main
// Add 函数用于计算两个整数的和
func Add(a, b int) int {
return a + b
}
4. 数据类型
4.1 基本数据类型
4.1.1 布尔类型
var flag bool = true
var isValid bool = false
// 布尔运算
result := true && false // false
result = true || false // true
result = !true // false
4.1.2 数字类型
// 整数类型
var i8 int8 = 127 // -128 到 127
var i16 int16 = 32767 // -32768 到 32767
var i32 int32 = 2147483647 // -2^31 到 2^31-1
var i64 int64 = 9223372036854775807
var ui8 uint8 = 255 // 0 到 255
var ui16 uint16 = 65535 // 0 到 65535
var ui32 uint32 = 4294967295
var ui64 uint64 = 18446744073709551615
// int和uint:根据平台自动选择32位或64位
var i int = 100
var ui uint = 200
// 特殊整数类型
var b byte = 255 // uint8的别名
var r rune = '中' // int32的别名,表示Unicode码点
// 浮点类型
var f32 float32 = 3.14
var f64 float64 = 3.141592653589793
// 复数类型
var c64 complex64 = 1 + 2i
var c128 complex128 = 2 + 3i
4.1.3 字符串类型
package main
import (
"fmt"
"strings"
)
func main() {
// 字符串声明
var s1 string = "Hello"
s2 := "World"
// 多行字符串
s3 := `这是一个
多行字符串
可以包含换行`
// 字符串连接
s4 := s1 + " " + s2
s5 := fmt.Sprintf("%s %s", s1, s2)
// 字符串长度
length := len(s1) // 字节长度
// 字符串遍历
for i := 0; i < len(s1); i++ {
fmt.Printf("%c ", s1[i]) // 按字节遍历
}
for _, r := range s1 {
fmt.Printf("%c ", r) // 按字符遍历
}
// 字符串操作
contains := strings.Contains(s1, "He") // 包含
hasPrefix := strings.HasPrefix(s1, "He") // 前缀
hasSuffix := strings.HasSuffix(s1, "lo") // 后缀
index := strings.Index(s1, "ll") // 查找
replace := strings.Replace(s1, "l", "L", -1) // 替换
split := strings.Split(s1, "l") // 分割
upper := strings.ToUpper(s1) // 转大写
lower := strings.ToLower(s1) // 转小写
fmt.Println(s4, s5, length, contains, hasPrefix, hasSuffix,
index, replace, split, upper, lower)
}
4.2 复合数据类型
4.2.1 指针
package main
import "fmt"
func main() {
// 声明指针
var p *int
// 获取变量地址
var a int = 10
p = &a
// 解引用
fmt.Println(*p) // 10
// 修改指针指向的值
*p = 20
fmt.Println(a) // 20
// new函数创建指针
ptr := new(int)
*ptr = 100
fmt.Println(*ptr)
// 指针作为函数参数
swap(&a, ptr)
}
func swap(x, y *int) {
temp := *x
*x = *y
*y = temp
}
4.2.2 类型转换
package main
import (
"fmt"
"strconv"
)
func main() {
// 数值类型转换
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
// 字符串转换
// 整数转字符串
str1 := strconv.Itoa(i)
str2 := fmt.Sprintf("%d", i)
// 字符串转整数
num1, err := strconv.Atoi("123")
num2, err := strconv.ParseInt("123", 10, 64)
// 浮点数转字符串
str3 := strconv.FormatFloat(3.14, 'f', 2, 64)
// 字符串转浮点数
f1, err := strconv.ParseFloat("3.14", 64)
// 布尔转换
str4 := strconv.FormatBool(true)
b1, err := strconv.ParseBool("true")
fmt.Println(f, u, str1, str2, num1, num2, str3, f1, str4, b1, err)
}
5. 控制结构
5.1 条件语句
5.1.1 if语句
package main
import "fmt"
func main() {
// 基本if
x := 10
if x > 5 {
fmt.Println("x大于5")
}
// if-else
if x > 15 {
fmt.Println("x大于15")
} else {
fmt.Println("x不大于15")
}
// if-else if-else
if x > 15 {
fmt.Println("x大于15")
} else if x > 10 {
fmt.Println("x大于10")
} else {
fmt.Println("x不大于10")
}
// if简短语句
if y := x * 2; y > 15 {
fmt.Println("y大于15")
}
}
5.1.2 switch语句
package main
import (
"fmt"
"time"
)
func main() {
// 基本switch
day := 3
switch day {
case 1:
fmt.Println("Monday")
case 2:
fmt.Println("Tuesday")
case 3:
fmt.Println("Wednesday")
default:
fmt.Println("Other day")
}
// 多条件switch
switch day {
case 1, 2, 3, 4, 5:
fmt.Println("Weekday")
case 6, 7:
fmt.Println("Weekend")
}
// 无条件switch(相当于if-else链)
score := 85
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
case score >= 70:
fmt.Println("C")
default:
fmt.Println("D")
}
// 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("布尔: %t\n", v)
default:
fmt.Printf("未知类型: %T\n", v)
}
// fallthrough关键字
switch time.Now().Weekday() {
case time.Saturday:
fmt.Println("Today is Saturday")
fallthrough
case time.Sunday:
fmt.Println("Today is weekend")
}
}
5.2 循环语句
package main
import "fmt"
func main() {
// 基本for循环
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// while风格的for循环
j := 0
for j < 5 {
fmt.Println(j)
j++
}
// 无限循环
k := 0
for {
if k >= 5 {
break
}
fmt.Println(k)
k++
}
// for range遍历
// 遍历数组/切片
nums := []int{1, 2, 3, 4, 5}
for index, value := range nums {
fmt.Printf("索引: %d, 值: %d\n", index, value)
}
// 只要索引
for index := range nums {
fmt.Println(index)
}
// 只要值
for _, value := range nums {
fmt.Println(value)
}
// 遍历字符串
str := "Hello"
for index, char := range str {
fmt.Printf("%d: %c\n", index, char)
}
// 遍历map
m := map[string]int{"a": 1, "b": 2, "c": 3}
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
// break和continue
for i := 0; i < 10; i++ {
if i == 3 {
continue // 跳过本次循环
}
if i == 7 {
break // 跳出循环
}
fmt.Println(i)
}
// 标签跳转
OuterLoop:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break OuterLoop // 跳出外层循环
}
fmt.Printf("i=%d, j=%d\n", i, j)
}
}
}
5.3 goto语句
package main
import "fmt"
func main() {
i := 0
Loop:
if i < 5 {
fmt.Println(i)
i++
goto Loop
}
// goto用于错误处理
err := doSomething()
if err != nil {
goto ErrorHandler
}
fmt.Println("Success")
return
ErrorHandler:
fmt.Println("Error occurred")
}
func doSomething() error {
return nil
}
6. 函数
6.1 函数定义
package main
import "fmt"
// 基本函数
func add(a int, 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, fmt.Errorf("除数不能为0")
}
return a / b, nil
}
// 命名返回值
func calculate(a, b int) (sum int, product int) {
sum = a + b
product = a * b
return // 直接return,返回命名的变量
}
// 可变参数
func sum(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
func main() {
// 调用函数
result1 := add(3, 5)
result2 := multiply(4, 6)
result3, err := divide(10, 2)
if err != nil {
fmt.Println(err)
}
s, p := calculate(3, 4)
total := sum(1, 2, 3, 4, 5)
fmt.Println(result1, result2, result3, s, p, total)
}
6.2 函数高级特性
package main
import "fmt"
// 函数作为参数
func apply(f func(int, int) int, a, b int) int {
return f(a, b)
}
// 函数作为返回值
func getOperation(op string) func(int, int) int {
switch op {
case "+":
return func(a, b int) int { return a + b }
case "-":
return func(a, b int) int { return a - b }
case "*":
return func(a, b int) int { return a * b }
default:
return nil
}
}
// 闭包
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
// 递归函数
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n-1)
}
// 匿名函数
func main() {
// 使用函数作为参数
result := apply(func(a, b int) int {
return a + b
}, 3, 5)
// 使用函数作为返回值
addFunc := getOperation("+")
sum := addFunc(10, 20)
// 使用闭包
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
// 递归
fact := factorial(5) // 120
// 匿名函数立即执行
func(msg string) {
fmt.Println(msg)
}("Hello from anonymous function")
fmt.Println(result, sum, fact)
}
6.3 defer语句
package main
import (
"fmt"
"os"
)
func main() {
// defer基本用法(后进先出)
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
fmt.Println("4")
// 输出: 4 3 2 1
// defer用于资源清理
file, err := os.Open("test.txt")
if err != nil {
return
}
defer file.Close() // 确保文件被关闭
// defer参数立即求值
x := 10
defer fmt.Println(x) // 输出10
x = 20
// defer与panic/recover
deferPanicRecover()
}
func deferPanicRecover() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from:", r)
}
}()
panic("Something went wrong!")
}
6.4 init函数
package main
import "fmt"
// init函数在main之前自动执行
// 一个包可以有多个init函数
func init() {
fmt.Println("Init 1")
}
func init() {
fmt.Println("Init 2")
}
func main() {
fmt.Println("Main")
}
// 输出: Init 1, Init 2, Main
7. 数组和切片
7.1 数组
package main
import "fmt"
func main() {
// 数组声明
var arr1 [5]int // 声明长度为5的整数数组
// 数组初始化
var arr2 = [5]int{1, 2, 3, 4, 5}
arr3 := [5]int{1, 2, 3, 4, 5}
arr4 := [...]int{1, 2, 3, 4, 5} // 自动推断长度
arr5 := [5]int{0: 1, 2: 3, 4: 5} // 指定索引初始化
// 访问数组元素
fmt.Println(arr2[0]) // 1
arr2[0] = 10
// 数组长度
length := len(arr2)
// 遍历数组
for i := 0; i < len(arr2); i++ {
fmt.Println(arr2[i])
}
for index, value := range arr2 {
fmt.Printf("索引: %d, 值: %d\n", index, value)
}
// 多维数组
var matrix [3][4]int
matrix2 := [2][3]int{
{1, 2, 3},
{4, 5, 6},
}
// 数组是值类型
arr6 := [3]int{1, 2, 3}
arr7 := arr6 // 复制整个数组
arr7[0] = 100
fmt.Println(arr6[0]) // 1(不受影响)
fmt.Println(arr1, arr3, arr4, arr5, length, matrix, matrix2)
}
7.2 切片
package main
import "fmt"
func main() {
// 切片声明
var slice1 []int // 声明切片,初始值为nil
// 使用make创建切片
slice2 := make([]int, 5) // 长度和容量都是5
slice3 := make([]int, 5, 10) // 长度5,容量10
// 切片字面量
slice4 := []int{1, 2, 3, 4, 5}
// 从数组创建切片
arr := [5]int{1, 2, 3, 4, 5}
slice5 := arr[1:4] // [2, 3, 4]
slice6 := arr[:3] // [1, 2, 3]
slice7 := arr[2:] // [3, 4, 5]
slice8 := arr[:] // [1, 2, 3, 4, 5]
// 切片长度和容量
fmt.Printf("长度: %d, 容量: %d\n", len(slice4), cap(slice4))
// 追加元素
slice4 = append(slice4, 6)
slice4 = append(slice4, 7, 8, 9)
slice4 = append(slice4, []int{10, 11, 12}...)
// 复制切片
slice9 := make([]int, len(slice4))
copy(slice9, slice4)
// 删除元素
// 删除索引为2的元素
index := 2
slice4 = append(slice4[:index], slice4[index+1:]...)
// 插入元素
// 在索引2处插入100
slice4 = append(slice4[:index], append([]int{100}, slice4[index:]...)...)
// 遍历切片
for i, v := range slice4 {
fmt.Printf("索引: %d, 值: %d\n", i, v)
}
// 切片是引用类型
slice10 := []int{1, 2, 3}
slice11 := slice10
slice11[0] = 100
fmt.Println(slice10[0]) // 100(受影响)
fmt.Println(slice1, slice2, slice3, slice5, slice6, slice7, slice8, slice9)
}
7.3 切片高级操作
package main
import (
"fmt"
"sort"
)
func main() {
// 二维切片
matrix := [][]int{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
}
// 动态创建二维切片
rows, cols := 3, 4
matrix2 := make([][]int, rows)
for i := range matrix2 {
matrix2[i] = make([]int, cols)
}
// 切片排序
nums := []int{5, 2, 8, 1, 9, 3}
sort.Ints(nums) // 升序
fmt.Println(nums)
sort.Sort(sort.Reverse(sort.IntSlice(nums))) // 降序
fmt.Println(nums)
// 切片过滤
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
evens := filter(numbers, func(n int) bool {
return n%2 == 0
})
fmt.Println(evens) // [2, 4, 6, 8, 10]
// 切片映射
squared := mapSlice(numbers, func(n int) int {
return n * n
})
fmt.Println(squared)
fmt.Println(matrix, matrix2)
}
// 过滤函数
func filter(slice []int, f func(int) bool) []int {
result := make([]int, 0)
for _, v := range slice {
if f(v) {
result = append(result, v)
}
}
return result
}
// 映射函数
func mapSlice(slice []int, f func(int) int) []int {
result := make([]int, len(slice))
for i, v := range slice {
result[i] = f(v)
}
return result
}
8. Map映射
8.1 Map基础
package main
import "fmt"
func main() {
// Map声明
var map1 map[string]int // 声明但未初始化,值为nil
// 使用make创建Map
map2 := make(map[string]int)
map3 := make(map[string]int, 10) // 指定初始容量
// Map字面量
map4 := map[string]int{
"apple": 1,
"banana": 2,
"orange": 3,
}
// 添加/修改元素
map2["key1"] = 100
map2["key2"] = 200
// 获取元素
value := map2["key1"]
// 检查键是否存在
value, ok := map2["key3"]
if ok {
fmt.Println("存在:", value)
} else {
fmt.Println("不存在")
}
// 删除元素
delete(map2, "key1")
// 遍历Map
for key, value := range map4 {
fmt.Printf("%s: %d\n", key, value)
}
// 只遍历键
for key := range map4 {
fmt.Println(key)
}
// Map长度
length := len(map4)
fmt.Println(map1, map3, length)
}
8.2 Map高级操作
package main
import (
"fmt"
"sync"
)
func main() {
// 嵌套Map
users := map[string]map[string]string{
"user1": {
"name": "Alice",
"email": "alice@example.com",
},
"user2": {
"name": "Bob",
"email": "bob@example.com",
},
}
// 访问嵌套Map
fmt.Println(users["user1"]["name"])
// Map作为集合(Set)
set := make(map[string]bool)
set["item1"] = true
set["item2"] = true
// 检查元素是否在集合中
if set["item1"] {
fmt.Println("item1在集合中")
}
// 并发安全的Map(sync.Map)
var syncMap sync.Map
// 存储
syncMap.Store("key1", "value1")
syncMap.Store("key2", "value2")
// 读取
value, ok := syncMap.Load("key1")
if ok {
fmt.Println(value)
}
// 删除
syncMap.Delete("key1")
// 遍历
syncMap.Range(func(key, value interface{}) bool {
fmt.Printf("%v: %v\n", key, value)
return true // 返回false停止遍历
})
}
9. 结构体
9.1 结构体定义
package main
import "fmt"
// 定义结构体
type Person struct {
Name string
Age int
Email string
}
// 匿名字段
type Student struct {
Person // 嵌入Person结构体
School string
Grade int
}
// 空结构体
type Empty struct{}
func main() {
// 创建结构体实例
// 方式1:字段名初始化
p1 := Person{
Name: "Alice",
Age: 25,
Email: "alice@example.com",
}
// 方式2:按顺序初始化
p2 := Person{"Bob", 30, "bob@example.com"}
// 方式3:部分初始化
p3 := Person{Name: "Charlie"}
// 方式4:使用new
p4 := new(Person)
p4.Name = "David"
// 访问字段
fmt.Println(p1.Name)
p1.Age = 26
// 匿名字段
s := Student{
Person: Person{Name: "Eve", Age: 20},
School: "MIT",
Grade: 3,
}
// 可以直接访问嵌入结构体的字段
fmt.Println(s.Name) // 等同于 s.Person.Name
// 结构体指针
p5 := &Person{Name: "Frank", Age: 35}
fmt.Println(p5.Name) // Go自动解引用
// 结构体比较
p6 := Person{Name: "Alice", Age: 25}
p7 := Person{Name: "Alice", Age: 25}
fmt.Println(p6 == p7) // true
fmt.Println(p2, p3, p4, s)
}
9.2 结构体标签
package main
import (
"encoding/json"
"fmt"
"reflect"
)
// 结构体标签
type User struct {
ID int `json:"id" db:"user_id"`
Username string `json:"username" db:"user_name"`
Email string `json:"email,omitempty" db:"email"`
Password string `json:"-" db:"password"` // json:"-" 表示忽略该字段
}
func main() {
user := User{
ID: 1,
Username: "alice",
Email: "alice@example.com",
Password: "secret",
}
// JSON序列化
jsonData, _ := json.Marshal(user)
fmt.Println(string(jsonData))
// 输出: {"id":1,"username":"alice","email":"alice@example.com"}
// JSON反序列化
jsonStr := `{"id":2,"username":"bob","email":"bob@example.com"}`
var user2 User
json.Unmarshal([]byte(jsonStr), &user2)
fmt.Println(user2)
// 使用反射读取标签
t := reflect.TypeOf(user)
field, _ := t.FieldByName("Username")
fmt.Println(field.Tag.Get("json")) // username
fmt.Println(field.Tag.Get("db")) // user_name
}
10. 方法和接口
10.1 方法
package main
import (
"fmt"
"math"
)
// 定义结构体
type Circle struct {
Radius float64
}
type Rectangle struct {
Width float64
Height float64
}
// 值接收者方法
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// 指针接收者方法
func (c *Circle) Scale(factor float64) {
c.Radius *= factor
}
// Rectangle的方法
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}
func main() {
c := Circle{Radius: 5}
fmt.Println("面积:", c.Area())
c.Scale(2)
fmt.Println("缩放后半径:", c.Radius)
r := Rectangle{Width: 10, Height: 5}
fmt.Println("面积:", r.Area())
r.Scale(2)
fmt.Println("缩放后:", r.Width, r.Height)
}
10.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
}
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// 使用接口作为参数
func PrintShapeInfo(s Shape) {
fmt.Printf("面积: %.2f, 周长: %.2f\n", s.Area(), s.Perimeter())
}
func main() {
c := Circle{Radius: 5}
r := Rectangle{Width: 10, Height: 5}
PrintShapeInfo(c)
PrintShapeInfo(r)
// 接口切片
shapes := []Shape{c, r}
for _, shape := range shapes {
PrintShapeInfo(shape)
}
}
10.3 空接口和类型断言
package main
import "fmt"
func main() {
// 空接口可以存储任何类型
var i interface{}
i = 42
fmt.Println(i)
i = "hello"
fmt.Println(i)
i = []int{1, 2, 3}
fmt.Println(i)
// 类型断言
var x interface{} = "hello"
// 方式1:直接断言
s := x.(string)
fmt.Println(s)
// 方式2:安全断言
s2, ok := x.(string)
if ok {
fmt.Println("是字符串:", s2)
}
// 类型switch
describe(42)
describe("hello")
describe(true)
describe([]int{1, 2, 3})
}
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("整数: %d\n", v)
case string:
fmt.Printf("字符串: %s\n", v)
case bool:
fmt.Printf("布尔: %t\n", v)
default:
fmt.Printf("未知类型: %T\n", v)
}
}
10.4 接口组合
package main
import "fmt"
// 基础接口
type Reader interface {
Read() string
}
type Writer interface {
Write(string)
}
// 组合接口
type ReadWriter interface {
Reader
Writer
}
// 实现组合接口
type File struct {
content string
}
func (f *File) Read() string {
return f.content
}
func (f *File) Write(content string) {
f.content = content
}
func main() {
file := &File{}
// File实现了ReadWriter接口
var rw ReadWriter = file
rw.Write("Hello, World!")
fmt.Println(rw.Read())
}
11. 并发编程
11.1 Goroutine
package main
import (
"fmt"
"time"
)
func sayHello() {
for i := 0; i < 5; i++ {
fmt.Println("Hello", i)
time.Sleep(100 * time.Millisecond)
}
}
func sayWorld() {
for i := 0; i < 5; i++ {
fmt.Println("World", i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
// 启动goroutine
go sayHello()
go sayWorld()
// 匿名goroutine
go func() {
fmt.Println("匿名goroutine")
}()
// 等待goroutine完成
time.Sleep(1 * time.Second)
fmt.Println("主程序结束")
}
11.2 Channel
package main
import "fmt"
func main() {
// 创建channel
ch := make(chan int)
// 发送数据到channel(在goroutine中)
go func() {
ch <- 42 // 发送
}()
// 从channel接收数据
value := <-ch // 接收
fmt.Println(value)
// 带缓冲的channel
bufferedCh := make(chan int, 3)
bufferedCh <- 1
bufferedCh <- 2
bufferedCh <- 3
fmt.Println(<-bufferedCh)
fmt.Println(<-bufferedCh)
fmt.Println(<-bufferedCh)
// 关闭channel
ch2 := make(chan int, 2)
ch2 <- 1
ch2 <- 2
close(ch2)
// 遍历channel
for v := range ch2 {
fmt.Println(v)
}
// 检查channel是否关闭
ch3 := make(chan int, 1)
ch3 <- 100
close(ch3)
v, ok := <-ch3
if ok {
fmt.Println("接收到:", v)
} else {
fmt.Println("channel已关闭")
}
}
11.3 Select语句
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "来自ch1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "来自ch2"
}()
// select等待多个channel操作
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
case <-time.After(3 * time.Second):
fmt.Println("超时")
}
}
// 非阻塞select
ch3 := make(chan int, 1)
select {
case ch3 <- 1:
fmt.Println("发送成功")
default:
fmt.Println("无法发送")
}
}
11.4 WaitGroup
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 完成时调用Done
fmt.Printf("Worker %d 开始\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d 完成\n", id)
}
func main() {
var wg sync.WaitGroup
// 启动5个worker
for i := 1; i <= 5; i++ {
wg.Add(1) // 增加计数
go worker(i, &wg)
}
// 等待所有worker完成
wg.Wait()
fmt.Println("所有worker完成")
}
11.5 Mutex互斥锁
package main
import (
"fmt"
"sync"
)
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
func main() {
counter := &Counter{}
var wg sync.WaitGroup
// 启动1000个goroutine,每个增加计数器
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Increment()
}()
}
wg.Wait()
fmt.Println("最终值:", counter.Value()) // 1000
}
11.6 并发模式
package main
import (
"fmt"
"time"
)
// 生产者-消费者模式
func producer(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i
fmt.Println("生产:", i)
time.Sleep(time.Millisecond * 500)
}
close(ch)
}
func consumer(ch <-chan int) {
for v := range ch {
fmt.Println("消费:", v)
time.Sleep(time.Millisecond * 1000)
}
}
// Worker Pool模式
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("Worker %d 处理任务 %d\n", id, j)
time.Sleep(time.Second)
results <- j * 2
}
}
func workerPool() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// 启动3个worker
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 发送9个任务
for j := 1; j <= 9; j++ {
jobs <- j
}
close(jobs)
// 收集结果
for a := 1; a <= 9; a++ {
<-results
}
}
func main() {
// 生产者-消费者
ch := make(chan int, 2)
go producer(ch)
consumer(ch)
// Worker Pool
workerPool()
}
12. 错误处理
12.1 错误基础
package main
import (
"errors"
"fmt"
)
// 返回错误
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("除数不能为0")
}
return a / b, nil
}
// 使用fmt.Errorf
func validateAge(age int) error {
if age < 0 {
return fmt.Errorf("年龄不能为负数: %d", age)
}
if age > 150 {
return fmt.Errorf("年龄过大: %d", age)
}
return nil
}
func main() {
// 处理错误
result, err := divide(10, 0)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("结果:", result)
}
// 验证年龄
if err := validateAge(-5); err != nil {
fmt.Println(err)
}
}
12.2 自定义错误类型
package main
import "fmt"
// 自定义错误类型
type ValidationError struct {
Field string
Value interface{}
Msg string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("验证错误 [%s]: %v - %s", e.Field, e.Value, e.Msg)
}
func validateUser(name string, age int) error {
if name == "" {
return &ValidationError{
Field: "name",
Value: name,
Msg: "名字不能为空",
}
}
if age < 18 {
return &ValidationError{
Field: "age",
Value: age,
Msg: "年龄必须大于18",
}
}
return nil
}
func main() {
err := validateUser("", 15)
if err != nil {
// 类型断言
if ve, ok := err.(*ValidationError); ok {
fmt.Printf("字段: %s, 值: %v, 消息: %s\n",
ve.Field, ve.Value, ve.Msg)
}
}
}
12.3 错误包装(Go 1.13+)
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("未找到")
func findUser(id int) error {
// 模拟查找失败
return fmt.Errorf("查找用户 %d: %w", id, ErrNotFound)
}
func main() {
err := findUser(123)
// 检查错误
if errors.Is(err, ErrNotFound) {
fmt.Println("用户不存在")
}
// 解包错误
fmt.Println(errors.Unwrap(err))
}
12.4 Panic和Recover
package main
import "fmt"
func riskyOperation() {
defer func() {
if r := recover(); r != nil {
fmt.Println("恢复自panic:", r)
}
}()
fmt.Println("开始危险操作")
panic("出错了!")
fmt.Println("这行不会执行")
}
func main() {
riskyOperation()
fmt.Println("程序继续运行")
}
13. 包管理
13.1 创建包
// mypackage/math.go
package mypackage
// 导出的函数(首字母大写)
func Add(a, b int) int {
return a + b
}
// 未导出的函数(首字母小写)
func subtract(a, b int) int {
return a - b
}
// 导出的常量
const PI = 3.14159
// 未导出的变量
var internalVar = "internal"
13.2 使用包
// main.go
package main
import (
"fmt"
"myproject/mypackage"
)
func main() {
result := mypackage.Add(3, 5)
fmt.Println(result)
fmt.Println(mypackage.PI)
}
13.3 Go Modules
# 初始化模块
go mod init github.com/username/project
# 添加依赖
go get github.com/gin-gonic/gin
# 更新依赖
go get -u github.com/gin-gonic/gin
# 下载依赖
go mod download
# 整理依赖
go mod tidy
# 查看依赖
go list -m all
# 验证依赖
go mod verify
13.4 go.mod文件
module github.com/username/project
go 1.21
require (
github.com/gin-gonic/gin v1.9.1
github.com/go-sql-driver/mysql v1.7.1
)
require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
)
replace github.com/old/package => github.com/new/package v1.0.0
14. 文件操作
14.1 读取文件
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"os"
)
func main() {
// 方式1:一次性读取整个文件
content, err := ioutil.ReadFile("test.txt")
if err != nil {
fmt.Println("错误:", err)
return
}
fmt.Println(string(content))
// 方式2:使用os.Open
file, err := os.Open("test.txt")
if err != nil {
fmt.Println("错误:", err)
return
}
defer file.Close()
// 读取文件内容
data := make([]byte, 100)
count, err := file.Read(data)
if err != nil && err != io.EOF {
fmt.Println("错误:", err)
return
}
fmt.Printf("读取了 %d 字节: %s\n", count, data[:count])
// 方式3:使用bufio逐行读取
file2, _ := os.Open("test.txt")
defer file2.Close()
scanner := bufio.NewScanner(file2)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
14.2 写入文件
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
)
func main() {
// 方式1:一次性写入
content := []byte("Hello, World!\n")
err := ioutil.WriteFile("output.txt", content, 0644)
if err != nil {
fmt.Println("错误:", err)
return
}
// 方式2:使用os.Create
file, err := os.Create("output2.txt")
if err != nil {
fmt.Println("错误:", err)
return
}
defer file.Close()
file.WriteString("第一行\n")
file.Write([]byte("第二行\n"))
// 方式3:使用bufio
writer := bufio.NewWriter(file)
writer.WriteString("第三行\n")
writer.Flush() // 刷新缓冲区
// 追加模式
file2, err := os.OpenFile("output.txt",
os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
fmt.Println("错误:", err)
return
}
defer file2.Close()
file2.WriteString("追加的内容\n")
}
14.3 文件操作
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
// 检查文件是否存在
if _, err := os.Stat("test.txt"); os.IsNotExist(err) {
fmt.Println("文件不存在")
}
// 创建目录
os.Mkdir("testdir", 0755)
os.MkdirAll("path/to/dir", 0755) // 创建多级目录
// 删除文件
os.Remove("test.txt")
// 删除目录
os.RemoveAll("testdir")
// 重命名/移动文件
os.Rename("old.txt", "new.txt")
// 获取文件信息
fileInfo, err := os.Stat("test.txt")
if err == nil {
fmt.Println("文件名:", fileInfo.Name())
fmt.Println("大小:", fileInfo.Size())
fmt.Println("权限:", fileInfo.Mode())
fmt.Println("修改时间:", fileInfo.ModTime())
fmt.Println("是否目录:", fileInfo.IsDir())
}
// 遍历目录
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fmt.Println(path)
return nil
})
// 读取目录
files, err := ioutil.ReadDir(".")
if err == nil {
for _, file := range files {
fmt.Println(file.Name())
}
}
}
15. 网络编程
15.1 HTTP客户端
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
// GET请求
resp, err := http.Get("https://api.github.com")
if err != nil {
fmt.Println("错误:", err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
// POST请求
data := map[string]string{
"name": "Alice",
"email": "alice@example.com",
}
jsonData, _ := json.Marshal(data)
resp2, err := http.Post(
"https://httpbin.org/post",
"application/json",
bytes.NewBuffer(jsonData),
)
if err != nil {
fmt.Println("错误:", err)
return
}
defer resp2.Body.Close()
// 自定义请求
client := &http.Client{}
req, _ := http.NewRequest("GET", "https://api.github.com", nil)
req.Header.Add("Authorization", "Bearer token")
resp3, err := client.Do(req)
if err != nil {
fmt.Println("错误:", err)
return
}
defer resp3.Body.Close()
}
15.2 HTTP服务器
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
// 处理函数
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "欢迎访问首页!")
}
func userHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
user := User{Name: "Alice", Email: "alice@example.com"}
json.NewEncoder(w).Encode(user)
} else if r.Method == "POST" {
var user User
json.NewDecoder(r.Body).Decode(&user)
fmt.Fprintf(w, "创建用户: %s", user.Name)
}
}
func main() {
// 注册路由
http.HandleFunc("/", homeHandler)
http.HandleFunc("/user", userHandler)
// 静态文件服务
fs := http.FileServer(http.Dir("./static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))
// 启动服务器
fmt.Println("服务器启动在 :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
15.3 TCP编程
package main
import (
"bufio"
"fmt"
"net"
)
// TCP服务器
func startTCPServer() {
listener, err := net.Listen("tcp", ":8888")
if err != nil {
fmt.Println("错误:", err)
return
}
defer listener.Close()
fmt.Println("TCP服务器启动在 :8888")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("错误:", err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
message, err := reader.ReadString('\n')
if err != nil {
return
}
fmt.Print("收到消息: ", message)
conn.Write([]byte("收到: " + message))
}
}
// TCP客户端
func startTCPClient() {
conn, err := net.Dial("tcp", "localhost:8888")
if err != nil {
fmt.Println("错误:", err)
return
}
defer conn.Close()
// 发送消息
conn.Write([]byte("Hello, Server!\n"))
// 接收响应
buffer := make([]byte, 1024)
n, _ := conn.Read(buffer)
fmt.Println("收到响应:", string(buffer[:n]))
}
15.4 WebSocket
package main
import (
"fmt"
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func wsHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
defer conn.Close()
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Println(err)
break
}
fmt.Printf("收到: %s\n", message)
err = conn.WriteMessage(messageType, message)
if err != nil {
log.Println(err)
break
}
}
}
func main() {
http.HandleFunc("/ws", wsHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
16. 数据库操作
16.1 MySQL操作
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
type User struct {
ID int
Name string
Email string
}
func main() {
// 连接数据库
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 测试连接
err = db.Ping()
if err != nil {
log.Fatal(err)
}
// 插入数据
result, err := db.Exec("INSERT INTO users (name, email) VALUES (?, ?)",
"Alice", "alice@example.com")
if err != nil {
log.Fatal(err)
}
lastID, _ := result.LastInsertId()
rowsAffected, _ := result.RowsAffected()
fmt.Printf("插入ID: %d, 影响行数: %d\n", lastID, rowsAffected)
// 查询单行
var user User
err = db.QueryRow("SELECT id, name, email FROM users WHERE id = ?", 1).
Scan(&user.ID, &user.Name, &user.Email)
if err != nil {
log.Fatal(err)
}
fmt.Printf("用户: %+v\n", user)
// 查询多行
rows, err := db.Query("SELECT id, name, email FROM users")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var u User
err := rows.Scan(&u.ID, &u.Name, &u.Email)
if err != nil {
log.Fatal(err)
}
fmt.Printf("用户: %+v\n", u)
}
// 更新数据
_, err = db.Exec("UPDATE users SET email = ? WHERE id = ?",
"newemail@example.com", 1)
// 删除数据
_, err = db.Exec("DELETE FROM users WHERE id = ?", 1)
// 事务
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
_, err = tx.Exec("INSERT INTO users (name, email) VALUES (?, ?)",
"Bob", "bob@example.com")
if err != nil {
tx.Rollback()
log.Fatal(err)
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
}
16.2 Redis操作
package main
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
)
var ctx = context.Background()
func main() {
// 创建客户端
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
// 测试连接
pong, err := rdb.Ping(ctx).Result()
fmt.Println(pong, err)
// 字符串操作
err = rdb.Set(ctx, "key", "value", 0).Err()
if err != nil {
panic(err)
}
val, err := rdb.Get(ctx, "key").Result()
if err != nil {
panic(err)
}
fmt.Println("key:", val)
// 设置过期时间
err = rdb.Set(ctx, "key2", "value2", 10*time.Second).Err()
// 列表操作
rdb.RPush(ctx, "list", "item1", "item2", "item3")
items, _ := rdb.LRange(ctx, "list", 0, -1).Result()
fmt.Println("列表:", items)
// 哈希操作
rdb.HSet(ctx, "user:1", "name", "Alice")
rdb.HSet(ctx, "user:1", "email", "alice@example.com")
name, _ := rdb.HGet(ctx, "user:1", "name").Result()
fmt.Println("用户名:", name)
// 集合操作
rdb.SAdd(ctx, "set", "member1", "member2", "member3")
members, _ := rdb.SMembers(ctx, "set").Result()
fmt.Println("集合:", members)
// 有序集合
rdb.ZAdd(ctx, "scores", &redis.Z{Score: 90, Member: "Alice"})
rdb.ZAdd(ctx, "scores", &redis.Z{Score: 85, Member: "Bob"})
scores, _ := rdb.ZRangeWithScores(ctx, "scores", 0, -1).Result()
fmt.Println("排行榜:", scores)
}
16.3 MongoDB操作
package main
import (
"context"
"fmt"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
Name string `bson:"name"`
Email string `bson:"email"`
Age int `bson:"age"`
}
func main() {
// 连接MongoDB
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, options.Client().
ApplyURI("mongodb://localhost:27017"))
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(ctx)
// 选择数据库和集合
collection := client.Database("testdb").Collection("users")
// 插入单个文档
user := User{Name: "Alice", Email: "alice@example.com", Age: 25}
result, err := collection.InsertOne(ctx, user)
if err != nil {
log.Fatal(err)
}
fmt.Println("插入ID:", result.InsertedID)
// 插入多个文档
users := []interface{}{
User{Name: "Bob", Email: "bob@example.com", Age: 30},
User{Name: "Charlie", Email: "charlie@example.com", Age: 35},
}
results, _ := collection.InsertMany(ctx, users)
fmt.Println("插入IDs:", results.InsertedIDs)
// 查询单个文档
var foundUser User
err = collection.FindOne(ctx, bson.M{"name": "Alice"}).Decode(&foundUser)
if err != nil {
log.Fatal(err)
}
fmt.Printf("找到用户: %+v\n", foundUser)
// 查询多个文档
cursor, err := collection.Find(ctx, bson.M{"age": bson.M{"$gte": 25}})
if err != nil {
log.Fatal(err)
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var u User
cursor.Decode(&u)
fmt.Printf("用户: %+v\n", u)
}
// 更新文档
update := bson.M{"$set": bson.M{"age": 26}}
_, err = collection.UpdateOne(ctx, bson.M{"name": "Alice"}, update)
// 删除文档
_, err = collection.DeleteOne(ctx, bson.M{"name": "Bob"})
}
17. 测试
17.1 单元测试
// math.go
package math
func Add(a, b int) int {
return a + b
}
func Subtract(a, b int) int {
return a - b
}
func Multiply(a, b int) int {
return a * b
}
func Divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("除数不能为0")
}
return a / b, nil
}
// math_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; 期望 %d", result, expected)
}
}
func TestSubtract(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"正数", 5, 3, 2},
{"负数", 3, 5, -2},
{"零", 5, 5, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Subtract(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Subtract(%d, %d) = %d; 期望 %d",
tt.a, tt.b, result, tt.expected)
}
})
}
}
func TestDivide(t *testing.T) {
// 正常情况
result, err := Divide(10, 2)
if err != nil {
t.Errorf("意外错误: %v", err)
}
if result != 5 {
t.Errorf("Divide(10, 2) = %d; 期望 5", result)
}
// 除以零
_, err = Divide(10, 0)
if err == nil {
t.Error("期望错误,但没有返回错误")
}
}
17.2 基准测试
// math_test.go
package math
import "testing"
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
func BenchmarkMultiply(b *testing.B) {
for i := 0; i < b.N; i++ {
Multiply(2, 3)
}
}
运行测试:
# 运行所有测试
go test
# 运行详细测试
go test -v
# 运行特定测试
go test -run TestAdd
# 运行基准测试
go test -bench=.
# 生成覆盖率报告
go test -cover
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
17.3 示例测试
// math_test.go
package math
import "fmt"
func ExampleAdd() {
result := Add(2, 3)
fmt.Println(result)
// Output: 5
}
func ExampleSubtract() {
result := Subtract(5, 3)
fmt.Println(result)
// Output: 2
}
17.4 Mock测试
package main
import (
"testing"
)
// 接口
type Database interface {
GetUser(id int) (string, error)
}
// Mock实现
type MockDatabase struct {
GetUserFunc func(id int) (string, error)
}
func (m *MockDatabase) GetUser(id int) (string, error) {
return m.GetUserFunc(id)
}
// 被测试的函数
func GetUserName(db Database, id int) string {
name, err := db.GetUser(id)
if err != nil {
return "Unknown"
}
return name
}
// 测试
func TestGetUserName(t *testing.T) {
mockDB := &MockDatabase{
GetUserFunc: func(id int) (string, error) {
if id == 1 {
return "Alice", nil
}
return "", errors.New("用户不存在")
},
}
name := GetUserName(mockDB, 1)
if name != "Alice" {
t.Errorf("期望 Alice, 得到 %s", name)
}
name = GetUserName(mockDB, 999)
if name != "Unknown" {
t.Errorf("期望 Unknown, 得到 %s", name)
}
}
18. 性能优化
18.1 性能分析
package main
import (
"fmt"
"os"
"runtime/pprof"
"time"
)
func fibonacci(n int) int {
if n <= 1 {
return n
}
return fibonacci(n-1) + fibonacci(n-2)
}
func main() {
// CPU性能分析
cpuFile, _ := os.Create("cpu.prof")
pprof.StartCPUProfile(cpuFile)
defer pprof.StopCPUProfile()
// 执行需要分析的代码
for i := 0; i < 30; i++ {
fibonacci(i)
}
// 内存性能分析
memFile, _ := os.Create("mem.prof")
defer memFile.Close()
pprof.WriteHeapProfile(memFile)
}
分析性能数据:
# 分析CPU性能
go tool pprof cpu.prof
# 分析内存性能
go tool pprof mem.prof
# Web界面查看
go tool pprof -http=:8080 cpu.prof
18.2 优化技巧
package main
import (
"fmt"
"strings"
"sync"
)
// 1. 使用strings.Builder代替字符串拼接
func stringConcat() {
var builder strings.Builder
for i := 0; i < 1000; i++ {
builder.WriteString("hello")
}
result := builder.String()
fmt.Println(len(result))
}
// 2. 预分配切片容量
func slicePrealloc() {
// 好的做法
slice := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {
slice = append(slice, i)
}
}
// 3. 使用sync.Pool复用对象
var bufferPool = sync.Pool{
New: func() interface{} {
return new(strings.Builder)
},
}
func usePool() {
builder := bufferPool.Get().(*strings.Builder)
defer func() {
builder.Reset()
bufferPool.Put(builder)
}()
builder.WriteString("使用对象池")
}
// 4. 避免不必要的内存分配
func avoidAlloc() {
// 使用值类型而不是指针
type Point struct {
X, Y int
}
points := make([]Point, 1000)
for i := range points {
points[i] = Point{X: i, Y: i * 2}
}
}
// 5. 并发优化
func concurrentProcess() {
var wg sync.WaitGroup
jobs := make(chan int, 100)
results := make(chan int, 100)
// 启动worker池
for w := 0; w < 10; w++ {
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
results <- job * 2
}
}()
}
// 发送任务
go func() {
for i := 0; i < 100; i++ {
jobs <- i
}
close(jobs)
}()
// 等待完成
go func() {
wg.Wait()
close(results)
}()
// 收集结果
for result := range results {
_ = result
}
}
18.3 内存优化
package main
import "runtime"
func main() {
// 手动触发GC
runtime.GC()
// 设置GC百分比
runtime.SetGCPercent(100)
// 获取内存统计
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("分配的内存: %v MB\n", m.Alloc/1024/1024)
fmt.Printf("总分配的内存: %v MB\n", m.TotalAlloc/1024/1024)
fmt.Printf("系统内存: %v MB\n", m.Sys/1024/1024)
fmt.Printf("GC次数: %v\n", m.NumGC)
}
19. 实战项目
19.1 RESTful API服务
package main
import (
"encoding/json"
"log"
"net/http"
"strconv"
"github.com/gorilla/mux"
)
type Book struct {
ID int `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
}
var books []Book
var nextID = 1
func getBooks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(books)
}
func getBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
id, _ := strconv.Atoi(params["id"])
for _, book := range books {
if book.ID == id {
json.NewEncoder(w).Encode(book)
return
}
}
http.Error(w, "Book not found", http.StatusNotFound)
}
func createBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var book Book
json.NewDecoder(r.Body).Decode(&book)
book.ID = nextID
nextID++
books = append(books, book)
json.NewEncoder(w).Encode(book)
}
func updateBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
id, _ := strconv.Atoi(params["id"])
for i, book := range books {
if book.ID == id {
books = append(books[:i], books[i+1:]...)
var updatedBook Book
json.NewDecoder(r.Body).Decode(&updatedBook)
updatedBook.ID = id
books = append(books, updatedBook)
json.NewEncoder(w).Encode(updatedBook)
return
}
}
http.Error(w, "Book not found", http.StatusNotFound)
}
func deleteBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
id, _ := strconv.Atoi(params["id"])
for i, book := range books {
if book.ID == id {
books = append(books[:i], books[i+1:]...)
json.NewEncoder(w).Encode(map[string]string{"message": "Book deleted"})
return
}
}
http.Error(w, "Book not found", http.StatusNotFound)
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/api/books", getBooks).Methods("GET")
router.HandleFunc("/api/books/{id}", getBook).Methods("GET")
router.HandleFunc("/api/books", createBook).Methods("POST")
router.HandleFunc("/api/books/{id}", updateBook).Methods("PUT")
router.HandleFunc("/api/books/{id}", deleteBook).Methods("DELETE")
log.Println("服务器启动在 :8000")
log.Fatal(http.ListenAndServe(":8000", router))
}
19.2 命令行工具
package main
import (
"flag"
"fmt"
"os"
)
func main() {
// 定义命令行参数
var (
name = flag.String("name", "World", "要问候的名字")
age = flag.Int("age", 0, "年龄")
verbose = flag.Bool("verbose", false, "详细输出")
)
flag.Parse()
if *verbose {
fmt.Printf("名字: %s\n", *name)
fmt.Printf("年龄: %d\n", *age)
}
fmt.Printf("Hello, %s!\n", *name)
// 处理非标志参数
args := flag.Args()
if len(args) > 0 {
fmt.Println("其他参数:", args)
}
}
19.3 Web爬虫
package main
import (
"fmt"
"log"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
)
func scrape(url string) {
// 发送HTTP请求
res, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
log.Fatalf("状态码错误: %d %s", res.StatusCode, res.Status)
}
// 解析HTML
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
log.Fatal(err)
}
// 提取数据
doc.Find("a").Each(func(i int, s *goquery.Selection) {
href, exists := s.Attr("href")
if exists {
text := strings.TrimSpace(s.Text())
fmt.Printf("链接: %s, 文本: %s\n", href, text)
}
})
}
func main() {
scrape("https://example.com")
}
20. 进阶主题
20.1 反射
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
p := Person{Name: "Alice", Age: 25}
// 获取类型
t := reflect.TypeOf(p)
fmt.Println("类型:", t)
fmt.Println("类型名:", t.Name())
fmt.Println("类型种类:", t.Kind())
// 获取值
v := reflect.ValueOf(p)
fmt.Println("值:", v)
// 遍历字段
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("字段: %s, 类型: %s, 值: %v, 标签: %s\n",
field.Name, field.Type, value, field.Tag.Get("json"))
}
// 修改值(需要指针)
pv := reflect.ValueOf(&p)
pv.Elem().FieldByName("Age").SetInt(26)
fmt.Println("修改后:", p)
// 调用方法
method := v.MethodByName("String")
if method.IsValid() {
result := method.Call(nil)
fmt.Println(result)
}
}

1458

被折叠的 条评论
为什么被折叠?



