Go 基础解析

涵盖基础语法、并发、内存管理、接口设计、标准库与工具、性能优化以及实战场景

一、基础语法与类型

1. Go 与 C/Java 的主要区别

  • 内存管理:Go 有垃圾回收(GC),不需要手动管理内存。
  • 并发模型:Go 内置 goroutine 和 channel,而 C/Java 需要线程库或 Executor。
  • 面向对象:Go 不支持继承,用组合 + 接口实现多态。
  • 错误处理:Go 通过返回值 error 处理,而不是异常。
  • 编译与部署:Go 生成静态二进制,部署简单;Java 需要 JVM。

2. Go 的数据类型

基础类型

布尔类型 :bool
数值类型
整型 :int8, int16, int32, int64, uint8, uint16, uint32, uint64, int, uint, uintptr
浮点型 :float32, float64
复数型 :complex64, complex128
字符类型
byte :uint8 的别名
rune :int32 的别名,表示一个 Unicode 码点
字符串类型:string

复合类型

数组类型 :[n]T,长度固定的元素序列
切片类型 :[]T,动态长度的元素序列
结构体类型 :struct,字段集合,支持方法和接口
指针类型 :*T,指向类型 T 的指针
函数类型 :func,函数签名
接口类型 :interface,定义方法集的类型
映射类型 :map[K]V,键值对集合
通道类型:chan T,用于 goroutine 之间通信的管道

3. new 与 make 区别

关键点 new make
用途 分配内存 初始化 slice、map、chan
返回值 指针 对象本身
例子 p := new(int) // *int s := make([]int,5) // []int

4. defer 执行时机

  • 在函数返回前执行,遵循 后进先出(LIFO)
  • 常用于释放资源、解锁、打印日志
go 复制代码
func f() {
    defer fmt.Println("first")
    defer fmt.Println("second")
    fmt.Println("function body")
}
// 输出:
// function body
// second
// first

5. 指针使用限制

  • Go 不允许指针算术运算
  • 避免野指针和内存越界
  • 只能通过 new/make 或取地址 &var 获取指针

二、控制流与函数

1. 可变参数函数

go 复制代码
func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

2. 闭包(closure)

  • 函数可以引用外部变量并返回函数,保持状态
go 复制代码
func adder() func(int) int {
    sum := 0
    return func(x int) int {
        sum += x
        return sum
    }
}

3. panic、recover 与 error

  • panic:运行时错误,触发堆栈展开
  • recover:捕获 panic,防止程序崩溃
  • error:函数返回错误,不影响堆栈
go 复制代码
func safeDiv(a,b int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    fmt.Println(a/b) // b=0 会 panic
}

4. 方法与函数区别

  • 方法绑定在 receiver 上
  • 值接收者 vs 指针接收者影响是否能修改对象状态
go 复制代码
type Point struct { x, y int }
func (p Point) Move(dx, dy int) { p.x += dx; p.y += dy } // 值接收
func (p *Point) MovePtr(dx, dy int) { p.x += dx; p.y += dy } // 指针接收

三、并发与调度

1. Goroutine

  • 轻量级线程,使用 go func() {} 创建

2. runtime.Gosched()

  • 让出当前 goroutine 的 CPU 时间片
  • 不保证其他 goroutine 立即执行
  • 不保证严格交替输出
go 复制代码
go func() { for i:=0;i<10;i+=2{ fmt.Println(i); runtime.Gosched() } }()
go func() { for i:=1;i<10;i+=2{ fmt.Println(i); runtime.Gosched() } }()

3. Channel

  • 无缓冲 channel(同步)
  • 有缓冲 channel(异步,满时阻塞)
  • select 多路复用
go 复制代码
ch := make(chan int, 2)
ch <- 1
ch <- 2
select {
case v := <- ch:
    fmt.Println(v)
default:
    fmt.Println("no value")
}

