Go学习
本文由ai生成
文章目录
1. 环境搭建
1.1 下载安装
# 访问官网下载:https://go.dev/dl/
# 安装后验证
go version
1.2 配置编辑器
- 安装 VS Code
- 安装 Go 插件(在扩展中搜索 “Go”)
- VS Code 会自动提示安装所需工具,点击 “Install All”
1.3 第一个程序
// hello.go
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
# 运行方式1:直接运行
go run hello.go
# 运行方式2:编译后运行
go build hello.go
./hello
2. 基础语法
2.1 变量声明
package main
import "fmt"
func main() {
// 方式1:自动推断类型(最常用)
a := 1
// 方式2:var + 自动推断
var b = 2
// 方式3:先声明后赋值(必须指定类型)
var c int
c = 3
// 方式4:一次声明多个
var x, y, z int = 1, 2, 3
m, n := 10, 20
// 注意:变量声明后必须使用,否则编译报错
// 可以用 _ 忽略不用的变量
_, value := 100, 200
fmt.Println(a, b, c, x, y, z, m, n, value)
}
2.2 常量
const PI = 3.14
const (
StatusOK = 200
StatusNotFound = 404
)
2.3 输入输出
package main
import "fmt"
func main() {
// 输出
fmt.Println("Hello") // 自动换行
fmt.Print("Hello") // 不换行
fmt.Printf("数字: %d\n", 42) // 格式化输出
// 输入
var name string
fmt.Print("请输入姓名: ")
fmt.Scan(&name) // 注意要传地址
fmt.Printf("你好, %s!\n", name)
}
2.4 注释
// 单行注释
/*
多行注释
可以写多行
*/
3. 数据类型
3.1 基本类型
package main
import "fmt"
func main() {
// 布尔类型
var isTrue bool = true
// 整数类型
var age int = 25
var small int8 = 127 // -128 到 127
var big uint64 = 123456789 // 无符号整数
var b byte = 255 // byte 是 uint8 的别名
var r rune = '中' // rune 是 int32 的别名,用于表示 Unicode
// 浮点类型
var pi float32 = 3.14
var e float64 = 2.718281828
// 字符串
var name string = "张三"
str := "Hello" + " " + "World" // 字符串拼接
// 复数(不常用)
var complex1 complex64 = 1 + 2i
fmt.Println(isTrue, age, small, big, b, r, pi, e, name, str, complex1)
}
3.2 类型转换
package main
import (
"fmt"
"strconv"
)
func main() {
// 数值类型转换(必须显式转换)
var a int = 10
var b float64 = float64(a)
var c int = int(b)
// 字符串转数字
num, err := strconv.Atoi("123")
if err != nil {
fmt.Println("转换失败")
}
// 数字转字符串
str := strconv.Itoa(456)
// 字符串转浮点数
f, _ := strconv.ParseFloat("3.14", 64)
// 布尔值转换
boolVal, _ := strconv.ParseBool("true")
fmt.Println(a, b, c, num, str, f, boolVal)
}
3.3 格式化符号速查表
| 符号 | 说明 | 示例 |
|---|---|---|
%v | 默认格式 | fmt.Printf("%v", 42) |
%+v | 结构体带字段名 | fmt.Printf("%+v", person) |
%#v | Go语法格式 | fmt.Printf("%#v", []int{1,2}) |
%T | 类型 | fmt.Printf("%T", 42) → int |
%t | 布尔值 | fmt.Printf("%t", true) |
%d | 十进制整数 | fmt.Printf("%d", 42) |
%b | 二进制 | fmt.Printf("%b", 5) → 101 |
%x | 十六进制(小写) | fmt.Printf("%x", 255) → ff |
%f | 浮点数 | fmt.Printf("%.2f", 3.14159) → 3.14 |
%s | 字符串 | fmt.Printf("%s", "hello") |
%q | 带引号字符串 | fmt.Printf("%q", "hello") → "hello" |
%p | 指针地址 | fmt.Printf("%p", &a) |
4. 运算符与控制流
4.1 运算符
package main
import "fmt"
func main() {
a, b := 10, 3
// 算术运算符
fmt.Println(a + b) // 13
fmt.Println(a - b) // 7
fmt.Println(a * b) // 30
fmt.Println(a / b) // 3 (整数除法)
fmt.Println(a % b) // 1 (取余)
// 关系运算符
fmt.Println(a == b) // false
fmt.Println(a != b) // true
fmt.Println(a > b) // true
fmt.Println(a >= b) // true
// 逻辑运算符
fmt.Println(true && false) // false (与)
fmt.Println(true || false) // true (或)
fmt.Println(!true) // false (非)
// 自增自减(只能后置)
a++
b--
fmt.Println(a, b) // 11 2
// 赋值运算符
c := 10
c += 5 // c = c + 5
c -= 2 // c = c - 2
c *= 2 // c = c * 2
fmt.Println(c) // 26
}
4.2 if-else
package main
import "fmt"
func main() {
score := 85
// 基本 if-else
if score >= 90 {
fmt.Println("优秀")
} else if score >= 60 {
fmt.Println("及格")
} else {
fmt.Println("不及格")
}
// if 可以包含初始化语句
if age := 20; age >= 18 {
fmt.Println("成年人")
} // 注意:age 只在 if 块内有效
}
4.3 switch
package main
import "fmt"
func main() {
day := 3
// 基本 switch
switch day {
case 1:
fmt.Println("星期一")
case 2:
fmt.Println("星期二")
case 3:
fmt.Println("星期三")
default:
fmt.Println("其他")
}
// 多条件匹配
switch day {
case 1, 2, 3, 4, 5:
fmt.Println("工作日")
case 6, 7:
fmt.Println("周末")
}
// 无表达式的 switch
score := 85
switch {
case score >= 90:
fmt.Println("优秀")
case score >= 60:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
// switch 初始化
switch grade := "B"; grade {
case "A":
fmt.Println("90-100")
case "B":
fmt.Println("80-89")
default:
fmt.Println("其他")
}
}
4.4 for 循环
package main
import "fmt"
func main() {
// 标准 for 循环
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// while 风格(Go 没有 while 关键字)
j := 0
for j < 5 {
fmt.Println(j)
j++
}
// 无限循环
count := 0
for {
count++
if count > 3 {
break // 跳出循环
}
if count == 2 {
continue // 跳过本次
}
fmt.Println(count)
}
// range 遍历
nums := []int{10, 20, 30}
for index, value := range nums {
fmt.Printf("索引%d: 值%d\n", index, value)
}
// 只要值
for _, value := range nums {
fmt.Println(value)
}
// 只要索引
for index := range nums {
fmt.Println(index)
}
// 遍历字符串
for i, char := range "Hello" {
fmt.Printf("%d: %c\n", i, char)
}
}
5. 函数
5.1 基本函数
package main
import "fmt"
// 无返回值
func sayHello() {
fmt.Println("Hello")
}
// 有参数和返回值
func add(a int, b int) int {
return a + b
}
// 参数类型相同可以简写
func multiply(a, b int) int {
return a * b
}
// 多返回值
func swap(a, b int) (int, int) {
return b, a
}
// 命名返回值
func divide(a, b float64) (result float64, err error) {
if b == 0 {
err = fmt.Errorf("除数不能为0")
return // 自动返回 result 和 err
}
result = a / b
return
}
func main() {
sayHello()
sum := add(3, 5)
fmt.Println(sum)
x, y := swap(1, 2)
fmt.Println(x, y)
result, err := divide(10, 2)
if err != nil {
fmt.Println("错误:", err)
} else {
fmt.Println("结果:", result)
}
}
5.2 可变参数
package main
import "fmt"
// ...表示可变参数,类型是切片
func sum(numbers ...int) int {
total := 0
for _, num := range numbers {
total += num
}
return total
}
func main() {
fmt.Println(sum(1, 2, 3)) // 6
fmt.Println(sum(1, 2, 3, 4, 5)) // 15
// 传递切片需要展开
nums := []int{10, 20, 30}
fmt.Println(sum(nums...)) // 60
}
5.3 匿名函数与闭包
package main
import "fmt"
func main() {
// 匿名函数
add := func(a, b int) int {
return a + b
}
fmt.Println(add(3, 5)) // 8
// 立即执行的匿名函数
func(msg string) {
fmt.Println(msg)
}("Hello!")
// 闭包:函数可以访问外部变量
counter := makeCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
fmt.Println(counter()) // 3
}
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
5.4 函数作为参数
package main
import "fmt"
// 函数类型作为参数
func apply(a, b int, operation func(int, int) int) int {
return operation(a, b)
}
func main() {
add := func(x, y int) int { return x + y }
multiply := func(x, y int) int { return x * y }
fmt.Println(apply(3, 5, add)) // 8
fmt.Println(apply(3, 5, multiply)) // 15
}
6. 数组与切片
6.1 数组(长度固定)
package main
import "fmt"
func main() {
// 声明数组
var arr1 [5]int // [0 0 0 0 0]
// 初始化
arr2 := [5]int{1, 2, 3, 4, 5}
arr3 := [...]int{1, 2, 3} // 自动计算长度
arr4 := [5]int{1: 10, 3: 20} // 指定索引赋值: [0 10 0 20 0]
// 访问和修改
arr2[0] = 100
fmt.Println(arr2[0])
// 长度
fmt.Println(len(arr2)) // 5
// 遍历
for i := 0; i < len(arr2); i++ {
fmt.Println(arr2[i])
}
for index, value := range arr2 {
fmt.Printf("arr2[%d] = %d\n", index, value)
}
fmt.Println(arr1, arr3, arr4)
}
6.2 切片(长度可变)
package main
import "fmt"
func main() {
// 创建切片的三种方式
// 方式1:直接初始化
slice1 := []int{1, 2, 3, 4, 5}
// 方式2:make创建
slice2 := make([]int, 3) // 长度3,容量3
slice3 := make([]int, 3, 5) // 长度3,容量5
// 方式3:从数组切取
arr := [5]int{1, 2, 3, 4, 5}
slice4 := arr[1:4] // [2 3 4],左闭右开
slice5 := arr[:3] // [1 2 3],省略起始索引默认0
slice6 := arr[2:] // [3 4 5],省略结束索引默认到末尾
fmt.Println(slice1, slice2, slice3, slice4, slice5, slice6)
}
6.3 切片操作
package main
import "fmt"
func main() {
s := []int{1, 2, 3}
// 添加元素
s = append(s, 4) // [1 2 3 4]
s = append(s, 5, 6, 7) // [1 2 3 4 5 6 7]
// 长度和容量
fmt.Printf("len=%d cap=%d\n", len(s), cap(s))
// 切片拼接
s2 := []int{10, 20, 30}
s = append(s, s2...) // 注意...展开
fmt.Println(s)
// 删除元素(删除索引2的元素)
s = []int{1, 2, 3, 4, 5}
s = append(s[:2], s[3:]...) // [1 2 4 5]
fmt.Println(s)
// 复制切片
original := []int{1, 2, 3}
copied := make([]int, len(original))
copy(copied, original)
copied[0] = 999
fmt.Println("original:", original) // [1 2 3]
fmt.Println("copied:", copied) // [999 2 3]
}
6.4 切片陷阱(重要!)
package main
import "fmt"
func main() {
// 陷阱1:切片共享底层数组
arr := []int{1, 2, 3, 4, 5}
s1 := arr[0:3] // [1 2 3]
s2 := arr[2:5] // [3 4 5]
s1[2] = 999 // 修改s1影响了s2
fmt.Println(arr) // [1 2 999 4 5]
fmt.Println(s1) // [1 2 999]
fmt.Println(s2) // [999 4 5]
// 陷阱2:append可能导致重新分配
s3 := make([]int, 2, 3) // len=2, cap=3
s4 := s3
s3 = append(s3, 10) // 容量够,不会重新分配
s4[0] = 999
fmt.Println(s3) // [999 0 10],受影响
s3 = append(s3, 20, 30) // 超过容量,重新分配
s4[0] = 111
fmt.Println(s3) // [999 0 10 20 30],不受影响
}
7. Map映射
7.1 创建和基本操作
package main
import "fmt"
func main() {
// 创建方式1:字面量
scores := map[string]int{
"Alice": 90,
"Bob": 85,
}
// 创建方式2:make
ages := make(map[string]int)
// 添加/修改
ages["Charlie"] = 25
ages["Diana"] = 30
// 访问
fmt.Println(scores["Alice"]) // 90
// 检查键是否存在(重要!)
score, exists := scores["Eve"]
if exists {
fmt.Println("Eve的分数:", score)
} else {
fmt.Println("Eve不存在")
}
// 删除
delete(scores, "Bob")
// 长度
fmt.Println("map长度:", len(scores))
// 遍历(顺序不固定)
for name, score := range scores {
fmt.Printf("%s: %d\n", name, score)
}
}
7.2 Map进阶操作
package main
import (
"fmt"
"sort"
)
func main() {
// 有序遍历:先取键排序
m := map[string]int{
"c": 3,
"a": 1,
"b": 2,
}
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Printf("%s: %d\n", k, m[k])
}
// Map的值是切片(一对多)
graph := make(map[string][]string)
graph["A"] = append(graph["A"], "B", "C")
graph["B"] = append(graph["B"], "D")
fmt.Println(graph) // map[A:[B C] B:[D]]
// 用Map模拟Set
set := make(map[string]struct{})
set["apple"] = struct{}{}
set["banana"] = struct{}{}
if _, exists := set["apple"]; exists {
fmt.Println("apple在集合中")
}
}
8. 指针
8.1 基本概念
package main
import "fmt"
func main() {
a := 42
// &取地址,*解引用
p := &a // p是指向a的指针
fmt.Println(p) // 0xc000012090(地址)
fmt.Println(*p) // 42(解引用得到值)
*p = 100 // 通过指针修改a
fmt.Println(a) // 100
// new函数:分配内存并返回指针
q := new(int) // q是*int,值为0
*q = 200
fmt.Println(*q) // 200
// 没有指针运算(安全)
// p++ // 这是不允许的
}
8.2 值传递 vs 指针传递
package main
import "fmt"
// 值传递:不影响原变量
func modifyValue(x int) {
x = 100
}
// 指针传递:影响原变量
func modifyPointer(x *int) {
*x = 100
}
func main() {
a := 1
modifyValue(a)
fmt.Println(a) // 1,没变
b := 1
modifyPointer(&b)
fmt.Println(b) // 100,改变了
}
8.3 什么时候用指针
package main
import "fmt"
type BigStruct struct {
Data [1000000]int
}
// 值传递:复制整个结构体,慢且占内存
func processByValue(s BigStruct) {
s.Data[0] = 1 // 不影响原数据
}
// 指针传递:只复制一个指针,快
func processByPointer(s *BigStruct) {
s.Data[0] = 1 // 修改原数据
}
func main() {
s := BigStruct{}
// 规则:
// 1. 需要修改原变量 → 用指针
// 2. 结构体很大 → 用指针
// 3. 小的基本类型 → 用值传递
processByPointer(&s)
fmt.Println(s.Data[0]) // 1
}
9. 结构体
9.1 基本定义
package main
import "fmt"
// 定义结构体
type Person struct {
Name string
Age int
}
type Student struct {
Name string
Age int
Grade int
}
func main() {
// 创建方式1:按字段顺序
p1 := Person{"Alice", 25}
// 创建方式2:指定字段名(推荐)
p2 := Person{
Name: "Bob",
Age: 30,
}
// 创建方式3:零值初始化
var p3 Person // {Name:"" Age:0}
// 创建方式4:指针
p4 := &Person{Name: "Charlie", Age: 35}
// 访问字段
fmt.Println(p1.Name)
fmt.Println(p4.Age) // 指针自动解引用,不需要(*p4).Age
// 修改字段
p1.Age = 26
p4.Name = "Charles"
fmt.Println(p1, p2, p3, p4)
}
9.2 匿名字段与嵌套
package main
import "fmt"
type Address struct {
City string
Street string
}
type Person struct {
Name string
Age int
Address // 匿名嵌套
}
func main() {
p := Person{
Name: "Alice",
Age: 25,
Address: Address{
City: "北京",
Street: "长安街",
},
}
// 访问嵌套字段
fmt.Println(p.Address.City) // 完整路径
fmt.Println(p.City) // 字段提升,可以直接访问
}
9.3 结构体标签(用于JSON等)
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Password string `json:"-"` // 忽略此字段
Email string `json:"email,omitempty"` // 空值不输出
}
func main() {
u := User{
ID: 1,
Username: "alice",
Password: "secret",
Email: "",
}
data, _ := json.Marshal(u)
fmt.Println(string(data))
// {"id":1,"username":"alice"}
// 注意:Password被忽略,Email为空不输出
}
10. 方法
10.1 基本方法
package main
import (
"fmt"
"math"
)
type Point struct {
X, Y float64
}
// 值接收者:不修改原结构体
func (p Point) Distance() float64 {
return math.Sqrt(p.X*p.X + p.Y*p.Y)
}
// 指针接收者:可以修改原结构体
func (p *Point) Move(dx, dy float64) {
p.X += dx
p.Y += dy
}
func main() {
p := Point{X: 3, Y: 4}
fmt.Println(p.Distance()) // 5
p.Move(1, 1)
fmt.Println(p) // {4 5}
}
10.2 何时用指针接收者
package main
import "fmt"
type Counter struct {
count int
}
// 规则:
// 1. 需要修改接收者 → 用指针
// 2. 接收者很大 → 用指针
// 3. 保持一致性:如果有一个方法用指针,其他也用指针
func (c *Counter) Increment() {
c.count++
}
func (c *Counter) Value() int {
return c.count
}
func main() {
c := Counter{}
c.Increment()
c.Increment()
fmt.Println(c.Value()) // 2
}
11. 接口
11.1 基本概念
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, 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: 3, Height: 4}
printShapeInfo(c) // 面积: 78.54, 周长: 31.42
printShapeInfo(r) // 面积: 12.00, 周长: 14.00
}
11.2 空接口
package main
import "fmt"
// interface{} 或 any 可以接收任何类型
func printAnything(v interface{}) {
fmt.Println(v)
}
func main() {
printAnything(42)
printAnything("hello")
printAnything([]int{1, 2, 3})
// Go 1.18+ 可以用 any 代替 interface{}
var x any = "world"
fmt.Println(x)
}
11.3 类型断言
package main
import "fmt"
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14 * c.Radius * c.Radius
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func describe(s Shape) {
// 方式1:单个类型断言
if c, ok := s.(Circle); ok {
fmt.Printf("圆,半径=%.2f\n", c.Radius)
return
}
// 方式2:type switch
switch v := s.(type) {
case Circle:
fmt.Printf("圆,半径=%.2f\n", v.Radius)
case Rectangle:
fmt.Printf("矩形,宽=%.2f,高=%.2f\n", v.Width, v.Height)
default:
fmt.Println("未知形状")
}
}
func main() {
shapes := []Shape{
Circle{Radius: 5},
Rectangle{Width: 3, Height: 4},
}
for _, s := range shapes {
describe(s)
}
}
11.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(s string) {
f.content = s
}
func main() {
var rw ReadWriter = &File{}
rw.Write("Hello")
fmt.Println(rw.Read())
}
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
}
func main() {
result, err := divide(10, 0)
if err != nil {
fmt.Println("错误:", err)
return
}
fmt.Println("结果:", result)
}
12.2 自定义错误
package main
import "fmt"
// 实现error接口
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("字段'%s'验证失败: %s", e.Field, e.Message)
}
func validateAge(age int) error {
if age < 0 {
return &ValidationError{
Field: "age",
Message: "年龄不能为负数",
}
}
if age > 150 {
return &ValidationError{
Field: "age",
Message: "年龄不合理",
}
}
return nil
}
func main() {
if err := validateAge(-5); err != nil {
fmt.Println(err) // 字段'age'验证失败: 年龄不能为负数
}
}
12.3 错误包装(Go 1.13+)
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found")
func findUser(id int) error {
if id > 100 {
return ErrNotFound
}
return nil
}
func getUser(id int) error {
err := findUser(id)
if err != nil {
return fmt.Errorf("getUser失败: %w", err) // %w包装错误
}
return nil
}
func main() {
err := getUser(200)
if err != nil {
fmt.Println(err)
// errors.Is:检查错误链
if errors.Is(err, ErrNotFound) {
fmt.Println("用户未找到")
}
}
}
12.4 panic 和 recover
package main
import "fmt"
func safeDivide(a, b int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("捕获panic:", r)
}
}()
result := a / b // b=0时会panic
fmt.Println("结果:", result)
}
func main() {
safeDivide(10, 0) // 捕获panic: runtime error: integer divide by zero
fmt.Println("程序继续运行")
}
13. defer关键字
13.1 基本用法
package main
import "fmt"
func main() {
// defer延迟执行,按LIFO顺序
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
fmt.Println("主函数")
// 输出:
// 主函数
// 3
// 2
// 1
}
13.2 资源清理
package main
import (
"fmt"
"os"
)
func writeFile(filename, content string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close() // 确保函数结束时关闭文件
_, err = f.WriteString(content)
return err
}
func main() {
err := writeFile("test.txt", "Hello World")
if err != nil {
fmt.Println("写入失败:", err)
} else {
fmt.Println("写入成功")
}
}
13.3 defer的陷阱
package main
import "fmt"
func main() {
// 陷阱1:defer注册时就确定参数值
x := 0
defer fmt.Println("值拷贝:", x) // 输出: 值拷贝: 0
x = 10
// 陷阱2:闭包捕获变量
y := 0
defer func() {
fmt.Println("闭包:", y) // 输出: 闭包: 10
}()
y = 10
// 输出顺序(LIFO):
// 闭包: 10
// 值拷贝: 0
}
14. 并发编程
14.1 Goroutine基础
package main
import (
"fmt"
"time"
)
func sayHello(name string) {
for i := 0; i < 3; i++ {
fmt.Printf("%s: Hello %d\n", name, i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
// 启动goroutine
go sayHello("goroutine-1")
go sayHello("goroutine-2")
// 主函数也执行
sayHello("main")
// 等待goroutine完成(简单方式)
time.Sleep(500 * time.Millisecond)
}
14.2 WaitGroup
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 完成时计数-1
fmt.Printf("Worker %d 开始\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d 完成\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1) // 计数+1
go worker(i, &wg)
}
wg.Wait() // 等待计数归零
fmt.Println("所有worker完成")
}
14.3 Mutex互斥锁
package main
import (
"fmt"
"sync"
)
type SafeCounter struct {
mu sync.Mutex
count int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
c.count++
c.mu.Unlock()
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
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
}
14.4 sync.Once
package main
import (
"fmt"
"sync"
)
var once sync.Once
func initialize() {
fmt.Println("初始化(只执行一次)")
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
once.Do(initialize) // 多次调用,只执行一次
}()
}
wg.Wait()
}
15. Channel通道
15.1 基本用法
package main
import "fmt"
func main() {
// 创建channel
ch := make(chan int)
// 发送数据(在goroutine中,否则会死锁)
go func() {
ch <- 42 // 发送
}()
// 接收数据
value := <-ch // 接收
fmt.Println(value) // 42
}
15.2 缓冲通道
package main
import "fmt"
func main() {
// 无缓冲:发送和接收必须同时准备好
ch1 := make(chan int)
// 有缓冲:缓冲区满之前发送不阻塞
ch2 := make(chan int, 3)
ch2 <- 1
ch2 <- 2
ch2 <- 3
// ch2 <- 4 // 这里会阻塞
fmt.Println(<-ch2) // 1
fmt.Println(<-ch2) // 2
_ = ch1
}
15.3 关闭通道与range
package main
import "fmt"
func producer(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch) // 关闭通道
}
func main() {
ch := make(chan int)
go producer(ch)
// range持续接收直到通道关闭
for value := range ch {
fmt.Println(value)
}
// 从已关闭的通道接收,得到零值
v, ok := <-ch
fmt.Println(v, ok) // 0 false
}
15.4 单向通道
package main
import "fmt"
// 只写通道
func producer(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
// 只读通道
func consumer(ch <-chan int) {
for value := range ch {
fmt.Println("收到:", value)
}
}
func main() {
ch := make(chan int)
go producer(ch)
consumer(ch)
}
15.5 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)
}
}
// 超时控制
select {
case msg := <-ch1:
fmt.Println(msg)
case <-time.After(3 * time.Second):
fmt.Println("超时")
}
}
15.6 Worker Pool模式
package main
import (
"fmt"
"sync"
)
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d 处理任务 %d\n", id, job)
results <- job * 2
}
}
func main() {
jobs := make(chan int, 10)
results := make(chan int, 10)
var wg sync.WaitGroup
// 启动3个worker
for w := 1; w <= 3; w++ {
wg.Add(1)
go worker(w, jobs, results, &wg)
}
// 发送任务
for j := 1; j <= 9; j++ {
jobs <- j
}
close(jobs)
// 等待所有worker完成
go func() {
wg.Wait()
close(results)
}()
// 收集结果
for result := range results {
fmt.Println("结果:", result)
}
}
16. Context上下文
16.1 基本用法
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Printf("%s: 收到取消信号\n", name)
return
default:
fmt.Printf("%s: 工作中...\n", name)
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
// WithCancel: 手动取消
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx, "worker-1")
time.Sleep(2 * time.Second)
cancel() // 取消
time.Sleep(1 * time.Second)
}
16.2 超时控制
package main
import (
"context"
"fmt"
"time"
)
func doWork(ctx context.Context) {
select {
case <-time.After(3 * time.Second):
fmt.Println("工作完成")
case <-ctx.Done():
fmt.Println("超时,取消工作")
}
}
func main() {
// WithTimeout: 超时自动取消
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
doWork(ctx)
}
16.3 传递数据
package main
import (
"context"
"fmt"
)
func process(ctx context.Context) {
userID := ctx.Value("userID")
fmt.Println("处理用户请求, userID:", userID)
}
func main() {
ctx := context.WithValue(context.Background(), "userID", 12345)
process(ctx)
}
17. 字符串处理
17.1 strings包
package main
import (
"fmt"
"strings"
)
func main() {
s := "Hello, World!"
// 判断
fmt.Println(strings.Contains(s, "World")) // true
fmt.Println(strings.HasPrefix(s, "Hello")) // true
fmt.Println(strings.HasSuffix(s, "!")) // true
// 查找
fmt.Println(strings.Index(s, "World")) // 7
fmt.Println(strings.Count(s, "l")) // 3
// 转换
fmt.Println(strings.ToUpper(s)) // HELLO, WORLD!
fmt.Println(strings.ToLower(s)) // hello, world!
// 替换
fmt.Println(strings.Replace(s, "World", "Go", 1)) // Hello, Go!
fmt.Println(strings.ReplaceAll(s, "l", "L")) // HeLLo, WorLd!
// 分割和连接
parts := strings.Split("a,b,c", ",")
fmt.Println(parts) // [a b c]
joined := strings.Join([]string{"a", "b", "c"}, "-")
fmt.Println(joined) // a-b-c
// 去除空格
fmt.Println(strings.TrimSpace(" hello ")) // hello
fmt.Println(strings.Trim("!!hello!!", "!")) // hello
// 重复
fmt.Println(strings.Repeat("Go", 3)) // GoGoGo
}
17.2 字符串与字节
package main
import "fmt"
func main() {
s := "Hello, 世界"
// 字符串长度(字节数)
fmt.Println(len(s)) // 13
// 按字符(rune)遍历
for i, ch := range s {
fmt.Printf("索引%d: %c\n", i, ch)
}
// 转为rune切片(正确处理中文)
runes := []rune(s)
fmt.Println(len(runes)) // 9(字符数)
fmt.Println(string(runes[7:])) // 世界
}
17.3 字符串拼接
package main
import (
"fmt"
"strings"
)
func main() {
// 方式1:+ 运算符(简单场景)
s1 := "Hello" + " " + "World"
// 方式2:fmt.Sprintf(需要格式化)
s2 := fmt.Sprintf("%s %d", "Number", 42)
// 方式3:strings.Builder(高效,大量拼接)
var builder strings.Builder
for i := 0; i < 100; i++ {
builder.WriteString("hello ")
}
s3 := builder.String()
fmt.Println(s1, s2, len(s3))
}
18. 文件操作
18.1 写文件
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
// 方式1:简单写入
err := os.WriteFile("simple.txt", []byte("Hello World"), 0644)
if err != nil {
fmt.Println("写入失败:", err)
}
// 方式2:Create + Write
f, err := os.Create("test.txt")
if err != nil {
panic(err)
}
defer f.Close()
f.WriteString("第一行\n")
// 方式3:带缓冲写入(推荐大量写入)
writer := bufio.NewWriter(f)
writer.WriteString("第二行\n")
writer.WriteString("第三行\n")
writer.Flush() // 别忘了
}
18.2 读文件
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// 方式1:一次性读取全部
data, err := os.ReadFile("test.txt")
if err != nil {
panic(err)
}
fmt.Println(string(data))
// 方式2:逐行读取
f, err := os.Open("test.txt")
if err != nil {
panic(err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
lineNum := 1
for scanner.Scan() {
fmt.Printf("第%d行: %s\n", lineNum, scanner.Text())
lineNum++
}
// 方式3:用Reader
f.Seek(0, io.SeekStart) // 回到开头
reader := bufio.NewReader(f)
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
fmt.Print(line)
}
}
18.3 文件是否存在
package main
import (
"fmt"
"os"
)
func fileExists(filename string) bool {
_, err := os.Stat(filename)
return !os.IsNotExist(err)
}
func main() {
if fileExists("test.txt") {
fmt.Println("文件存在")
} else {
fmt.Println("文件不存在")
}
}
19. JSON处理
19.1 结构体与JSON
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email,omitempty"` // 空值不输出
Password string `json:"-"` // 忽略
Tags []string `json:"tags"`
}
func main() {
// 结构体 → JSON(序列化)
user := User{
ID: 1,
Username: "alice",
Email: "",
Password: "secret",
Tags: []string{"admin", "user"},
}
jsonBytes, _ := json.Marshal(user)
fmt.Println(string(jsonBytes))
// {"id":1,"username":"alice","tags":["admin","user"]}
// 美化输出
prettyJSON, _ := json.MarshalIndent(user, "", " ")
fmt.Println(string(prettyJSON))
// JSON → 结构体(反序列化)
jsonStr := `{"id":2,"username":"bob","tags":["developer"]}`
var user2 User
json.Unmarshal([]byte(jsonStr), &user2)
fmt.Printf("%+v\n", user2)
}
19.2 动态JSON
package main
import (
"encoding/json"
"fmt"
)
func main() {
jsonStr := `{
"name": "Alice",
"age": 25,
"scores": [90, 85, 92]
}`
// 用map处理
var data map[string]interface{}
json.Unmarshal([]byte(jsonStr), &data)
fmt.Println(data["name"]) // Alice
fmt.Println(data["age"]) // 25
// scores是[]interface{}类型
scores := data["scores"].([]interface{})
fmt.Println(scores[0]) // 90
}
20. HTTP编程
20.1 HTTP客户端
package main
import (
"fmt"
"io"
"net/http"
"time"
)
func main() {
// 简单GET请求
resp, err := http.Get("https://httpbin.org/get")
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
// 自定义请求
client := &http.Client{
Timeout: 10 * time.Second,
}
req, _ := http.NewRequest("GET", "https://httpbin.org/get", nil)
req.Header.Set("Authorization", "Bearer token123")
resp2, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp2.Body.Close()
fmt.Println("状态码:", resp2.StatusCode)
}
20.2 HTTP服务端
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
)
type Response struct {
Message string `json:"message"`
Time string `json:"time"`
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
resp := Response{
Message: "Hello, World!",
Time: time.Now().Format(time.RFC3339),
}
json.NewEncoder(w).Encode(resp)
}
func userHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
fmt.Fprintf(w, "获取用户列表")
case "POST":
fmt.Fprintf(w, "创建用户")
default:
http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
}
}
func main() {
http.HandleFunc("/hello", helloHandler)
http.HandleFunc("/users", userHandler)
fmt.Println("服务器启动在 http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
21. 包管理
21.1 创建模块
Bash
# 初始化模块
mkdir myproject
cd myproject
go mod init myproject
# 项目结构
myproject/
├── go.mod
├── main.go
├── pkg/
│ └── utils/
│ └── helper.go
└── internal/
└── service/
└── user.go
21.2 包的定义和使用
// pkg/utils/helper.go
package utils
import "strings"
// 大写开头 = 导出(public)
func ToUpperCase(s string) string {
return strings.ToUpper(s)
}
// 小写开头 = 未导出(private)
func toLowerCase(s string) string {
return strings.ToLower(s)
}
// main.go
package main
import (
"fmt"
"myproject/pkg/utils"
)
func main() {
result := utils.ToUpperCase("hello")
fmt.Println(result) // HELLO
// utils.toLowerCase("WORLD") // 错误:未导出
}
21.3 依赖管理
Bash
# 添加依赖
go get github.com/gin-gonic/gin
# 整理依赖
go mod tidy
# 查看依赖
go list -m all
# 下载依赖到本地
go mod download
22. 测试
22.1 基本测试
// math.go
package math
func Add(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
}
// math_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2, 3) = %d; want 5", result)
}
}
// 表驱动测试(推荐)
func TestAddTable(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"正数", 2, 3, 5},
{"负数", -1, -2, -3},
{"零值", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("got %d, want %d", result, tt.expected)
}
})
}
}
// 基准测试
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(1, 2)
}
}
Bash
# 运行测试
go test
# 详细输出
go test -v
# 基准测试
go test -bench=.
# 覆盖率
go test -cover
23. 常用标准库
23.1 time包
package main
import (
"fmt"
"time"
)
func main() {
// 当前时间
now := time.Now()
fmt.Println(now)
// 格式化(Go特定:用2006-01-02 15:04:05)
fmt.Println(now.Format("2006-01-02 15:04:05"))
fmt.Println(now.Format("2006/01/02"))
// 解析
t, _ := time.Parse("2006-01-02", "2024-12-25")
fmt.Println(t)
// 时间运算
future := now.Add(24 * time.Hour)
fmt.Println(future)
duration := future.Sub(now)
fmt.Println(duration) // 24h0m0s
// 睡眠
time.Sleep(1 * time.Second)
// 定时器
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("2秒后执行")
// 定时执行
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
count := 0
for range ticker.C {
count++
fmt.Println("tick", count)
if count >= 3 {
break
}
}
}
23.2 sort包
package main
import (
"fmt"
"sort"
)
func main() {
// 排序整数
nums := []int{5, 2, 8, 1, 9}
sort.Ints(nums)
fmt.Println(nums) // [1 2 5 8 9]
// 排序字符串
strs := []string{"banana", "apple", "cherry"}
sort.Strings(strs)
fmt.Println(strs) // [apple banana cherry]
// 自定义排序
type Person struct {
Name string
Age int
}
people := []Person{
{"Bob", 25},
{"Alice", 30},
{"Charlie", 20},
}
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
fmt.Println(people)
// [{Charlie 20} {Bob 25} {Alice 30}]
}
23.3 regexp包
package main
import (
"fmt"
"regexp"
)
func main() {
// 编译正则表达式
re := regexp.MustCompile(`\d+`)
// 匹配
fmt.Println(re.MatchString("hello123")) // true
// 查找
fmt.Println(re.FindString("abc123def")) // 123
fmt.Println(re.FindAllString("a1b2c3", -1)) // [1 2 3]
// 替换
result := re.ReplaceAllString("a1b2c3", "X")
fmt.Println(result) // aXbXcX
}
24. 泛型(Go 1.18+)
24.1 泛型函数
package main
import "fmt"
// 泛型函数
func Max[T comparable](a, b T) T {
if a > b {
return a
}
return b
}
// 类型约束
type Number interface {
int | int64 | float64
}
func Sum[T Number](slice []T) T {
var sum T
for _, v := range slice {
sum += v
}
return sum
}
func main() {
fmt.Println(Max(3, 5)) // 5
fmt.Println(Max("a", "z")) // z
fmt.Println(Sum([]int{1, 2, 3, 4, 5})) // 15
fmt.Println(Sum([]float64{1.1, 2.2, 3.3})) // 6.6
}
24.2 泛型结构体
package main
import "fmt"
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
var zero T
if len(s.items) == 0 {
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
func main() {
// 整数栈
intStack := &Stack[int]{}
intStack.Push(1)
intStack.Push(2)
v, _ := intStack.Pop()
fmt.Println(v) // 2
// 字符串栈
strStack := &Stack[string]{}
strStack.Push("hello")
strStack.Push("world")
s, _ := strStack.Pop()
fmt.Println(s) // world
}
25. 示例
25.1 命令行TODO应用
// todo.go
package main
import (
"bufio"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
)
type Task struct {
ID int `json:"id"`
Title string `json:"title"`
Done bool `json:"done"`
}
type TodoList struct {
tasks []Task
file string
}
func NewTodoList(filename string) *TodoList {
tl := &TodoList{file: filename}
tl.load()
return tl
}
func (tl *TodoList) load() {
data, err := os.ReadFile(tl.file)
if err != nil {
return
}
json.Unmarshal(data, &tl.tasks)
}
func (tl *TodoList) save() {
data, _ := json.MarshalIndent(tl.tasks, "", " ")
os.WriteFile(tl.file, data, 0644)
}
func (tl *TodoList) Add(title string) {
id := len(tl.tasks) + 1
tl.tasks = append(tl.tasks, Task{ID: id, Title: title})
tl.save()
fmt.Printf("添加任务: %s\n", title)
}
func (tl *TodoList) List() {
for _, task := range tl.tasks {
status := " "
if task.Done {
status = "✓"
}
fmt.Printf("[%s] %d. %s\n", status, task.ID, task.Title)
}
}
func (tl *TodoList) Done(id int) {
for i := range tl.tasks {
if tl.tasks[i].ID == id {
tl.tasks[i].Done = true
tl.save()
fmt.Println("任务已完成")
return
}
}
fmt.Println("任务不存在")
}
func main() {
todo := NewTodoList("tasks.json")
reader := bufio.NewReader(os.Stdin)
for {
fmt.Println("\n=== TODO应用 ===")
fmt.Println("1. 添加任务")
fmt.Println("2. 查看任务")
fmt.Println("3. 完成任务")
fmt.Println("4. 退出")
fmt.Print("请选择: ")
input, _ := reader.ReadString('\n')
choice := strings.TrimSpace(input)
switch choice {
case "1":
fmt.Print("任务名称: ")
title, _ := reader.ReadString('\n')
todo.Add(strings.TrimSpace(title))
case "2":
todo.List()
case "3":
fmt.Print("任务ID: ")
idStr, _ := reader.ReadString('\n')
id, _ := strconv.Atoi(strings.TrimSpace(idStr))
todo.Done(id)
case "4":
return
}
}
}
25.2 简单Web API
// api.go
package main
import (
"encoding/json"
"log"
"net/http"
"strconv"
"sync"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
var (
users = []User{}
nextID = 1
usersMu sync.Mutex
)
func getUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
usersMu.Lock()
defer usersMu.Unlock()
json.NewEncoder(w).Encode(users)
}
func createUser(w http.ResponseWriter, r *http.Request) {
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
usersMu.Lock()
user.ID = nextID
nextID++
users = append(users, user)
usersMu.Unlock()
w.Header().Set("Content-Type", "application/json")
w.WriteStatus(http.StatusCreated)
json.NewEncoder(w).Encode(user)
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
getUsers(w, r)
case "POST":
createUser(w, r)
default:
http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
}
}
func main() {
http.HandleFunc("/users", usersHandler)
log.Println("服务器启动在 :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
# 测试API
curl http://localhost:8080/users
curl -X POST -H "Content-Type: application/json" -d '{"name":"Alice"}' http://localhost:8080/users
1331

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