4. sync.Mutex 与 sync.RWMutex

  • Mutex:互斥锁
  • RWMutex:读写锁,多个读不阻塞,写独占

5. WaitGroup

go 复制代码
var wg sync.WaitGroup
wg.Add(1)
go func() {
    defer wg.Done()
    // do something
}()
wg.Wait()

6. Goroutine 泄漏排查

  • channel 阻塞未关闭
  • 无限循环或 select 默认分支未退出
  • 使用 pprof/goroutine dump 检查堆栈

四、内存管理

1. 栈 vs 堆

  • 栈:局部变量,函数返回自动释放
  • 堆:逃逸分析判断需跨函数使用的变量分配到堆,GC 回收

2. slice 扩容原理

  • slice 底层结构:ptr, len, cap
  • append 超出 cap,会分配新数组并复制原数据
go 复制代码
s := make([]int,0,2)
s = append(s,1,2,3) // cap 扩容

3. map 底层实现

  • 哈希表 + 链表/红黑树冲突处理
  • 随机 hash 减少碰撞

4. 指针逃逸分析

  • 如果变量被返回或闭包引用,会分配到堆
  • go build -gcflags '-m' 可查看逃逸信息

五、接口与面向对象

1. 接口

  • 定义行为,不定义实现
  • 空接口 interface{} 可接收任意类型
  • 类型断言/类型开关提取具体类型
go 复制代码
var i interface{} = "hello"
s := i.(string)
switch v := i.(type) {
case string:
    fmt.Println("string:", v)
}

2. 多态实现

  • 通过接口实现
go 复制代码
type Shape interface { Area() float64 }
type Circle struct { r float64 }
func (c Circle) Area() float64 { return 3.14*c.r*c.r }
func PrintArea(s Shape) { fmt.Println(s.Area()) }

六、标准库与工具

1. Go module 与 GOPATH

  • Module 是推荐方式,支持版本管理
  • GOPATH 已逐渐弱化

2. context.Context

  • 用于控制 goroutine 生命周期、超时、取消和传递请求范围值

3. net/http 与 fasthttp

  • fasthttp 性能更高,但 API 不兼容 http

4. reflect 包

  • 用于运行时类型信息
  • 使用过度可能影响性能

5. log 包

  • 简单日志打印
  • 支持自定义输出、前缀和标志位

七、性能与优化

1. 性能分析工具

  • pprof CPU / memory / goroutine profiling
  • trace 分析并发阻塞、调度

2. 常见优化点

  • 减少对象频繁分配
  • 减少锁竞争
  • 使用池(sync.Pool)复用对象
  • 控制 goroutine 数量,避免泄漏

八、实战类场景题

1. 限流器(Token Bucket)

go 复制代码
type TokenBucket struct {
    tokens int
    capacity int
    mu sync.Mutex
}
func (tb *TokenBucket) Allow() bool {
    tb.mu.Lock()
    defer tb.mu.Unlock()
    if tb.tokens > 0 {
        tb.tokens--
        return true
    }
    return false
}

2. 生产者-消费者

go 复制代码
ch := make(chan int, 10)
go func() {
    for i:=0;i<10;i++ { ch <- i }
    close(ch)
}()
go func() {
    for v := range ch { fmt.Println(v) }
}()

3. 协程池示例

go 复制代码
tasks := make(chan func(), 100)
for i:=0;i<5;i++ {
    go func() {
        for task := range tasks { task() }
    }()
}
tasks <- func() { fmt.Println("task") }

4. 奇偶交替输出

go 复制代码
odd := make(chan struct{})
even := make(chan struct{})
go func() {
    for i:=1;i<=10;i+=2 {
        <- odd
        fmt.Println(i)
        even <- struct{}{}
    }
}()
go func() {
    for i:=2;i<=10;i+=2 {
        <- even
        fmt.Println(i)
        odd <- struct{}{}
    }
}()
odd <- struct{}{} // 启动奇数